我正在尝试使用Newtonsoft.Json实现自定义序列化,在这里我希望所有字段都进行序列化,然后反序列化为它们的正确类型。该类包含“对象”类型的字段,实现ISerializable
,具有[Serializable]
属性集以及序列化构造函数。我将此对象字段的值设置为某个类的实例,然后对其进行序列化。我使用JsonSerializer
设置为TypeNameHandling
的{{1}}。
这是我正在尝试的代码:
TypeNameHandling.Auto
反序列化后,字段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"
}
}
属性。然后,我在[JsonObject(MemberSerialization = MemberSerialization.Fields)]
字段(一个Foobar.Something
实例)上获得了正确类型的对象,但是,任何具有默认值的非序列化字段都将初始化为null而不是其默认值(例如字段SomeOtherClass
)。Foobar.someOtherObject
属性。然后Serializable
和序列化构造函数不会被调用。ISerializable GetObjectData
属性设置为TypeNameHandling
(无效)。那么-关于如何解决此问题的任何提示?
答案 0 :(得分:1)
因此,总结一下您的问题/评论:
object
,并且您希望保留往返的那些值的类型。TypeNameHandling.Auto
并实现ISerializable
;这适用于序列化(子对象类型已写入JSON),但不适用于反序列化(返回了JObject
而不是实际的子对象实例)。TypeNameHandling.Auto
,并用MemberSerialization.Fields
标记您的外部类;这适用于序列化,但是在反序列化时,您丢失了忽略字段的默认值。让我们看看两种方法,看看我们能做什么。
当Json.Net确实将序列类型的信息写入JSON时,Json.Net不尊重TypeNameHandling.Auto
子对象的反序列化上的ISerializable
设置似乎有些奇怪。我不知道这是错误,疏忽,还是这里存在技术限制。无论如何,它的行为均不符合预期。
但是,您也许可以实施解决方法。由于您确实在{{1}中返回了JObject
,并且SerializationInfo
中包含了JObject
字符串,因此您可以创建一个扩展方法,该方法将从基于$type
的{{1}}:
JObject
然后,只要类型为$type
,就在序列化构造函数中代替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");
}
时Json.Net故意不调用普通的构造函数-而是使用FormatterServices.GetUninitializedObject
方法在填充字段之前创建完全空对象从JSON。显然,这将防止默认值被初始化。
要解决此问题,您可以使用自定义[JsonObject(MemberSerialization = MemberSerialization.Fields)]
来替换对象创建功能,例如:
MemberSerialization.Fields
注意:上面假设所有对象都将具有默认(无参数)构造函数。如果不是这种情况,则可以根据需要添加它们(它们可以是私有的),也可以更改解析器以根据类型提供不同的创建者功能。
要使用它,只需将解析器添加到您的ContractResolver
实例:
class FieldsOnlyResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
contract.DefaultCreator = () => Activator.CreateInstance(objectType, true);
return contract;
}
}