Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Circular references preventing serialization of object graph

Tags:

I've got a simple data model involving Weeds and Weed Families.

WeedFamily <-1---*-> Weed (WeedFamily and Weed have a one-to-many relationship)

I'm attempting to complete my first ApiController so that I can easily retrieve my data as JSON for an AngularJS application. When I access the /WeedAPI/ URL in my application, I get the following error. I'm pretty sure the problem is that I have circular references between Weed and WeedFamily.

How should I change my data model so that the JSON serialization will work while maintaining the bi-directional quality of the Weed-WeedFamily relationship?

(ie. I want to still be able to build expressions like the following:

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

and

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

)

The error:

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

My data:

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"])          };     } } 

My model classes:

[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 + ")";     } } 

and

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

Finally, the ApiController:

public class WeedAPIController : ApiController {     //     // GET: /WeedAPI/      public IEnumerable<Weed> GetAllWeeds()     {         return WeedData.GetWeeds();     }  } 
like image 958
Jonathan Wilson Avatar asked Aug 22 '13 23:08

Jonathan Wilson


1 Answers

Add [DataContract(IsReference = true)] to the objects that have circular references.

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

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

like image 59
Hack Avatar answered Nov 06 '22 20:11

Hack