programing

사전을 직렬화 할 때 대 / 소문자 유지

nasanasas 2020. 10. 9. 11:17
반응형

사전을 직렬화 할 때 대 / 소문자 유지


다음과 같이 구성된 Web Api 프로젝트가 있습니다.

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

그러나 사전 키 대 / 소문자를 변경하지 않고 유지하고 싶습니다. Newtonsoft.Json직렬화 중에 대 / 소문자를 변경하지 않고 유지하기를 원한다는 것을 나타내는 클래스에 사용할 수 있는 속성이 있습니까?

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

이를 수행하는 속성은 없지만 해결 프로그램을 사용자 정의하여 수행 할 수 있습니다.

이미 CamelCasePropertyNamesContractResolver. 여기에서 새 리졸버 클래스를 파생하고 CreateDictionaryContract()메서드를 재정의 DictionaryKeyResolver하면 키 이름을 변경하지 않는 대체 함수를 제공 할 수 있습니다 .

다음은 필요한 코드입니다.

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

        contract.DictionaryKeyResolver = propertyName => propertyName;

        return contract;
    }
}

데모:

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; }
}

다음은 위의 출력입니다. 모든 클래스 속성 이름은 카멜 케이스이지만 사전 키는 원래 케이스를 유지했습니다.

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

Json.NET 9.0.1 introduced the NamingStrategy class hierarchy to handle this sort of issue. It extracts the logic for algorithmic remapping of property names from the contract resolver to a separate, lightweight class that allows for control of whether dictionary keys, explicitly specified property names, and extension data names (in 10.0.1) are remapped.

By using DefaultContractResolver and setting NamingStrategy to an instance of CamelCaseNamingStrategy you can generate JSON with camel-cased property names and unmodified dictionary keys by setting it in JsonSerializerSettings.ContractResolver:

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

Notes:

  • The current implementation of CamelCasePropertyNamesContractResolver also specifies that .Net members with explicitly specified property names (e.g. ones where JsonPropertyAttribute.PropertyName has been set) should have their names remapped:

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

    The above resolver preserves this behavior. If you don't want this, set OverrideSpecifiedNames = false.

  • Json.NET has several built-in naming strategies including:

    1. CamelCaseNamingStrategy. A camel case naming strategy that contains the name-remapping logic formerly embedded in CamelCasePropertyNamesContractResolver.
    2. SnakeCaseNamingStrategy. A snake case naming strategy.
    3. DefaultNamingStrategy. The default naming strategy. Property names and dictionary keys are unchanged.

    Or, you can create your own by inheriting from the abstract base class NamingStrategy.

  • While it is also possible to modify the NamingStrategy of an instance of CamelCasePropertyNamesContractResolver, since the latter shares contract information globally across all instances of each type, this can lead to unexpected side-effects if your application tries to use multiple instances of CamelCasePropertyNamesContractResolver. No such problem exists with DefaultContractResolver, so it is safer to use when any customization of casing logic is required.


That is a very nice answer. But why not just override the ResolveDictionaryKey?

class CamelCaseExceptDictionaryResolver : CamelCasePropertyNamesContractResolver
    {
        #region Overrides of DefaultContractResolver

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

        #endregion
    }

The selected answer is perfect but I guess by the time I'm typing this, the contract resolver must change to something like this because DictionaryKeyResolver doesn't exists anymore :)

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

참고URL : https://stackoverflow.com/questions/24143149/keep-casing-when-serializing-dictionaries

반응형