如何实现多态Newtonsoft.Json.JsonConverter?

问题描述 投票:0回答:1

给出以下多态类型层次结构:

[JsonConverter(typeof(PolymorphicConverter<Base>))]
public record Base
{
    private Base() {}

    public record A(string Value) : Base;
    public record B(int Foobar) : Base;
    public record C(Base Recursive) : Base;
}

使用所需的序列化 json,例如答:

{
  "type": "A",
  "content": {
    "value": "jalla"
  }
}

是否可以为 PolymorphicConverter 创建一个不依赖于类型

Base
、高性能且线程安全的实现?

这是一个有效且高性能的实现,但不是线程安全的:

using System;
using Newtonsoft.Json;

Base a = new Base.A("foobar");

var json = JsonConvert.SerializeObject(a);

Console.WriteLine(json);

[JsonConverter(typeof(PolymorphicConverter<Base>))]
public record Base
{
    private Base() {}

    public record A(string Value) : Base;
    public record B(int Foobar) : Base;
    public record C(Base Recursive) : Base;
}


public class PolymorphicConverter<TBase> : JsonConverter where TBase : class
{
    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
    {
        var type = value?.GetType() ?? throw new NotImplementedException();
        var converter = serializer.ContractResolver.ResolveContract(type).Converter;
        serializer.ContractResolver.ResolveContract(type).Converter = null;
        writer.WriteStartObject();
        writer.WritePropertyName("type");
        writer.WriteValue(type.Name);
        writer.WritePropertyName("content");
        serializer.Serialize(writer, value);
        serializer.ContractResolver.ResolveContract(type).Converter = converter;
        writer.WriteEndObject();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(TBase).IsAssignableFrom(objectType);
    }
}
c# json json.net polymorphism
1个回答
0
投票

您可以利用应用于属性的转换器取代应用于类型或设置中的转换器的记录功能来序列化包含您的

TBase
值的DTO,而不使用其应用的转换器:

public class PolymorphicConverter<TBase> : JsonConverter<TBase> where TBase : class
{
    class DTO
    {
        [JsonProperty(Order = 1)]
        public string? type { get; set; }
        [JsonProperty(Order = 2, ReferenceLoopHandling = ReferenceLoopHandling.Serialize), 
         JsonConverter(typeof(NoConverter))]
        public TBase? content { get; set; }
    }
    
    public override void WriteJson(JsonWriter writer, TBase? value, JsonSerializer serializer)
    {
        var type = value?.GetType() ?? throw new NotImplementedException();
        serializer.Serialize(writer, new DTO { type = type.Name, content = value });
    }

    public override TBase? ReadJson(JsonReader reader, Type objectType, TBase? existingValue, bool hasExistingValue, JsonSerializer serializer) =>
        throw new NotImplementedException();
}

public class NoConverter : JsonConverter
{
    // NoConverter taken from this answer https://stackoverflow.com/a/39739105/3744182
    // By https://stackoverflow.com/users/3744182/dbc
    // To https://stackoverflow.com/questions/39738714/selectively-use-default-json-converter
    public override bool CanConvert(Type objectType) => throw new NotImplementedException(); /* This converter should only be applied via attributes */
    public override bool CanRead => false;
    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) => throw new NotImplementedException();
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) => throw new NotImplementedException();
}

这消除了

serializer.ContractResolver.ResolveContract(type).Converter
null
的线程不安全设置。

备注:

演示小提琴在这里

© www.soinside.com 2019 - 2024. All rights reserved.