You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用JSON.NET实现广度优先的引用序列化

解决JSON.NET深度优先序列化导致层级过深的问题

针对你遇到的Puzzle对象循环引用序列化层级过深的问题,提供以下几种可行方案:

方案一:给实体类的引用属性标记优先序列化引用

直接在Edge、Square、Vertex类的交叉引用属性上添加[JsonProperty(IsReference = true)],强制这些属性在序列化时优先使用引用格式,而非嵌套序列化:

public class Edge
{
    [JsonProperty(IsReference = true)]
    public Vertex VertexA { get; set; }
    
    [JsonProperty(IsReference = true)]
    public Vertex VertexB { get; set; }
    
    [JsonProperty(IsReference = true)]
    public Square AdjacentSquare { get; set; }
}

public class Square
{
    [JsonProperty(IsReference = true)]
    public List<Edge> Edges { get; set; }
    
    [JsonProperty(IsReference = true)]
    public List<Vertex> Vertices { get; set; }
}

public class Vertex
{
    [JsonProperty(IsReference = true)]
    public List<Edge> ConnectedEdges { get; set; }
    
    [JsonProperty(IsReference = true)]
    public List<Square> AdjacentSquares { get; set; }
}

修改后,无论Puzzle的哪个属性先被序列化,交叉引用的对象都会以$ref形式指向已序列化的实例,避免深层嵌套。

方案二:自定义ContractResolver批量处理引用属性

如果不想逐个标记属性,可以自定义契约解析器,自动将所有Edge、Square、Vertex类型的属性(包括单个实例和集合)设置为引用序列化:

public class ReferenceFirstContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        var targetType = property.PropertyType;
        
        // 处理单个实体实例
        var entityTypes = new[] { typeof(Vertex), typeof(Edge), typeof(Square) };
        if (entityTypes.Contains(targetType))
        {
            property.IsReference = true;
            return property;
        }
        
        // 处理实体集合
        if (targetType.IsGenericType && typeof(IEnumerable<>).IsAssignableFrom(targetType.GetGenericTypeDefinition()))
        {
            var elementType = targetType.GetGenericArguments()[0];
            if (entityTypes.Contains(elementType))
            {
                property.IsReference = true;
            }
        }
        
        return property;
    }
}

然后更新序列化器配置,添加这个契约解析器:

private JsonSerializer _serializer = new JsonSerializer()
{
    Formatting = Formatting.Indented,
    PreserveReferencesHandling = PreserveReferencesHandling.All,
    ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
    Converters = { new ArrayReferencePreservingConverter()},
    TypeNameHandling = TypeNameHandling.Auto,
    ContractResolver = new ReferenceFirstContractResolver()
};

方案三:调整属性序列化顺序+统一实体集合

如果希望所有实体对象都出现在Puzzle的下一级,可以调整Puzzle类的属性序列化顺序,让实体集合优先序列化,确保所有实例先被分配$id,后续引用全部用$ref:

public class Puzzle
{
    // 让实体集合优先序列化
    [JsonProperty(Order = -3)]
    public List<Vertex> Vertices { get; set; }
    
    [JsonProperty(Order = -2)]
    public List<Edge> Edges { get; set; }
    
    [JsonProperty(Order = -1)]
    public List<Square> Squares { get; set; }
    
    // 值类型属性放在后面
    public int Size { get; set; }
}

结合方案一或方案二的配置,序列化后所有实体都会在Puzzle的顶层属性中,交叉引用全部为$ref,JSON层级会大幅降低。

内容的提问来源于stack exchange,提问作者Kaito Kid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 22:20:33