自定义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包含类型名称。字典避免了不必要的数据,并确保所有具有相同名称的对象共享同一实例。

自定义JsonConverter-意外行为

看看: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;
            }
        }
    }