Giữ cách viết hoa khi sắp xếp tuần tự từ điển


92

Tôi có một dự án Web Api đang được định cấu hình như sau:

config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

Tuy nhiên, tôi muốn cách viết hoa của các phím từ điển không thay đổi. có thuộc tính nào trong Newtonsoft.Jsontôi có thể sử dụng cho một lớp để biểu thị rằng tôi muốn cách viết hoa không thay đổi trong quá trình tuần tự hóa không?

public class SomeViewModel
{
    public Dictionary<string, string> Data { get; set; }    
}

1
Bạn đã thử trình giải quyết mặc định chưa?
— Matthew

1
@Matthew Không, tôi chưa; bạn có thể giải thích bằng một ví dụ mã sẽ trông như thế nào không? Lưu ý, tôi vẫn muốn tuần tự hóa trường hợp Camel cho tất cả các yêu cầu api web của mình, tôi chỉ muốn tuần tự hóa tùy chỉnh cho một lớp (hoặc có thể cho bất kỳ khóa từ điển nào).
— zafeiris.m

Câu trả lời:


132

Không có thuộc tính để thực hiện việc này, nhưng bạn có thể thực hiện bằng cách tùy chỉnh trình phân giải.

Tôi thấy rằng bạn đã sử dụng a CamelCasePropertyNamesContractResolver. Nếu bạn dẫn xuất một lớp trình phân giải mới từ đó và ghi đè CreateDictionaryContract()phương thức, bạn có thể cung cấp một DictionaryKeyResolverhàm thay thế không thay đổi tên khóa.

Đây là mã bạn cần:

class CamelCaseExceptDictionaryKeysResolver : CamelCasePropertyNamesContractResolver
{
    protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
    {
        JsonDictionaryContract contract = base.CreateDictionaryContract(objectType);

        contract.DictionaryKeyResolver = propertyName => propertyName;

        return contract;
    }
}

Bản giới thiệu:

class Program
{
    static void Main(string[] args)
    {
        Foo foo = new Foo
        {
            AnIntegerProperty = 42,
            HTMLString = "<html></html>",
            Dictionary = new Dictionary<string, string>
            {
                { "WHIZbang", "1" },
                { "FOO", "2" },
                { "Bar", "3" },
            }
        };

        JsonSerializerSettings settings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCaseExceptDictionaryKeysResolver(),
            Formatting = Formatting.Indented
        };

        string json = JsonConvert.SerializeObject(foo, settings);
        Console.WriteLine(json);
    }
}

class Foo
{
    public int AnIntegerProperty { get; set; }
    public string HTMLString { get; set; }
    public Dictionary<string, string> Dictionary { get; set; }
}

Đây là kết quả từ phía trên. Lưu ý rằng tất cả các tên thuộc tính lớp đều có vỏ camel, nhưng các khóa từ điển vẫn giữ nguyên cách viết hoa ban đầu của chúng.

{
  "anIntegerProperty": 42,
  "htmlString": "<html></html>",
  "dictionary": {
    "WHIZbang": "1",
    "FOO": "2",
    "Bar": "3"
  }
}

2
FYI, PropertyNameResolver hiện đã lỗi thời. Có vẻ như nó contract.DictionaryKeyResolver = key => key;hoạt động tốt.
— John Gietzen

1
Điều này vẫn RẤT phù hợp với các loại ẩn danh, đặc biệt là khi chúng tôi muốn vỏ lạc đà cho hầu hết cấu trúc, nhưng không muốn các khóa bên trong từ điển được bọc da cam.
— Chris Schaller

Hoàn toàn đồng ý với Chris. Tôi đã bị buộc phải thực hiện các bước lặp lại trong JavaScript của mình chỉ vì tôi không thể giữ cho các từ điển khỏi bị CamelCased. Hóa ra rằng một dòng mã sẽ giải quyết vấn đề này (và làm cho JavaScript của tôi đơn giản hơn rất nhiều)!
— Stephen Chung

@BrianRogers Hoạt động tuyệt vời! Tuy nhiên, bạn có biết liệu tôi có thể điều kiện bằng cách sử dụng DictionaryKeyResolverchỉ của tôi nếu thuộc tính Từ điển của tôi có một số Thuộc tính tùy chỉnh không?
— Mugen

@Mugen Không ra khỏi đỉnh đầu của tôi. Tôi khuyên bạn nên hỏi điều đó như một Câu hỏi mới. Bạn có thể liên kết lại câu hỏi này nếu bạn cần cung cấp ngữ cảnh.
— Brian Rogers

67

Json.NET 9.0.1 đã giới thiệu NamingStrategyhệ thống phân cấp lớp để xử lý loại vấn đề này. Nó trích xuất logic để ánh xạ lại theo thuật toán của các tên thuộc tính từ trình phân giải hợp đồng thành một lớp riêng biệt, nhẹ cho phép kiểm soát xem các khóa từ điển , tên thuộc tính được chỉ định rõ ràng và tên dữ liệu mở rộng (trong 10.0.1 ) có được ánh xạ lại hay không.

