为什么JsonConvert.DeserializeObject不使用指定的JsonConverter? [英] Why does JsonConvert.DeserializeObject not use the specified JsonConverter?

查看:922
本文介绍了为什么JsonConvert.DeserializeObject不使用指定的JsonConverter?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经编写了一个自定义的JsonConverter,可以将其分配给JsonSerializerSettings并与JsonConvert.DeserializeObject的通用替代一起使用:

I've written a custom JsonConverter that I can assign to JsonSerializerSettings and use with the generic override of JsonConvert.DeserializeObject just fine:

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

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

The serialized Json was built also using TypeNameHandling.All so it contains type information in a $type field.

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

In some scenarios, though, I do not know what type was serialized and would like to use the non-generic override of DeserializeObject. I was expecting that, if I use the same settings and/or converter and the Json contained type information that the engine would be able to process the Json correctly.

但是,看来我的自定义转换器仅用于Json中的嵌套对象,而不用于最高级别-尽管每个级别上均$type.

But it appears that my custom converter is only used for nested objects within the Json and not for the top-most level - despite the $type on each level.

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

My problem is that without my custom converter I need a default constructor for the class. If I implement that - just for testing - then DeserializeObject indeed returns the correct type. But that isn't a real-life option: amongst other things the custom converter resolves the required instances using an IOC container and then populates them.

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

Am I missing something or is what I'm asking for simply not possible?

编辑:由于有人提出要求,因此下面是一些示例代码.在此示例中,反序列化在没有默认构造函数的情况下有效,显然另一个调用了空值(或default(T)).但是基本问题仍然存在:ExampleConverter没有像我期望的那样使用.

Because it was requested, below is some example code. In this example deserializing worked without the default constructor, apparently the other one was called with null values (or default(T)). But the basic problem still exists: the ExampleConverter is not used as I would expect.

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

我也发现了这一点,但答案似乎是错误的:

I also found this, but the answer appears to be wrong: How to deserialize JSON to objects of the correct type, without having to define the type before hand?

推荐答案

对于我的特定用例,可以解决以下问题:

For my specific use-case the following worked out:

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重载.

The idea is to manually parse the string for a $type definition, resolve the type and use that to call the appropriate DeserializeObject overload.

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

This was good enough for me, because in my specific use-case I was always getting JSON as a string (and not e.g. a Stream or JToken) representing one root object (and not an array). The solution could be adapted accordingly, but I'm still hoping for a better/cleaner solution.

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

If I add the method and exchange the according lines in my example from above...

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

...输出为(正确):

...the output is (correctly):

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的输出,这表明确实使用了我的自定义转换器. (我还在我的实际代码中使用嵌套对象成功地对其进行了测试.)

Note the output of ExampleConverter.ReadJson and Example Ctor in both deserialization cases which shows that my custom converter is indeed being used. (I also tested it successfully with nested objects in my actual code.)

这篇关于为什么JsonConvert.DeserializeObject不使用指定的JsonConverter?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