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

9 c# json json.net

数据结构蓝图:

public class Movie
{
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

var settings = new JsonSerializerSettings() { 
    ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
Run Code Online (Sandbox Code Playgroud)

显然,这将打印出来:

{
    name: null
}
Run Code Online (Sandbox Code Playgroud)

现在,我需要另一个NullToEmptyStringResolver添加到JsonSerializerSettings的ContractResolver,我怎么能做到这一点,其下面的输出:

{
    name: ""
}
Run Code Online (Sandbox Code Playgroud)
  • 请注意我的NullToEmptyStringResolver已经写好了.但是我需要将NullToEmptyStringResolver和CamelCasePropertyNamesContractResolver添加到Contract Resolver.

Bri*_*ers 12

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

public class NullToEmptyStringResolver : CamelCasePropertyNamesContractResolver
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

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

    ...
}
Run Code Online (Sandbox Code Playgroud)


t3c*_*b0t 5

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,我像这样使用它:

Settings = new JsonSerializerSettings
{
    ContractResolver = new CompositeContractResolver
    {
        new InterfaceContractResolver<ISomething>(),
        new DefaultContractResolver()
    }
}
Run Code Online (Sandbox Code Playgroud)

我的自定义合同解析器在其中返回了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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 好吧,我认为问题的重点是以某种方式“组合”同一类型的两个合约解析器的行为。一个解析器提供小写属性名称,另一个解析器将 null 值转换为空字符串。在您的设计中,您将返回第一个可以解析给定类型的合同的解析器。因此,例如,如果两个合约解析器都可以处理“字符串”,那么您只能获得其中一个的行为 - 无论是列表中的第一个。在实践中,确实没有一种很好的方法来组合行为,因为你只能返回一份合同。 (3认同)
  • 事实上,现在我想得更多了,你也许可以使用装饰器模式来做到这一点。无论如何,值得深思。 (2认同)
  • @Konrad这是我用来避免编写`的功能之一[https://github.com/he-dev/Reusable/blob/master/Reusable.Core/src/Extensions/Conditional.cs#L14) =&gt; x!= null`等;-) (2认同)

Ton*_*ony 5

这是一个替代方案。除了使用两个 ContractResolver,您还可以使用NamingStrategy代替CamelCasePropertyNamesContractResolver.

var settings = new JsonSerializerSettings()
{
    ContractResolver = new NullToEmptyStringResolver(){NamingStrategy = new CamelCaseNamingStrategy()}
};
Run Code Online (Sandbox Code Playgroud)

这是类似@BrianRogers 的第二种方法。我只是没有将设置硬编码到NullToEmptyStringResolver课堂上。