防止对象图序列化的循环引用

Posted

技术标签:

【中文标题】防止对象图序列化的循环引用【英文标题】:Circular references preventing serialization of object graph 【发布时间】:2013-08-25 21:20:58 【问题描述】:

我有一个涉及杂草和杂草家族的简单数据模型。

WeedFamily <-1---*-> Weed(WeedFamily 和 Weed 是一对多的关系)

我正在尝试完成我的第一个 ApiController,以便我可以轻松地将我的数据作为 JSON 检索到 AngularJS 应用程序。当我在我的应用程序中访问 /WeedAPI/ URL 时,我收到以下错误。我很确定问题是我在WeedWeedFamily 之间有循环引用。

我应该如何更改我的数据模型,以便 JSON 序列化能够正常工作,同时保持Weed-WeedFamily 关系的双向质量?

(即。我仍然希望能够构建如下表达式:

 WeedData.GetFamilies()["mustard"].Weeds.Count

WeedData.GetWeeds()[3].Family.Weeds

)

错误:

<Error>
    <Message>An error has occurred.</Message>
    <ExceptionMessage>
        The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
    </ExceptionMessage>
    <ExceptionType>System.InvalidOperationException</ExceptionType>
    <StackTrace/>
    <InnerException>
        <Message>An error has occurred.</Message>
        <ExceptionMessage>
            Object graph for type 'WeedCards.Models.WeedFamily' contains cycles and cannot be serialized if reference tracking is disabled.
        </ExceptionMessage>
        <ExceptionType>
            System.Runtime.Serialization.SerializationException
        </ExceptionType>
        <StackTrace>
            at System.Runtime.Serialization.XmlObjectSerializerWriteContext.OnHandleReference(XmlWriterDelegator xmlWriter, Object obj, Boolean canContainCyclicReference) at WriteWeedToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract ) at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDecl...etc
        </StackTrace>
    </InnerException>
</Error>

我的数据:

public class WeedData

    public static Dictionary<string,WeedFamily> GetFamilies()
        return new Dictionary<string,WeedFamily>
        
             "mustard",new WeedFamily("Mustard","Brassicaceae")
            ,"pigweed",new WeedFamily("Pigweed","Amaranthus")
            ,"sunflower",new WeedFamily("Sunflower","Asteraceae")
        ;
    

    public static List<Weed> GetWeeds()
        var Families = GetFamilies();
        return new List<Weed>
        
             new Weed("Hairy Bittercress","Cardamine hirsuta",Families["mustard"])
            ,new Weed("Little Bittercress","Cardamine oligosperma",Families["mustard"])
            ,new Weed("Shepherd's-Purse","Capsella bursa-pastoris",Families["mustard"])
            ,new Weed("Wild Mustard","Sinapis arvensis / Brassica kaber",Families["mustard"])
            ,new Weed("Wild Radish","Raphanus raphanistrum",Families["mustard"])
            ,new Weed("Radish","Raphanus sativus",Families["mustard"])
            ,new Weed("Redroot Pigweed","Amaranthus retroflexus",Families["pigweed"])
            ,new Weed("Prickly Lettuce","Lactuca serriola",Families["sunflower"])
            ,new Weed("Spiny Sowthistle","Sonchus asper",Families["sunflower"])
            ,new Weed("Annual Sowthistle","Sonchus oleraceus",Families["sunflower"])

        ;
    

我的模型类:

[Serializable]
public class Weed

    public string CommonName;
    public string LatinName;
    public List<WeedPhoto> Photos;
    public WeedFamily Family;

    public Weed(string commonName, string latinName)
    
        CommonName = commonName;
        LatinName = latinName;
    

    public Weed(string commonName, string latinName, WeedFamily family)
    
        CommonName = commonName;
        LatinName = latinName;
        Family = family;
        Family.Weeds.Add(this);
    

    override public string ToString()
    
        return CommonName + " (" + LatinName + ")";
    

[Serializable]
public class WeedFamily

    public string CommonName;
    public string LatinName;
    public List<Weed> Weeds;

    public WeedFamily(string commonName, string latinName)
    
        CommonName = commonName;
        LatinName = latinName;
        Weeds = new List<Weed>();
    

最后是 ApiController:

public class WeedAPIController : ApiController

    //
    // GET: /WeedAPI/

    public IEnumerable<Weed> GetAllWeeds()
    
        return WeedData.GetWeeds();
    


【问题讨论】:

你为什么要覆盖ToString()?您可能只想使用 [DebuggerDisplay] 属性。 【参考方案1】:

[DataContract(IsReference = true)] 添加到具有循环引用的对象中。

[Serializable]
[DataContract(IsReference = true)]
public class WeedFamily

[Serializable]
[DataContract(IsReference = true)]
public class Weed

见http://msdn.microsoft.com/en-us/library/vstudio/hh241056(v=vs.100).aspx

【讨论】:

我需要导入才能使用该属性吗? The type or namespace name 'DataContractAttribute' could not be found 是的,它在 System.Runtime.Serialization 命名空间中 原来我需要在我的 VS 项目中添加 System.Runtime.Serialization.dll 作为参考:***.com/questions/7401795/namespace-for-datacontract 在我这样做之后,我的序列化工作正常!谢谢。 如果我使用 json 序列化器会怎样

以上是关于防止对象图序列化的循环引用的主要内容,如果未能解决你的问题,请参考以下文章

javascript中的循环引用对象处理

Node.js:如何使用循环引用序列化大对象

序列化对象时出现循环引用错误。一对多关联对象

EF中Json序列化对象时检测到循环引用的解决办法

Kendo UI - InvalidOperationException:序列化类型对象时检测到循环引用

序列化“System.Reflection”类型的对象时检测到循环引用