我已经编写了一个我可以分配给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?
答案 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.ReadJson
和Example Ctor
的输出,这表明我的自定义转换器确实在使用。 (我还在实际代码中使用嵌套对象成功测试了它。)