自定义JsonConverter - 意外行为

时间:2014-08-02 19:00:09

标签: c# .net json serialization json.net

目标

我需要序列化一些类(我试图使用JSON.NET)。有些人有自我参考。对于其中的一个,我需要能够决定序列化哪些字段(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:&#34; CanConvert&#34;,&#34; WriteJson&#34; (用于序列化),&#34; ReadJson&#34; (用于反序列化)。

这是我如何尝试使用我的&#34; NodeConverter&#34;:

// 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 fields)

但是当我尝试将JsonConverter添加到JsonSerializerSettings(用于序列化和反序列化)时,JSON.NET会自动停止处理这些功能。

问题

  • 当我使用自己的JsonConverter时,如何让JSON.NET处理对象引用的保存和类型信息?
  • 如何使反序列化正常工作?
  • 是否有更适合这种情况的JSON库?

我花了好几个小时查找如何使这项工作,任何帮助将不胜感激

编辑:我试图缩短我的问题

编辑:

序列化&amp;反序列化逻辑如下所示:

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保留引用(现在使用PreserveReferencesHandling.Objects选项),并且还包含TypeNameHandling.Auto的类型名称。字典避免了不必要的数据,并确保为所有具有相同名称的对象共享相同的实例。

2 个答案:

答案 0 :(得分:0)

看看: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)){

答案 1 :(得分:0)

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