【问题标题】:Newtonsoft JsonConvert.SerializeObject ignoring JsonProperty if name is uppercaseNewtonsoft JsonConvert.SerializeObject 如果名称为大写则忽略 JsonProperty
【发布时间】:2018-02-06 16:30:31
【问题描述】:

我希望能够使用CamelCasePropertyNameContractResolver,但为特定的属性名称覆盖它。为此,我使用JsonProperty 属性。这很好用,除非我选择的名称完全大写。任何想法有什么问题或如何解决它?

在下面的示例中,Bar 在我不使用 CamelCasePropertyNameContractResolver 时被序列化为 "BAR",但在我使用解析器时被序列化为 "bar"FooCamelCaseProperty 在这两种情况下都正确序列化。

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

【问题讨论】:

  • 而不是new CamelCasePropertyNamesContractResolver() 使用new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() }。原因见here

标签: c# .net serialization json.net


【解决方案1】:

原因你看到这个是CamelCasePropertyNamesContractResolver被有意设计为覆盖dictionary keysexplicitly set property names的大小写,从reference source可以看出:

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

如果您不希望这样,您有几个选项可以在不创建您自己的自定义合同解析器类型的情况下防止显式名称的大小写。

首先,您可以使用DefaultContractResolverNamingStrategy = new CamelCaseNamingStrategy() 进行序列化:

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

这会将CamelCaseNamingStrategy.OverrideSpecifiedNames 保留为默认值false

其次,如果您无法访问框架的合约解析器,您可以在特定属性上设置JsonPropertyAttribute.NamingStrategyType = typeof(DefaultNamingStrategy),如下所示:

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

第三,如果你想让你的整个对象忽略当前合约解析器的命名策略,你可以将[JsonObject(NamingStrategyType = typeof(TNamingStrategy))]应用于你的对象:

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

注意事项:

  • 虽然也可以修改CamelCasePropertyNamesContractResolver 实例的NamingStrategy,因为后者是shares contract information globally across all instances of each type,如果您的应用程序尝试使用多个@ 实例,这可能会导致意外的副作用987654346@。 DefaultContractResolver 不存在此类问题,因此在需要自定义大小写逻辑时使用它更安全。

  • 在使用DefaultContractResolver 或对其进行子类化时,您可能需要cache the contract resolver 以获得最佳性能,因为它不会在每种类型的所有实例之间全局共享合同信息。

  • 不知道为什么 Json.NET 的驼峰式解析器会覆盖指定名称,可能是历史原因。

  • 命名策略最初是在 Json.NET 9.0.1 中引入的,因此此答案仅适用于该版本及更高版本。

【讨论】:

    【解决方案2】:

    使用 ContractResolver 时不支持 JsonProperty 属性。

    解决此问题的方法是覆盖 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;
        }
    }
    

    并使用您的解析器:

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

    【讨论】:

    • 在某种程度上必须尊重 JsonProperty 属性。否则,为什么属性 Foo 会被序列化为 fOO 而不是 foo?
    • 是的,你是对的。但它在使用 ContractResolver 之前尊重 JsonProperty。所以 fOO 是 camelCame fOO,但 BAR 是 camelCase bar。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多