在Newtonsoft.Json中添加多个契约解析器

本文关键字:契约 Newtonsoft Json 添加 | 更新日期: 2023-09-27 18:07:58

数据结构蓝图:

public class Movie
{
    public string Name { get; set; }
}
使用Newtonsoft

。Json,我对Json序列化有以下配置:

var settings = new JsonSerializerSettings() { 
    ContractResolver = new CamelCasePropertyNamesContractResolver(),
};

很明显,这将打印出:

{
    name: null
}

现在,我需要添加另一个NullToEmptyStringResolver到JsonSerializerSettings中的ContractResolver,我如何才能实现输出如下:

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

在Newtonsoft.Json中添加多个契约解析器

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

public class NullToEmptyStringResolver : CamelCasePropertyNamesContractResolver
{
    ...
}

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

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

我发现创建复合契约解析器是一个更好的主意。这是我在我的项目中使用的:

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

这是另一种选择。比起使用两个合约解析器,您可以使用NamingStrategy而不是CamelCasePropertyNamesContractResolver

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

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