Newtonsoft JsonConvert.SerializeObject ignoring JsonProperty if name is uppercase

时光毁灭记忆、已成空白 提交于 2019-12-22 01:29:59

问题


I want to be able to use the CamelCasePropertyNameContractResolver but override it for specific property names. For this, I use the JsonProperty attribute. This works fine except when the name that I choose is fully uppercase. Any ideas what's wrong or how to get around it?

In the example below, Bar is serialized to "BAR" when I don't use the CamelCasePropertyNameContractResolver, but is serialized to "bar" when I do use the resolver. Foo and CamelCaseProperty are serialized correctly in both scenarios.

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace ConsoleTester
{
    class Program
    {
        static void Main(string[] args)
        {
            var foo = new FooBar {CamelCaseProperty = "test", Foo = "test", Bar = "test" };
            var output = JsonConvert.SerializeObject(foo);
            // output "CamelCaseProperty", "fOO", "BAR"

            var output2 = JsonConvert.SerializeObject(foo, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
            // output "camelCaseProperty", "fOO", "bar"
        }
    }

    public class FooBar
    {
        public string CamelCaseProperty { get; set; }
        [JsonProperty("fOO")]
        public string Foo { get; set; }
        [JsonProperty("BAR")]
        public string Bar { get; set; }
    }
}

回答1:


The reason you are seeing this is that CamelCasePropertyNamesContractResolver is intentionally designed to override the casing of dictionary keys and explicitly set property names, as can be see from the reference source:

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

If you don't want that, you have several options to prevent casing of explicit names without creating your own custom contract resolver type.

Firstly, you could serialize using a DefaultContractResolver with NamingStrategy = new CamelCaseNamingStrategy():

var settings = new JsonSerializerSettings 
{ 
    ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() }
};
var output2 = JsonConvert.SerializeObject(foo, settings);

This leaves CamelCaseNamingStrategy.OverrideSpecifiedNames at its default value of false.

Secondly, if you don't have access to the contract resolver of your framework, you could set JsonPropertyAttribute.NamingStrategyType = typeof(DefaultNamingStrategy) on specific properties, like so:

public class FooBar
{
    public string CamelCaseProperty { get; set; }

    [JsonProperty("fOO")]
    public string Foo { get; set; }

    [JsonProperty("BAR", NamingStrategyType = typeof(DefaultNamingStrategy))]
    public string Bar { get; set; }
}

Thirdly, if you want your entire object to ignore the naming strategy of the current contract resolver, you can apply [JsonObject(NamingStrategyType = typeof(TNamingStrategy))] to your object:

[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public class FooBar
{
    public string CamelCaseProperty { get; set; }

    [JsonProperty("fOO")]
    public string Foo { get; set; }

    [JsonProperty("BAR")]
    public string Bar { get; set; }
}

Notes:

  • 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.

  • When using or subclassing DefaultContractResolver, you may want to cache the contract resolver for best performance, since it does not share contract information globally across all instances of each type.

  • I don't know why Json.NET's camel case resolver is designed to override specified names, it may be for historical reasons.

  • Naming strategies were first introduced in Json.NET 9.0.1 so this answer works only for that version and later.




回答2:


JsonProperty Attribute is not honoured when you use a ContractResolver.

What you can do to solve this issue is override the ContractResolver:

public class MyResolver : CamelCasePropertyNamesContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        if(member.GetCustomAttribute<JsonPropertyAttribute>() is JsonPropertyAttribute jsonProperty)
        {
            property.PropertyName = jsonProperty.PropertyName;
        }

        return property;
    }
}

And use your Resolver:

var output2 = JsonConvert.SerializeObject(foo, new JsonSerializerSettings { ContractResolver = new MyResolver() });


来源:https://stackoverflow.com/questions/48647650/newtonsoft-jsonconvert-serializeobject-ignoring-jsonproperty-if-name-is-uppercas

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!