json.net序列化为通用基础

时间:2018-06-22 09:48:23

标签: c# json.net c#-7.0

(此问题源于试图将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>派生的类都将以T相同的方式通过Json.Net序列化/反序列化为JSON?

(在这种情况下,Json.Net应该以与字符串相同的方式对Name进行序列化/反序列化)

2 个答案:

答案 0 :(得分:1)

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

我建议使用JsonConverter实现。这里有一个非常相似的帖子:Json.NET - Serialize generic type wrapper without property name

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

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进行演示(也改编自链接文章)。

答案 1 :(得分:0)

控制大多数序列化程序进行序列化的一种方法是使用Serializable attribute并实现ISerializable interface