Подтвердить что ты не робот

Циркулярная ссылка при использовании XML-сериализации?

Я получаю следующее исключение при попытке сериализации объекта с помощью XMLSerialization.

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

Я знаю, что круговая ссылка состоит в том, что ObjectA может иметь childObject объекта ObjectB и ObjectB parentObject ObjectA, однако я хотел бы сохранить эту ссылку, если это возможно. Есть ли способ заставить этот объект сериализоваться с XML-сериализацией без потери каких-либо данных во время процесса сериализации? Я не очень знакомы с сериализацией, поэтому я надеюсь, что у меня будет какой-то атрибут, который я мог бы установить.

4b9b3361

Ответ 1

В зависимости от типа сериализатора существует несколько опций.

Если вы можете использовать DataContractSerializer или BinaryFormatter тогда вы можете использовать OnSerializedAttribute и установить для родительского объекта свойство Parent:

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

}

Если вы хотите использовать XmlSerializer, вы должны реализовать IXmlSerializable, используйте XmlIgnoreAttribute и реализовали более или менее ту же логику в методе ReadXml. Но в этом случае вы также должны реализовать всю логику сериализации 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; }
}

Ответ 2

Если вы можете использовать DataContractSerializer вместо XMLSerializer, вы можете использовать свойство IsReference в атрибуте DataContract. Включение этого будет содержать ссылки, так что они будут воссозданы после десериализации.

DataContractSerializer также сериализуется в XML, но у вас есть несколько меньше контроля над тем, как выглядит вывод, что вы делаете со старым XMLSerializer. Вы можете узнать больше о сериализаторах здесь: http://www.danrigsby.com/blog/index.php/2008/03/07/xmlserializer-vs-datacontractserializer-serialization-in-wcf/