在Newtonsoft.Json中添加多个合同解析程序

时间:2016-09-21 09:29:44

标签: c# json json.net

数据结构蓝图:

public class Movie
{
    public string Name { get; set; }
}

使用Newtonsoft.Json,我有以下Json序列化配置。

var settings = new JsonSerializerSettings() { 
    ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
显然,这将打印出来:

{
    name: null
}

现在,我需要在JsonSerializerSettings中向ContractResolver添加另一个NullToEmptyStringResolver,如何实现以下输出:

{
    name: ""
}
  • 请注意我的NullToEmptyStringResolver已经写好了。但我需要将NullToEmptyStringResolver和CamelCasePropertyNamesContractResolver添加到合同解析器。

3 个答案:

答案 0 :(得分:11)

Json.Net一次不允许多个合同解析器,因此您需要一种方法来组合他们的行为。我假设NullToEmptyStringResolver是一个自定义解析器,它继承自Json.Net的DefaultContractResolver类。如果是这样,实现所需结果的一种简单方法是改为NullToEmptyStringResolver继承CamelCasePropertyNamesContractResolver

public class NullToEmptyStringResolver : CamelCasePropertyNamesContractResolver
{
    ...
}

如果您不喜欢这种方法,另一个想法是将骆驼套管行为添加到NullToEmptyStringResolver。如果你看一下source codeCamelCasePropertyNamesContractResolver的实现方式,你会发现这就像在构造函数中设置NamingStrategy一样简单(假设你使用的是Json)。 Net 9.0.1或更高版本)。您可以将相同的代码添加到NullToEmptyStringResolver的构造函数中。

public class NullToEmptyStringResolver : DefaultContractResolver
{
    public NullToEmptyStringResolver() : base()
    {
        NamingStrategy = new CamelCaseNamingStrategy
        {
            ProcessDictionaryKeys = true,
            OverrideSpecifiedNames = true
        };
    }

    ...
}

答案 1 :(得分:3)

我发现创建复合合约解析器更好。这就是我在项目中使用的内容:

public class CompositeContractResolver : IContractResolver, IEnumerable<IContractResolver>
{
    private readonly IList<IContractResolver> _contractResolvers = new List<IContractResolver>();

    public JsonContract ResolveContract(Type type)
    {
        return
            _contractResolvers
                .Select(x => x.ResolveContract(type))
                .FirstOrDefault(Conditional.IsNotNull);
    }

    public void Add([NotNull] IContractResolver contractResolver)
    {
        if (contractResolver == null) throw new ArgumentNullException(nameof(contractResolver));
        _contractResolvers.Add(contractResolver);
    }

    public IEnumerator<IContractResolver> GetEnumerator()
    {
        return _contractResolvers.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

然后我就这样使用它:

Settings = new JsonSerializerSettings
{
    ContractResolver = new CompositeContractResolver
    {
        new InterfaceContractResolver<ISomething>(),
        new DefaultContractResolver()
    }
}

我的自定义合约解析器会返回null合约,因此如果没有匹配,复合合约可以通过默认合约。

public class InterfaceContractResolver<T> : DefaultContractResolver
{
    public InterfaceContractResolver()
    {
        if (!typeof(T).IsInterface) throw new InvalidOperationException("T must be an interface.");
    }

    public override JsonContract ResolveContract(Type type)
    {
        return 
            typeof(T).IsAssignableFrom(type) 
                ? base.ResolveContract(typeof(T)) 
                : default;
    }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        return
            typeof(T).IsAssignableFrom(type)
                ? base.CreateProperties(typeof(T), memberSerialization)
                : default;
    }
}

答案 2 :(得分:2)

这是另一种选择。您可以使用NamingStrategy代替CamelCasePropertyNamesContractResolver,而不是使用两个ContractResolvers。

var settings = new JsonSerializerSettings()
{
    ContractResolver = new NullToEmptyStringResolver(){NamingStrategy = new CamelCaseNamingStrategy()}
};

这是类似@ BrianRogers的第二种方法。我刚刚没有将设置硬编码为NullToEmptyStringResolver类。