为什么JsonConvert.DeserializeObject不使用指定的JsonConverter?

时间:2018-05-16 15:24:43

标签: c# json json.net deserialization

我已经编写了一个我可以分配给JsonConverter的自定义JsonSerializerSettings,并使用JsonConvert.DeserializeObject的通用覆盖就好了:

var settings = new JsonSerializerSettings()
{
    TypeNameHandling = TypeNameHandling.All,
    Converters = new List<JsonConverter>() { new MyConverter() }
};
var x = JsonConvert.DeserializeObject<MyType>(input, settings);

序列化的Json也使用TypeNameHandling.All构建,因此它包含$type字段中的类型信息。

但在某些情况下,我不知道序列化了什么类型,并且想要使用DeserializeObject的非泛型覆盖。我期待的是,如果我使用相同的设置和/或转换器,并且Json包含类型信息,引擎将能够正确处理Json。

但似乎我的自定义转换器仅用于Json中的嵌套对象,而不是最高级别 - 尽管每个级别都有$type

我的问题是没有我的自定义转换器,我需要一个类的默认构造函数。如果我实现它 - 仅用于测试 - 那么DeserializeObject确实返回正确的类型。但这不是一个现实生活中的选择:除其他外,自定义转换器使用IOC容器解析所需的实例,然后填充它们。

我错过了什么或者我要求的根本不可能?

编辑:因为有人请求,下面是一些示例代码。在这个示例中,反序列化在没有默认构造函数的情况下工作,显然另一个是使用空值(或default(T))调用的。但基本问题仍然存在:ExampleConverter未按预期使用。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;

namespace UnitTests.Serialization
{
    public class Example
    {
        public Example()
        {
            Console.WriteLine("...Example Default Ctor...");
        }

        public Example(Guid guid)
        {
            Console.WriteLine("...Example Ctor: " + guid.ToString());
        }
        public string ExampleProp { get; set; }
    }

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

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer){
            Console.WriteLine("...ExampleConverter.ReadJson...");
            var result = new Example(Guid.Empty);

            serializer.Populate(reader, result);

            return result;
        }

        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(Example);
        }
    }

    [TestClass]
    public class JsonTests
    {
        [TestMethod]
        public void TestDeserializeObject()
        {
            var writeSettings = new JsonSerializerSettings() {TypeNameHandling = TypeNameHandling.All,};
            var readSettings = new JsonSerializerSettings() {TypeNameHandling = TypeNameHandling.All,Converters = new List<JsonConverter>() { new ExampleConverter() }};

            Console.WriteLine("Creating Example...");
            var e1 = new Example(Guid.NewGuid()) { ExampleProp = "some value"};

            Console.WriteLine("\nSerializing e1...");
            var json = Newtonsoft.Json.JsonConvert.SerializeObject(e1, writeSettings);
            Console.WriteLine("e1: " + json);

            Console.WriteLine("\nDeserializing e2 - using DeserializeObject<Example>...");
            var e2 = Newtonsoft.Json.JsonConvert.DeserializeObject<Example>(json, readSettings);
            Console.WriteLine("e2: " +  Newtonsoft.Json.JsonConvert.SerializeObject(e2, writeSettings));

            Console.WriteLine("\nDeserializing e3 - using DeserializeObject...");
            var e3 = Newtonsoft.Json.JsonConvert.DeserializeObject(json, readSettings);
            Console.WriteLine("e3: " + Newtonsoft.Json.JsonConvert.SerializeObject(e2, writeSettings));

        }
    }
}

输出:

Creating Example...
...Example Ctor: d860aa00-4493-4ab0-b681-f0af7b123212

Serializing e1...
e1: {"$type":"UnitTests.Serialization.Example, UnitTests","ExampleProp":"some value"}

Deserializing e2 - using DeserializeObject<Example>...
...ExampleConverter.ReadJson...
...Example Ctor: 00000000-0000-0000-0000-000000000000
e2: {"$type":"UnitTests.Serialization.Example, UnitTests","ExampleProp":"some value"}

Deserializing e3 - using DeserializeObject...
...Example Default Ctor...
e3: {"$type":"UnitTests.Serialization.Example, UnitTests","ExampleProp":"some value"}

编辑:我也发现了这一点,但答案似乎错了:How to deserialize JSON to objects of the correct type, without having to define the type before hand?

1 个答案:

答案 0 :(得分:0)

对于我的具体用例,以下结果如下:

public object DeserializeFromTypedString(string input, JsonSerializerSettings settings)
{
    var r = new Regex(@"^\{\s*""\$type"":\s*""([^""]+)""");

    var m = r.Match(input);
    if (m.Success)
    {
        var t = Type.GetType(m.Groups[1].Value);
        return Newtonsoft.Json.JsonConvert.DeserializeObject(input, t, settings);
    }
    else
    {
        throw new Exception("$type not found!");
    }
}

我们的想法是手动解析字符串中的$type定义,解析类型并使用它来调用相应的DeserializeObject重载。

这对我来说已经足够了,因为在我的特定用例中,我总是将JSON作为表示一个根对象(而不是数组)的字符串(而不是例如Stream或JToken)。解决方案可以相应调整,但我仍然希望有更好/更清洁的解决方案

如果我添加方法并在上面的示例中交换相应的行......

Console.WriteLine("\nDeserializing e3 - using DeserializeFromTypeString...");
var e3 = DeserializeFromTypedString(json, readSettings);

...输出是(正确):

Creating Example...
...Example Ctor: b5af2c3f-1c03-49d8-85c6-f3ff60c9f711

Serializing e1...
e1: {"$type":"UnitTests.Serialization.Example, UnitTests","Guid":"b5af2c3f-1c03-49d8-85c6-f3ff60c9f711","ExampleProp":"some value"}

Deserializing e2 - using DeserializeObject<Example>...
...ExampleConverter.ReadJson...
...Example Ctor: 00000000-0000-0000-0000-000000000000
e2: {"$type":"UnitTests.Serialization.Example, UnitTests","Guid":"00000000-0000-0000-0000-000000000000","ExampleProp":"some value"}

Deserializing e3 - using DeserializeFromTypedString...
...ExampleConverter.ReadJson...
...Example Ctor: 00000000-0000-0000-0000-000000000000
e3: {"$type":"UnitTests.Serialization.Example, UnitTests","Guid":"00000000-0000-0000-0000-000000000000","ExampleProp":"some value"}

请注意两个反序列化情况下ExampleConverter.ReadJsonExample Ctor的输出,这表明我的自定义转换器确实在使用。 (我还在实际代码中使用嵌套对象成功测试了它。)