Bằng cách sử dụng DefaultContractResolvervà đặt NamingStrategythành một phiên bản của CamelCaseNamingStrategybạn, bạn có thể tạo JSON với tên thuộc tính dựa trên lạc đà và các khóa từ điển chưa sửa đổi bằng cách đặt nó trong JsonSerializerSettings.ContractResolver:

var resolver = new DefaultContractResolver
{
    NamingStrategy = new CamelCaseNamingStrategy
    {
        ProcessDictionaryKeys = false,
        OverrideSpecifiedNames = true
    }
};
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = resolver;

Ghi chú:

  • Việc triển khai hiện tại của CamelCasePropertyNamesContractResolvercũng chỉ định rằng các thành viên .Net có tên thuộc tính được chỉ định rõ ràng (ví dụ: những thuộc tính JsonPropertyAttribute.PropertyNameđã được đặt) phải có tên của chúng được ánh xạ lại:

    public CamelCasePropertyNamesContractResolver()
    {
        NamingStrategy = new CamelCaseNamingStrategy
        {
            ProcessDictionaryKeys = true,
            OverrideSpecifiedNames = true
        };
    }
    

    Ở trên resolverbảo tồn hành vi này. Nếu bạn không muốn điều này, hãy thiết lập OverrideSpecifiedNames = false.

  • Json.NET có một số chiến lược đặt tên tích hợp bao gồm:

    1. CamelCaseNamingStrategy. Chiến lược đặt tên theo trường hợp lạc đà có chứa logic ánh xạ lại tên trước đây được nhúng vào CamelCasePropertyNamesContractResolver.
    2. SnakeCaseNamingStrategy. Một chiến lược đặt tên trường hợp rắn .
    3. DefaultNamingStrategy. Chiến lược đặt tên mặc định. Tên thuộc tính và khóa từ điển không thay đổi.

    Hoặc, bạn có thể tạo của riêng mình bằng cách kế thừa từ lớp cơ sở trừu tượng NamingStrategy.

  • Mặc dù cũng có thể sửa đổi NamingStrategyphiên bản của CamelCasePropertyNamesContractResolver, vì phiên bản sau chia sẻ thông tin hợp đồng trên toàn cầu trên tất cả các phiên bản của mỗi loại , điều này có thể dẫn đến các tác dụng phụ không mong muốn nếu ứng dụng của bạn cố gắng sử dụng nhiều phiên bản của CamelCasePropertyNamesContractResolver. Không có vấn đề như vậy tồn tại DefaultContractResolver, vì vậy sẽ an toàn hơn khi sử dụng khi bất kỳ tùy chỉnh nào của logic vỏ được yêu cầu.


Giải pháp này không hoạt động cho một tài sản như public Dictionary<string, Dictionary<string, string>> Values { get; set; }. Nó vẫn hoạt động camelCase cho các khóa từ điển bên trong.
— hikalkan

@hikalkan - trong khi tôi không thể tái tạo sự cố chính xác của bạn, tôi có thể tìm thấy sự cố khi sử dụng nhiều phiên bản của CamelCasePropertyNamesContractResolver. Về cơ bản NamingStrategyđối với thứ nhất sẽ ảnh hưởng đến các hợp đồng được tạo ra bởi thứ hai. Đó có thể là những gì bạn đang thấy. Hãy thử đề xuất mới thay thế và cho tôi biết nếu khắc phục được sự cố của bạn.
— dbc

1
Có một linh hoạt NamingStrategyđể nó có khả năng phân tích cú pháp cả vỏ lạc đà và vỏ pascal không?
— Shimmy Weitzhandler

@dbc Trong mẫu mã ban đầu, điều gì được configcho là?
— Ryan Lundy

@RyanLundy - Tôi sao chép nó từ câu hỏi ban đầu, cho thấy dòng mã sau đây: config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();. Có vẻ như đây là API Web MVC 4 HttpConfiguration, hãy xem Cách đặt JsonSerializerSettings tùy chỉnh cho Json.NET trong API Web MVC 4? .
— dbc

12

Đó là một câu trả lời rất hay. Nhưng tại sao không chỉ cần ghi đè ResolveDictionaryKey?

class CamelCaseExceptDictionaryResolver : CamelCasePropertyNamesContractResolver
    {
        #region Overrides of DefaultContractResolver

        protected override string ResolveDictionaryKey(string dictionaryKey)
        {
            return dictionaryKey;
        }

        #endregion
    }

Nhiều ngắn gọn. Thx để chia sẻ.
— Abu Abdullah

1

Câu trả lời đã chọn là hoàn hảo nhưng tôi đoán vào thời điểm tôi nhập câu này, trình giải quyết hợp đồng phải thay đổi thành một cái gì đó như thế này vì DictionaryKeyResolver không tồn tại nữa :)

public class CamelCaseExceptDictionaryKeysResolver : CamelCasePropertyNamesContractResolver
    {
        protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
        {
            JsonDictionaryContract contract = base.CreateDictionaryContract(objectType);
            contract.PropertyNameResolver = propertyName => propertyName;
            return contract;
        }
    }

4
Thực ra điều ngược lại mới đúng. Bạn phải đang sử dụng phiên bản cũ của Json.Net. DictionaryKeyResolverđã được thêm vào phiên bản 7.0.1 và PropertyNameResolverđược đánh dấu là lỗi thời.
— Brian Rogers
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.