Newtonsoft JSON/JSON.NET 自定义序列化 - System.Object 成员未正确反序列化,即使



我正在尝试使用Newtonsoft.Json实现自定义序列化,我希望将所有字段序列化,然后反序列化为正确的类型。该类包含一个类型为"object"的字段,实现ISerializable,具有[Serializable]属性集,并具有序列化构造函数。我将这个对象字段的值设置为某个类的实例,然后对其进行序列化。我使用TypeNameHandling设置为TypeNameHandling.AutoJsonSerializer

这是我正在尝试的代码:

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.IO;
using System.Runtime.Serialization;
namespace ConsoleApp1
{
class Program
{
[Serializable]
class Foobar : ISerializable
{
public Foobar()
{
}
public Foobar(SerializationInfo info, StreamingContext context)
{
something = info.GetValue("something", typeof(object));
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("something", something);
}
public object Something { get { return something; } set { something = value; } }
private object something;
[JsonIgnore]
private string someOtherObject = "foobar";
}
class SomeOtherClass
{
public string Foo { get; set; }
public string Bar { get; set; }
}
static void Main(string[] args)
{
Foobar myObj = new Foobar();
myObj.Something = new SomeOtherClass() { Bar = "My first", Foo = "fqwkifjwq" };
var serializer = new Newtonsoft.Json.JsonSerializer();
serializer.TypeNameHandling = TypeNameHandling.Auto;
serializer.Formatting = Formatting.Indented;
string serialized;
using (var tw = new StringWriter())
{
using (var jw = new JsonTextWriter(tw))
serializer.Serialize(jw, myObj);
tw.Flush();
serialized = tw.ToString();
}
Foobar deserialized;
using (var rt = new StringReader(serialized))
using (var jsonReader = new JsonTextReader(rt))
deserialized = serializer.Deserialize<Foobar>(jsonReader);
Console.WriteLine("Type of deserialized.Something: " + deserialized.Something.GetType().FullName);
}
}
}

反序列化后,字段Foobar.Something只是一个Newtonsoft.Json.Linq.JObject,这不是我想要的。我希望将其正确反序列化为类型SomeOtherClass的对象。序列化输出包含所需的信息:

{
"something": {
"$type": "ConsoleApp1.Program+SomeOtherClass, ConsoleApp1",
"Foo": "fqwkifjwq",
"Bar": "My first"
}
}

这是我到目前为止尝试过的:

  1. 使用上面的代码。完全按照我上面描述的方式做。
  2. 在我正在序列化的对象上使用[JsonObject(MemberSerialization = MemberSerialization.Fields)]属性。然后我在Foobar.Something字段(SomeOtherClass实例)上得到一个正确类型的对象,但是,任何具有默认值的非序列化字段都会初始化为 null 而不是它们的默认值(如字段Foobar.someOtherObject)。
  3. 删除Serializable属性。然后不调用ISerializable GetObjectData和序列化构造函数。
  4. 将 JsonSerializer 的TypeNameHandling属性设置为All(无效)。

那么 - 关于如何解决这个问题的任何提示?

因此,总结一下您的问题/评论:

  1. 您有一个庞大而复杂的遗留数据结构,您希望对其进行序列化和反序列化。
  2. 要反序列化的大部分数据都在私有字段中,向这些字段添加属性或添加公共属性太麻烦了。
  3. 其中一些字段的类型为object,您希望为往返保留这些值的类型。
  4. 其中一些字段被明确标记为出于序列化目的而被忽略,并且它们具有您希望保留的默认值。
  5. 您尝试使用TypeNameHandling.Auto并实现ISerializable;这适用于序列化(子对象类型已写入JSON),但不适用于反序列化(您得到了一个JObject而不是实际的子对象实例)。
  6. 您尝试使用TypeNameHandling.Auto并用MemberSerialization.Fields标记外部类;这适用于序列化,但在反序列化时,您丢失了忽略字段的默认值。

让我们看看这两种方法,看看我们能做什么。

可独立化

Json.Net 在序列化时确实将这些对象的类型信息写入 JSON 时,不尊重ISerializable子对象的反序列化TypeNameHandling.Auto设置,这似乎有点奇怪。 我不知道这是一个错误,一个疏忽,还是这里有一些技术限制。 无论如何,它的行为都不符合预期。

但是,您可以实施解决方法。 由于您确实在SerializationInfo中获得了JObject,并且该JObject中包含$type字符串,因此您可以创建一个扩展方法,该方法将根据$typeJObject创建子对象:

public static class SerializationInfoExtensions
{
public static object GetObject(this SerializationInfo info, string name)
{
object value = info.GetValue(name, typeof(object));
if (value is JObject)
{
JObject obj = (JObject)value;
string typeName = (string)obj["$type"];
if (typeName != null)
{
Type type = Type.GetType(typeName);
if (type != null)
{
value = obj.ToObject(type);
}
}
}
return value;
}
}

然后,在序列化构造函数中使用它代替GetValue,只要类型object

public Foobar(SerializationInfo info, StreamingContext context)
{
something = info.GetObject("something");
}

成员序列化。字段

看起来使用[JsonObject(MemberSerialization = MemberSerialization.Fields)]属性除了丢失某些被忽略属性的默认值外,大部分时间都在执行您想要的操作。 事实证明,Json.Net 故意在使用MemberSerialization.Fields时不调用普通构造函数 - 而是使用FormatterServices.GetUninitializedObject方法创建一个完全空的对象,然后再从 JSON 填充字段。 这显然会阻止您的默认值被初始化。

要解决此问题,您可以使用自定义ContractResolver来替换对象创建函数,例如:

class FieldsOnlyResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
contract.DefaultCreator = () => Activator.CreateInstance(objectType, true);
return contract;
}
}

注意:以上假设所有对象都将具有默认(无参数)构造函数。 如果不是这种情况,则可以根据需要添加它们(它们可以是私有的),或者更改解析程序以根据类型提供不同的创建者函数。

要使用它,只需将解析程序添加到您的JsonSerializer实例:

serializer.ContractResolver = new FieldsOnlyResolver();

相关内容

最新更新