json.net 将派生序列化为通用基础

json.net serialize derived as generic base

(这个问题源于尝试 serialize/deserialize LikeType 类 到 JSON - https://github.com/kleinwareio/LikeType

我有:

    public abstract class LikeType<T>
    {
      public T Value;
      // ....

      // how to tell json.net to serialize/deserialize classes deriving
      // from this like it would T ???
    }

    public class Name : LikeType<string>  { 
      public Name(string s) : base(s) { } 
      // does not add any properties
    }

    void test()
    {
      var name = new Name("john");
      var jobj = new JObject();
      try
      {
        jobj.Add("key", new JObject(name));
      }
      catch (Exception e)
      {
         !Exeption !
         e = {System.ArgumentException: Could not determine JSON object type for type Name. at Newtonsoft.Json.Linq.JValue.GetValueType(Nullable`1 current, Object value) at  Newtonsoft.Json.Linq.JContainer.CreateFromContent(Object content)
      }
    }

我如何指定从 LikeType<T> 派生的所有 类 将序列化/反序列化为 JSON 和 Json.Net 以与 T 相同的方式?

(在这种情况下,Json.Net 应该 serialize/deserialize 以与字符串相同的方式命名)

我相信您想要 "forward" LikeType<T> 序列化,将其视为不可见的包装类型。这个假设对我的解决方案至关重要。

我建议使用 JsonConverter 实现来做到这一点。这里有个很像的post:

我已根据您的情况修改了示例。这是适应的方法:

class LikeTypeConverter : JsonConverter
{
    static Type GetValueType(Type objectType)
    {
        return objectType
            .BaseTypesAndSelf()
            .Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(LikeType<>))
            .Select(t => t.GetGenericArguments()[0])
            .FirstOrDefault();
    }

    public override bool CanConvert(Type objectType)
    {
        return GetValueType(objectType) != null;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // You need to decide whether a null JSON token results in a null LikeType<T> or 
        // an allocated LikeType<T> with a null Value.
        if (reader.SkipComments().TokenType == JsonToken.Null)
            return null;
        var valueType = GetValueType(objectType);
        var value = serializer.Deserialize(reader, valueType);

        // Here we assume that every subclass of LikeType<T> has a constructor with a single argument, of type T.
        return Activator.CreateInstance(objectType, value);
    }

    const string ValuePropertyName = "Value";// nameof(LikeType<object>.Value); // in C#6+

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
        var valueProperty = contract.Properties.Single(p => p.UnderlyingName == ValuePropertyName);
        serializer.Serialize(writer, valueProperty.ValueProvider.GetValue(value));
    }
}

public static partial class JsonExtensions
{
    public static JsonReader SkipComments(this JsonReader reader)
    {
        while (reader.TokenType == JsonToken.Comment && reader.Read())
        {
        }

        return reader;
    }
}

public static class TypeExtensions
{
    public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
    {
        while (type != null)
        {
            yield return type;
            type = type.BaseType;
        }
    }
}

如果您想将其包含在您的库中,您可以将其用作 LikeType<T> 声明的属性:

[JsonConverter(typeof(LikeTypeConverter))]
public abstract class LikeType<T> { ... }

或者你可以在必要的时候使用转换器,修改JsonSerializerSettings.Converters集合:

    var settings = new JsonSerializerSettings
    {
        Converters = { new LikeTypeConverter() },
        ContractResolver = new CamelCasePropertyNamesContractResolver()
    };
    var result = JsonConvert.SerializeObject(myObject, Formatting.Indented, settings);

我还创建了一个 working dotnetfiddle sample 用于演示(还改编了链接 post 中的那个)。

控制大多数序列化器序列化的内容的一种方法是使用 Serializable attribute and implement the ISerializable interface