【问题标题】:C# JsonConvert using the default converter instead of the custom converterC# JsonConvert 使用默认转换器而不是自定义转换器
【发布时间】:2019-11-26 22:12:23
【问题描述】:

我有一个具有自定义JsonConverter 的类如下:

[JsonConverter(typeof(TheShapeSerializer))]
public class TheShape : IShape {
//....
}

我无法更改课程。自定义序列化程序的工作方式不适合我的需要。

有没有办法使用默认的序列化器而不是 TheShapeSerializer 来序列化 TheShape 的实例?

按照同样的思路,有没有一种方法可以在序列化时根据给定条件选择多个转换器?

【问题讨论】:

  • 在直接调用serialize/deserializevar json = JsonConvert.SerializeObject(obj, new MyCustomConverter())时可以尝试使用其他转换器,这样您可以更好地控制流程。
  • 这是一个有趣的问题。我正在检查我建议的评论是否可行,并且没有看到任何有类似问题的人。
  • JsonConverter 使用属性应用取代 JsonConverter 通过设置应用。来自docs使用JsonConverter的优先级是成员属性,然后是类属性,最后是传递给JsonSerializer的任何转换器。。您需要使用custom contract resolver
  • @dbc 在文档中是一个很好的发现。我没有意识到这一点。

标签: c# json.net


【解决方案1】:

选择JsonConverters的顺序为documented如下:

使用JsonConverter的优先级是成员属性,然后是类属性,最后是传递给JsonSerializer的任何转换器。

因此,您不能使用JsonSerializerSettings.Converters 禁用通过JsonConverterAttribute 应用的JsonConverter。相反,您有以下选择。

首先,如果您的TheShape 被您控制的某种类型直接引用,您可以将NoConverterthis answer 抓取到Selectively use default JSON converter 并将其应用于引用成员使用JsonConverterAttributeJsonPropertyAttribute.ItemConverterType,例如如下:

public class ShapeContainer
{
    [JsonConverter(typeof(NoConverter))]
    public TheShape Shape { get; set; }

    [JsonProperty(ItemConverterType = typeof(NoConverter))]
    public List<TheShape> Shapes { get; set; }
}

现在 NoConverter 将取代 TheShapeSerializer 应用它的属性,并导致 Json.NET 回退到默认序列化。

其次,如果您无法将成员属性添加到使用 TheShape 的类型,您可以创建一个覆盖 DefaultContractResolver.ResolveContractConvertercustom contract resolver 并为 TheShape 返回 null。首先定义如下合约解析器:

public class ConverterDisablingContractResolver : DefaultContractResolver
{
    readonly HashSet<Type> types;

    public ConverterDisablingContractResolver(IEnumerable<Type> types)
    {
        if (types == null)
            throw new ArgumentNullException();
        this.types = new HashSet<Type>(types);
    }

    bool ContainsType(Type type)
    {
        return types.Contains(type);
    }

    protected override JsonConverter ResolveContractConverter(Type objectType)
    {
        // This could be enhanced to deal with inheritance.  I.e. if TBase is in types and has a converter then
        // its converter should not be used for TDerived -- but if TDerived has its own converter then it should still be
        // used, so simply returning null for TDerived would be wrong.
        if (types.Contains(objectType))
            return null;
        return base.ResolveContractConverter(objectType);
    }
}

然后,在某处定义一个静态成员如下,出于性能原因描述here

static IContractResolver shapeResolver = new ConverterDisablingContractResolver(new[] { typeof(TheShape) });

并序列化如下:

var settings = new JsonSerializerSettings
{
    ContractResolver = shapeResolver,
};
var json = JsonConvert.SerializeObject(root, settings);

演示小提琴显示两个选项here

同理,有没有一种方法可以在序列化时根据给定条件选择多个转换器?

显然,您可以根据某些运行时条件向JsonSerializerSettings.Converters 添加不同的转换器。但是如果你想用运行时转换器取代静态应用的转换器,你需要适当地设置你的类型,例如通过使用OverridableJsonConverterDecoratorthis answerWhy Json.net does not use customized IsoDateTimeConverter?

【讨论】:

  • 这很棒。我使用了 ContactResolver 方法,因为第一个解决方案与您所说的原因无关,并且效果很好
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多