【问题标题】:How change object type to another when deserialising with JSON.Net or call default serialiser?使用 JSON.Net 反序列化或调用默认序列化程序时如何将对象类型更改为另一种?
【发布时间】:2017-01-09 00:04:28
【问题描述】:

序列化时,我将 ClassName 写入对象到 _CurrentClassName 属性中。当使用 JSON.Net 库读取 json 时,我需要将对象更改为该属性的值。

{
 "Field1": 0,
 "Field2": "34",
 "_CurrentClassName": "MyCustomClass"
}


class CustomJsonConverter : JsonConverter
{
...
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsClass;
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var value = existingValue;
        if (reader.TokenType == JsonToken.Null)
        {
            return null;
        }
        else if (reader.TokenType == JsonToken.StartObject)
        {
            JObject jObject = JObject.Load(reader);
            JToken jToken;
            if (jObject.TryGetValue("_CurrentClassName", out jToken))
            {
                var t = jToken.Value<string>();
                Type tt = Type.GetType(objectType.Namespace + "." + t);
                value = Activator.CreateInstance(tt);
                return value;
            }
        }
        return serializer.Deserialize(reader);            
    }

...
}

【问题讨论】:

  • 您可以做类似于Json.Net Serialization of Type with Polymorphic Child Object 的回答中所做的事情。
  • 谢谢。类似,但我不能使用“硬”类型,我的 CanConvert 看起来像: public override bool CanConvert(Type objectType) { return objectType.IsClass; }
  • 您是否与该属性名称和语法结婚,或者您可以使用内置的 Json.Net 来处理这个?
  • 如果您将new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All } 传递给序列化或反序列化方法,您将添加类似$"type": "Namespace.Type, Assembly" 的属性。

标签: c# .net serialization json.net


【解决方案1】:

在推断出对象类型并实例化对象后,您可以使用JsonSerializer.Populate(jObject.CreateReader()) 来填充它。

例如:

public abstract class CustomJsonConverterBase : JsonConverter
{
    protected abstract Type InferType(JToken token, Type objectType, object existingValue);

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var token = JToken.Load(reader);
        var actualType = InferType(token, objectType, existingValue);
        if (existingValue == null || existingValue.GetType() != actualType)
        {
            var contract = serializer.ContractResolver.ResolveContract(actualType);
            existingValue = contract.DefaultCreator();
        }
        using (var subReader = token.CreateReader())
        {
            // Using "populate" avoids infinite recursion.
            serializer.Populate(subReader, existingValue);
        }
        return existingValue;
    }

    public override bool CanWrite { get { return false; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

public class CustomJsonConverter : CustomJsonConverterBase
{
    public const string ClassPropertyName = @"_CurrentClassName";

    protected override Type InferType(JToken token, Type objectType, object existingValue)
    {
        if (token is JObject)
        {
            var typeName = (string)token[ClassPropertyName];
            if (typeName != null)
            {
                var actualType = Type.GetType(objectType.Namespace + "." + typeName);
                if (actualType != null)
                    return actualType;
            }
        }
        return objectType;
    }

    public override bool CanConvert(Type objectType)
    {
        throw new NotImplementedException();
    }
}

CustomCreationConverter&lt;T&gt; 也使用serializer.Populate() 来填充刚刚分配的对象,因此这是解决此问题的标准方法。

请注意,您正在部分复制 Json.NET 的内置 TypeNameHandling 功能。

【讨论】:

  • 谢谢,我会尽量理解和使用这个。
  • 使用过滤系统类型。谢谢! public override bool CanConvert(Type objectType) { // throw new NotImplementedException(); return objectType.IsClass && objectType.Namespace != null && !objectType.Namespace.StartsWith("System") ; }
【解决方案2】:

如果您没有使用 _CurrentClassName 属性名称或其值语法,则可以使用 Json.Net 内置的类型处理。

在序列化或反序列化时,您可以传入控制序列化或反序列化的JsonSerializerSettings 对象。

在此对象上,您可以设置一个TypeNameHandling 属性,该属性控制 Json.Net 如何序列化和反序列化正在处理的确切类型。

这是一个LINQPad 示例:

void Main()
{
    var t = new Test { Key = 42, Value = "Meaning of life" };

    var json = JsonConvert.SerializeObject(
        t, Newtonsoft.Json.Formatting.Indented,
        new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
    Console.WriteLine(json);

    var obj  =JsonConvert.DeserializeObject(json,
        new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
    Console.WriteLine(obj.GetType().FullName);
}

public class Test
{
    public int Key { get; set; }
    public string Value { get; set; }
}

输出:

{
  "$type": "UserQuery+Test, LINQPadQuery",
  "Key": 42,
  "Value": "Meaning of life"
}
UserQuery+Test

这里可以看到反序列化返回的对象的类型是Test类。

【讨论】:

  • 已婚。在 typename 中使用 C# 命名空间是不可接受的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多