Подскажите, пожалуйста, как правильно сериализовывать/десериализовывать объекты в XML, имеющие взаимные ссылки.
Например, есть классы Car
и Garage
. Garage
содержит список объектов типа Car
, при этом каждый объект Car
имеет ссылку на объект типа Garage
.
Вот класс Garage
:
using System;
using System.Collections.Generic;
namespace CarsApp
{
[Serializable]
public class Garage
{
public List<Car> cars = new List<Car>();
public Garage() { }
public void AddCar(Car car)
{
cars.Add(car);
car.Garage = this;
}
}
}
Вот класс Car
:
using System;
namespace CarsApp
{
[Serializable]
public class Car
{
public string Model { get; set; }
public Garage Garage { get; set; }
public Car() { }
public Car(string model)
{
Model = model;
}
}
}
Вот код сериализации объекта типа Garage
:
using System.IO;
using System.Xml.Serialization;
namespace CarsApp
{
class Program
{
static void Main(string[] args)
{
Car fiat = new Car("Fiat");
Car reno = new Car("Reno");
Garage garage = new Garage();
garage.AddCar(fiat);
garage.AddCar(reno);
XmlSerializer xmlFormat = new XmlSerializer(typeof(Garage));
using(Stream fStream = new FileStream("garage.xml",
FileMode.Create, FileAccess.Write, FileShare.None))
{
xmlFormat.Serialize(fStream, garage);
}
}
}
}
В результате выполнения получаю ошибку
System.InvalidOperationException: 'Ошибка при создании документа XML.'
InnerException
System.InvalidOperationException: При сериализации объекта типа CarsApp.Garage обнаружена циклическая ссылка.
В чём ошибка и как исправить?
Для этой цели можно использовать DataContractSerializer с настройкой DataContractSerializerSettings.
Добавьте к проекту сборку System.Runtime.Serialization.dll и откройте пространство имен System.Runtime.Serialization.
var dcss = new DataContractSerializerSettings { PreserveObjectReferences = true };
var dcs = new DataContractSerializer(typeof(Garage), dcss);
using (Stream fStream = new FileStream("test.xml",
FileMode.Create, FileAccess.Write, FileShare.None))
{
dcs.WriteObject(fStream, garage);
}
PreserveObjectReferences = true
как раз будет сохранять ссылки на объекты. Циклические ссылки разруливаются корректно.
При чтении можно создавать сериализатор без указания настроек.
PS: аттрибут Serializable
не нужен для xml-сериализаторов. Его использует BinaryFormatter
. Кстати, этот форматтер тоже корректно обрабатывает циклические ссылки - можно использовать его.
PPS: при наличии атрибута Serializable
у класса Car
генерируется xml следующего вида:
<Car z:Id="3">
<_x003C_Garage_x003E_k__BackingField z:Ref="1" i:nil="true"/>
<_x003C_Model_x003E_k__BackingField z:Id="4">Fiat</_x003C_Model_x003E_k__BackingField>
</Car>
Без этого атрибута xml выглядит намного приятней:
<Car z:Id="3">
<Garage z:Ref="1" i:nil="true"/>
<Model z:Id="4">Fiat</Model>
</Car>
То есть DataContractSerializer
все-таки как-то учитывает этот атрибут.
Если использовать бинарную сериализацию вместо сериализации в XML то можно хранить любые данные лобой сложности вложенности
К классу который будем сериализовать добавляем
[Serializable]
public class SomeItem
{}
Враппер на сериализацию:
public static class Serializator
{
private static BinaryFormatter _bin = new BinaryFormatter();
public static void Serialize(string pathOrFileName, object objToSerialise)
{
using (Stream stream = File.Open(pathOrFileName, FileMode.Create))
{
try
{
_bin.Serialize(stream, objToSerialise);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to serialize. Reason: " + e.Message);
throw;
}
}
}
public static T Deserialize<T>(string pathOrFileName)
{
T items;
using (Stream stream = File.Open(pathOrFileName, FileMode.Open))
{
try
{
items = (T) _bin.Deserialize(stream);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
throw;
}
}
return items;
}
}
ну и, собственно, сам пример использования:
List<SomeItem> itemsCollected;//list with some data
Serializator.Serialize("data.dat", itemsCollected);
var a = Serializator.Deserialize<List<SomeItem>>("data.dat");
В случае ошибки будет выдавать в консоль сообщение о исключении сериализации.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Есть две таблицы comments и usersОни связаны между собой
На сайте под управлением Wordpress есть форма сделана c помощью плагина Contact Form 7Как добавить во входящий имейл из формы еще функцию get_page_link() Мне...
Учу язык С++ по книги РЛафоре "Объектно-ориентированное программирование на С++"