自定义JsonConverter-意外行为
本文关键字:意外 JsonConverter- 自定义 | 更新日期: 2023-09-27 18:16:01
目标
我需要序列化一些类(我正在尝试使用JSON.NET(。有些人有自荐信。对于其中的1个,我需要能够决定要序列化哪些字段(2个可能的选择(。这门课是最难的部分。这是类的简化示例(Node是特殊类(:
// classes
class TreeObject
{ }
class Node : TreeObject // needs to be specially serialized
{
public string name;
public List<TreeObject> childs; // can contain self-reference
public Node()
{
name = null;
childs = new List<TreeObject>();
}
}
我尝试通过继承JsonConverter并重写方法为Node类创建JsonConverter:"CanConvert"、"WriteJson"(用于序列化(和"ReadJson"。
这就是我如何尝试使用我的"NodeConverter":
// create object
Node parent = new Node();
parent.name = null;
parent.childs.Add(parent);
// create settings
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
settings.TypeNameHandling = TypeNameHandling.Auto;
settings.Converters = new[] {new NodeConverter()}; // add my converter
settings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
// serialize
string json = JsonConvert.SerializeObject(parent, Formatting.Indented, settings);
// expected result
json == @"{
""$id"": ""1"",
""$type"": ""Node, test"",
""childs"": [
{
""$ref"": ""1""
}
]
}"; // the project is called "test"
问题
- 我需要保留引用(例如:$id和$ref字段(
- 我还需要保留类型信息,因为我使用多态性/继承(例如:$type字段(
但是当我尝试将JsonConverter添加到JsonSerializerSettings(用于序列化和反序列化(时,JSON.NET会自动停止处理这些功能。
问题
- 当我使用自己的JsonConverter时,如何让JSON.NET处理对象引用和类型信息的保留
- 如何使反序列化也能正常工作
- 有没有更适合这个的JSON库
我花了几个小时研究如何使这项工作,如果有任何帮助,将不胜感激
编辑:我试着缩短我的问题
编辑:
序列化&反序列化逻辑如下所示:
string SerializeNode(Node node)
{
if(node.name == null)
{
compress(node);
string strNode = (serialize all fields except the name field);
decompress(node);
return strNode;
}
else
{
return (serialize only the name field);
}
}
Node DeserializeNode(string strNode)
{
if(strNode does not have name field)
{
Node node = new Node();
deserialize all strNode fields and assign them to node;
decompress(node);
return node;
}
else
{
return KnownNodes[strNode name field];
// where KnownNodes is a Dictionary<string, Node>
}
}
当然,我仍然希望JSON.NET保留引用(现在使用PreserveReferenceHandling.Objects选项(,并使用TypeNameHandling.Auto包含类型名称。字典避免了不必要的数据,并确保所有具有相同名称的对象共享同一实例。
看看:http://christianarg.wordpress.com/2012/11/06/serializing-and-deserializing-inherited-types-with-json-anything-you-want/
[TestMethod]
public void List_Test()
{
var resource = new Resource() { ResourceProperty = "Resource" };
var video = new Video() { ResourceProperty = "Video", VideoProperty = "VideoMP4" };
var list = new List<Resource>() { resource, video };
// Again the important part are the settings
var settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Objects
};
var serializedList = JsonConvert.SerializeObject(list, settings);
var deserializedList = JsonConvert.DeserializeObject<List<Resource>> (serializedList, settings);
// And we recover the information with NO data loss
Assert.AreEqual("Resource", deserializedList[0].ResourceProperty);
Assert.AreEqual("VideoMP4", ((Video)deserializedList[1]).VideoProperty);
}
编辑:
就我个人而言,我使用字符串扩展名:
public static bool DeserializeJson<T>(this String str, out T item)
{
var returnResult = default(T);
var success = Ui.Instance.Try(
() =>
{
returnResult = new JavaScriptSerializer().Deserialize<T>(str);
},
"Deserializing json " + typeof(T),
"Deserializing json done",
"Deserializing json failed",
isCritical:false
);
item = returnResult;
return success;
}
然后我可以使用
Node node;
if(GetJson(service, out jsonstring) && jsonstring.DeserializeJson(out node)){
或
TreeObject node;
或
... jsonstring.DeserializeJson<TreeObject>(out node)){
5年后,我遇到了同样的问题。下面是一个保持引用正确解析的示例。请注意对serializer.ReferenceResolver
的调用,它起到了神奇的作用。
public class EntityConverter : JsonConverter<Entity>
{
public override void WriteJson( JsonWriter writer, Entity value, JsonSerializer serializer )
{
var me = new JObject();
me["$id"] = new JValue( serializer.ReferenceResolver.GetReference( serializer, value ) );
me["your_array"] = JArray.FromObject( value.components, serializer );
me["your_other_values"] = new JValue( value.tag );
me.WriteTo( writer );
}
public override Entity ReadJson( JsonReader reader, Type objectType, Entity existingValue, bool hasExistingValue, JsonSerializer serializer )
{
var o = JObject.Load( reader );
var id = (string)o["$id"];
if( id != null )
{
var entity = new Entity();
serializer.Populate( o.CreateReader(), entity );
return entity;
}
else
{
var reference = (string)o["$ref"];
return serializer.ReferenceResolver.ResolveReference( serializer, reference ) as Entity;
}
}
}