处理json.net中的参考循环



我希望将项目的集合( List<Item>)序列化为json。

这些项目具有Connection的集合,可提供有关从一个Item到第二Item的连接的信息。而且由于连接对象对项目的参考,它使其成为无限循环。

我的问题是我第二次序列化对象时跳过连接集合的序列化。

我尝试了诸如从JsonConverter继承并编写自定义WriteJson()方法之类的事情,但是从那里我不应该写这个数组。

我也尝试使用自定义的违规器,但没有好的结果。


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;

如果我没记错的话,您只需要保留包含连接集合的第一个参考深度吗?如果是这样,请尝试使用:

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

,而不是在 Item时声明SourceTarget的类型,您可以介绍一个新类,例如ItemClass,它将包含Item类的所有字段,除Connections属性外。

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属性添加到Item上的Connections属性。

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

最新更新