C# JsonConvert使用默认转换器而不是自定义转换器

8

我有一个类如下,它有一个自定义的JsonConverter

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

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

是否有一种方法可以使用默认序列化程序而不是TheShapeSerializer来序列化TheShape的实例?

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


当直接调用serialize/deserialize var json = JsonConvert.SerializeObject(obj, new MyCustomConverter())时,您可以尝试使用另一个转换器。这样您就可以更好地控制整个过程。 - Nkosi
这是一个有趣的问题。我正在检查我的建议评论是否可行,没有看到任何类似的问题。 - Nkosi
1
使用属性应用的 JsonConverter 优先于通过设置应用的 JsonConverter。根据文档使用的 JsonConverter 的优先级是成员属性、类属性,最后是传递给 JsonSerializer 的任何转换器。 您需要使用 自定义契约解析器 - dbc
1
@dbc 这是在文档中找到的一个好东西。我以前不知道。 - Nkosi
1个回答

11

JsonConverters的选择顺序如下documented:

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

因此,您无法使用JsonSerializerSettings.Converters禁用通过JsonConverterAttribute应用的JsonConverter。 相反,您有以下选项。

首先,如果您直接引用了某个类型的TheShape,则可以从this answer中获取NoConverterSelectively 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.ResolveContractConverter并返回null以供TheShape使用。首先定义以下合同解析器:
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);
    }
}

然后,为了性能考虑,按照这里所述,在某个地方定义一个静态成员:

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

然后按照以下方式进行序列化:

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

演示fiddle展示了两个选项在这里

同样的,是否有一种方法可以有多个转换器,在序列化时根据给定条件进行选择?

显然,您可以根据某些运行时条件向JsonSerializerSettings.Converters添加不同的转换器。但是,如果您想使用运行时转换器取代静态应用的转换器,则需要适当设置类型,例如通过使用此答案中的OverridableJsonConverterDecorator来回答为什么Json.net不使用自定义IsoDateTimeConverter?


1
太好了。我使用了ContactResolver的方法,因为第一个解决方案不符合你所说的原因,而ContactResolver的方法完美地解决了问题。 - isaac.hazan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接