簡體   English   中英

處理JSON.net中的引用循環

[英]Handling reference loops in JSON.net

我希望將List<Item>的集合( List<Item> )序列化為JSON。

這些項目的集合Connection ,其給出了關於從一個連接信息Item的第二Item 並且由於連接對象具有對項的引用,因此它使其成為無限循環。

我的問題是,在第二次序列化對象時,有一種方法可以跳過連接集的序列化。

我已經嘗試過繼承JsonConverter和編寫自定義WriteJson()方法之類的東西,但是從那里開始我是否應該寫出數組。

我也嘗試過使用自定義的ContractResolver,但效果不佳。


public class Item
{
    private static int _lastID = 0;

    public Item()
    {
        ID = ++_lastID;
        Connections = new List<Connection>();
    }


    public int ID { get; set; }

    public string Name { get; set; }

    public string Prop1 { get; set; }

    public string Prop2 { get; set; }

    public List<Connection> Connections { get; set; }

}



public class Connection
{
    private Connection(ConnectionType type, Item source, Item target)
    {
        if (type == ConnectionType.None)
            throw new ArgumentException();
        if (source == null)
            throw new ArgumentNullException("source");
        if (target == null)
            throw new ArgumentNullException("target");

        Type = type;
        Source = source;
        Target = target;
    }


    public ConnectionType Type { get; set; }

    public Item Source { get; set; }

    public Item Target { get; set; }


    public static void Connect(ConnectionType type, Item source, Item target)
    {
        var conn = new Connection(type, source, target);
        source.Connections.Add(conn);
        target.Connections.Add(conn);
    }
}


通緝結果:

[
    {
        "id": 1,
        "name": "Item #1",
        "prop1": "val1",
        "prop2": "val2",
        "connections": {
            "type": "ConnType",
            "source": {
                "id": 1,
                "name": "Item #1",
                "prop1": "val1",
                "prop2": "val2"
                // no connections array
            },
            "target": {
                "id": 2,
                "name": "Item #2",
                "prop1": "val1",
                "prop2": "val2"
                // no connections array
            }
        }
    },
    {
        "id": 2,
        "name": "Item #2",
        "prop1": "val1",
        "prop2": "val2",
        "connections": {
            "type": "ConnType",
            "source": {
                "id": 1,
                "name": "Item #1",
                "prop1": "val1",
                "prop2": "val2"
                // no connections array
            },
            "target": {
                "id": 2,
                "name": "Item #2",
                "prop1": "val1",
                "prop2": "val2"
                // no connections array
            }
        }
    }
]



編輯:

C#

var settings = new JsonSerializerSettings
    {
        ContractResolver = new CamelCasePropertyNamesContractResolver(),
        ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
        Formatting = Formatting.Indented
    };
settings.Converters.Add(new StringEnumConverter());
var json = JsonConvert.SerializeObject(collection, settings);

將此添加到您的Global.asax(或WebApiConfig或任何其他配置類)

var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;

您可以引入一個新類,比如ItemClass ,它將包含類Item所有字段,而不是Connections屬性,而不是聲明ItemSourceTarget類型。

public class ItemClass
{
    public int ID { get; set; }

    public string Name { get; set; }

    public string Prop1 { get; set; }

    public string Prop2 { get; set; }
}

public class Connection
{
    // ...

    public ConnectionType Type { get; set; }

    public ItemClass Source { get; set; }

    public ItemClass Target { get; set; }

    // ...
}

現在,您將有相應的填充新ItemClass類型實例的開銷。

存在一個問題,因為在Json中無法引用對象(請參閱通過標識引用對象的標准方式(例如,循環引用)? )。

這意味着您必須在每次引用時復制一個項目,以便將所有連接帶到客戶端。

我建議只使用id將連接帶到客戶端,將連接作為單獨的對象。

添加JsonIgnore屬性的Connections對房地產Item

[JsonIgnore]
public List<Connection> Connections { get; set; }

並使用類直接發送到客戶端而不是項目列表。

    class ConnectionContainer
    {
        private readonly List<Item> _items;
        private readonly List<ConnectionInfo> _connections;

        public ConnectionContainer(IEnumerable<Item> items)
        {
            _items = items.ToList();
            Connections = items.SelectMany(i => i.Connections).Distinct().Select(c => new ConnectionInfo
            {
                Type = c.Type,
                SourceId = c.Source.ID,
                TargetId = c.Target.ID
            }).ToList();
        }

        public List<Item> Items
        {
            get { return _items; }
        }

        public List<ConnectionInfo> Connections
        {
            get { return _connections; }
        }
    }

    class ConnectionInfo
    {
        private ConnectionType Type { get; set; }
        private int SourceId { get; set; }
        private int TargetId { get; set; }
    }

如果我沒有弄錯,你只需要保留包含連接集合的第一個參考深度? 如果是這種情況,請嘗試使用:

settings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
settings.MaxDepth = 1;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM