2010-09-28 14 views
18

Recibo la siguiente excepción al tratar de serializar un objeto utilizando XMLSerialization.Referencia circular al usar serialización XML?

A circular reference was detected while serializing an object of type MyObject}

sé la referencia circular es porque Objecta puede tener un childObject de ObjectB y ObjectB de parentObject es Objecta, sin embargo, me gustaría mantener esa referencia si es posible. ¿Hay alguna manera de hacer que este objeto se serialice con Serialización XML sin perder ningún dato durante el proceso de serialización? No estoy muy familiarizado con la serialización, así que espero que haya algún tipo de Atributo que pueda establecer.

Respuesta

23

Existen varias opciones dependiendo del tipo de serializador.

Si usted podría utilizar DataContractSerializer o BinaryFormatter entonces puede usar OnSerializedAttribute y establezca la propiedad Parent para su objeto secundario a esto:

[Serializable] 
public class Child 
{ 
    public string Foo { get; set; } 

    public Parent Parent { get { return parent; } set { parent = value; } } 

    // We don't want to serialize this property explicitly. 
    // But we could set it during parent deserialization 
    [NonSerialized] 
    private Parent parent; 
} 

[Serializable] 
public class Parent 
{ 
    // BinaryFormatter or DataContractSerializer whould call this method 
    // during deserialization 
    [OnDeserialized()] 
    internal void OnSerializedMethod(StreamingContext context) 
    { 
     // Setting this as parent property for Child object 
     Child.Parent = this; 
    } 

    public string Boo { get; set; } 

    public Child Child { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Child c = new Child { Foo = "Foo" }; 
     Parent p = new Parent { Boo = "Boo", Child = c }; 

     using (var stream1 = new MemoryStream()) 
     { 
      DataContractSerializer serializer = new DataContractSerializer(typeof (Parent)); 
      serializer.WriteObject(stream1, p); 
      stream1.Position = 0; 
      var p2 = (Parent)serializer.ReadObject(stream1); 

      Console.WriteLine(object.ReferenceEquals(p, p2)); //return false 
      Console.WriteLine(p2.Boo); //Prints "Boo" 

      //Prints: Is Parent not null: True 
      Console.WriteLine("Is Parent not null: {0}", p2.Child.Parent != null); 
     } 
    } 

} 

Si desea utilizar XmlSerializer debe implementar IXmlSerializable, utilice XmlIgnoreAttribute y una aplicación más o menos la misma lógica en el método ReadXml. Pero en este caso también debería implementar manualmente toda la lógica de serialización Xml:

[Serializable] 
public class Child 
{ 
    public Child() 
    { 
    } 

    public string Foo { get; set; } 

    [XmlIgnore] 
    public Parent Parent { get; set; } 
} 

[Serializable] 
public class Parent 
{ 
    public Parent() 
    { 
    } 

    #region IXmlSerializable Members 

    public System.Xml.Schema.XmlSchema GetSchema() 
    { 
     throw new NotImplementedException(); 
    } 

    public void ReadXml(System.Xml.XmlReader reader) 
    { 
     //Reading Parent content 
     //Reading Child 
     Child.Parent = this; 
    } 

    public void WriteXml(System.Xml.XmlWriter writer) 
    { 
     //Writing Parent and Child content 
    } 

    #endregion 

    public string Boo { get; set; } 

    public Child Child { get; set; } 
} 
+0

Si hago eso, entonces la referencia desaparece cuando deserializo el objeto. Objeto proviene de un servicio de WCF – Rachel

+1

¡Gracias! Tu edición aclaró cosas ... Me olvidé por completo de que podía agregar el padre '[OnDeserializing()]', que es lo que terminé haciendo. – Rachel

+0

Como mencioné con el comentario de las respuestas de AHM, OnDeserializingAttribute es redundante en este caso, porque aún funciona bien sin él. Pero aún podría agregar algo de lógica adicional en este método. –

2

Si puede utilizar DataContractSerializer en lugar de la XMLSerializer a continuación, puede utilizar la propiedad IsReference en el atributo DataContract. Habilitar esto mantendrá las referencias, de modo que se recrearán después de la deserialización.

DataContractSerializer también se serializa en XML, pero tiene un control un poco menor sobre el aspecto de la salida, que hace con el XMLSerializer anterior. Puede leer más sobre los serializadores aquí: http://www.danrigsby.com/blog/index.php/2008/03/07/xmlserializer-vs-datacontractserializer-serialization-in-wcf/

+0

Gracias, lo investigaré. ¿Tiene un ejemplo de cómo establecería ese valor en una clase que simplemente como atributo '[Serializable]' en lugar de definirse como '[DataContract]'? – Rachel

+0

Ugh intentando encontrar una manera de hacer esto sin cambiar a los atributos '[DataContract]' .... Mi clase base se usa para todo y si la modifico, tengo que modificar casi todas las clases en mi ObjectLibrary para usar ' [DataContract] 'en lugar de' [Serializable] 'y añada el atributo' [DataMember] 'a todas las propiedades y no tenga ni idea de cómo hacerlo rápidamente ... – Rachel

+0

No puede hacer esto sin cambiar a DataContract. Simplemente no es compatible. Si desea utilizar el atributo Serializable, debe hacer algo como sugiere Sergey y llamar a algún código para corregir las referencias después de la deserialización. – AHM

Cuestiones relacionadas