简体   繁体   English

json.net序列化为通用库

[英]json.net serialize derived as generic base

(this issue stems from trying to serialize/deserialize LikeType classes to JSON - https://github.com/kleinwareio/LikeType ) (此问题源于试图将LikeType类序列化/反序列化为JSON- https://github.com/kleinwareio/LikeType

I have: 我有:

    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)
      }
    }

How can I specify that all classes deriving from LikeType<T> will be serialized/ deserialized to JSON with Json.Net in the same way T would? 如何指定所有使用LikeType<T>派生的类都将使用Json.Net以T相同的方式序列化/反序列化为JSON?

(in this case, Json.Net should serialize/deserialize Name in the same way it would a string) (在这种情况下,Json.Net应该以与字符串相同的方式对Name进行序列化/反序列化)

I believe you want to "forward" LikeType<T> serialization, treating this like an invisible wrapper type. 我相信您想“转发” LikeType<T>序列化,将其LikeType<T>不可见的包装器类型。 This assumption is crucial to my solution. 这个假设对我的解决方案至关重要。

I'd suggest using JsonConverter implementation to do that. 我建议使用JsonConverter实现来做到这一点。 There is a very similar post here: Json.NET - Serialize generic type wrapper without property name 这里有一个非常类似的帖子: Json.NET-序列化没有属性名称的通用类型包装器

I've adapted the example to your case. 我已根据您的情况调整了示例。 This is the adapted approach: 这是适应的方法:

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;
        }
    }
}

You can use this as an attribute on LikeType<T> declaration if you want to include this in your library: 如果要将其包含在库中,可以将其用作LikeType<T>声明的属性:

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

Or you can use the converter when necessary, modifying JsonSerializerSettings.Converters collection: 或者,您可以在必要时使用转换器,修改JsonSerializerSettings.Converters集合:

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

I've also created a working dotnetfiddle sample for demonstration (also adapting the one from linked post). 我还创建了一个可工作的dotnetfiddle示例进行演示(还改编了链接文章中的示例 )。

One way of controlling what most serializers serialize is using the Serializable attribute and implement the ISerializable interface . 控制大多数序列化程序进行序列化的一种方法是使用Serializable属性并实现ISerializable接口

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM