.NET XmlSerializer и несколько ссылок на один и тот же объект - PullRequest
8 голосов
/ 08 января 2010

В моем хранилище есть List<Student>, List<Course> и List<Enrolment>, где в Enrollment есть Enrolment.Student и Enrolment.Course, которые ссылаются на одного из студентов или курсов в двух предыдущих списках.

Когда я использую XmlSerializer в моем хранилище, он выводит избыточные данные, поскольку сериализует все свойства каждого учащегося в List<Student>, а затем снова для каждой ссылки на тех же учеников в List<Enrolment>. Я ищу элегантный способ решить эту проблему.

После десериализации я могу исправить ссылки, используя значения идентификаторов в экземплярах дублированных объектов, созданных десериализацией, но это кажется хакерским.

Один из способов исправить избыточный вывод - использовать XmlIgnore Enrolment.Student и Enrolment.Course и создать еще два свойства для сериализации - Enrolment.StudentID и Enrolment.CourseID. Однако во время десериализации ссылки на Enrolment.Student и Enrolment.Course не могут быть установлены (AFAIK), поскольку результаты десериализации List<Student> и List<Course> недоступны.

Еще один метод, о котором я подумал, - это сериализацию внизу в моей иерархии объектов, делая каждый из моих списков отдельно и контролируя порядок десериализации - я, скорее, не делаю этого.

Другим методом может быть XmlIgnore List<Enrolment> и создание вспомогательного класса сериализации регистрации, который инициализирует List<Enrolment> после завершения десериализации. Это похоже на большие усилия.

Как другие люди сериализуют / десериализуют несколько ссылок на один и тот же объект с помощью XmlSerializer?

Ответы [ 5 ]

3 голосов
/ 08 января 2010

О боли сериализации: -> ...

Для этого никогда не было универсального решения, я думаю, именно поэтому MS удалила его из среды Silverlight.

Я никогда не полагаюсь ни на какие автоматические механизмы сериализации .net framework. Для моих собственных моделей и репозиториев я обычно знаю или могу легко программно определить, какие свойства являются простыми скалярными (числа / строки / и т. Д.), А какие - ссылками на другие объекты (а также списками обоих).

Есть в основном 2 сценария:

1: Мы хотим сериализовать / передавать только плоскую информацию об объектах. В этом случае я передаю только соответствующие идентификаторы для свойств, которые ссылаются на другие объекты. Затем получатель может сделать последующие запросы, чтобы получить все другие необходимые ему объекты.

2: Мы хотим передать как можно больше информации, т. Е. Более глубокий вложенный XML с несколькими уровнями, в основном для некоторых функций отчетности, отображающих все напрямую, используя лишь немного CSS в XML. В этом случае на самом деле желательно, чтобы одинаковые объекты разрешались многократно в дерево XML.

Иногда мне нужно немного настроить первый сценарий, чтобы избежать слишком большого количества последующих запросов, но обычно я очень хорошо ладил. То есть Я встроил в нашу базу кода, что мы можем указать, какие дополнительные объекты мы хотим разрешить, когда и / или это где-то настроено.

2 голосов
/ 08 января 2010

Вы можете реализовать интерфейс IXmlSerializable для Enrollment и в методе WriteXml генерировать XML студента и курса, который будет содержать только ключи, например:

<Student Id="5"/>
<Course Id="6"/>

и в методе ReadXml вы можете загрузить ссылки из этого. Вы также должны установить атрибут XmlIgnore для свойства Student и Course.

2 голосов
/ 08 января 2010

Нет решения этой проблемы с использованием XML Serializer. У него нет концепции идентичности, которую он мог бы использовать для устранения дублирования.

Лучшее, что вы можете сделать, - это сериализовать пул объектов отдельно от их ссылок. После этого вы можете восстановить свои списки после десериализации.

Кстати, вы знаете, что XmlSerializer не специфичен для C #?

0 голосов
/ 29 августа 2018

Вы должны / можете использовать отслеживание ссылок с сериализатором данных:

//deserilaize:
using(MemoryStream memStmBack = new MemoryStream()) {
  var serializerForth = new DataContractSerializer(
    typeof(YourType),
    null,
    0x7FFF /*maxItemsInObjectGraph*/ ,
    false /*ignoreExtensionDataObject*/ ,
    true /*preserveObjectReferences*/ ,
    null /*dataContractSurrogate*/ );

  byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
  memStmBack.Write(data, 0, data.Length);
  memStmBack.Position = 0;
  var lsBack = (YourType) serializerForth.ReadObject(memStmBack);

}
//serialize...
using(MemoryStream memStm = new MemoryStream()) {
    var serializer = new DataContractSerializer(
      typeof(YourType),
      knownTypes,
      0x7FFF /*maxItemsInObjectGraph*/ ,
      false /*ignoreExtensionDataObject*/ ,
      true /*preserveObjectReferences*/ ,
      null /*dataContractSurrogate*/ );

    serializer.WriteObject(memStm, yourType);

    memStm.Seek(0, SeekOrigin.Begin);

    using(var streamReader = new StreamReader(memStm)) {
        result = streamReader.ReadToEnd();

Или используйте

[Serializable]
[DataContract(IsReference = true)]
0 голосов
/ 08 января 2010

Как это звучит как решение:

  1. XML Игнорировать каждый вторичный ссылка т.е. поступление. Студент & Зачисление.Курс
  2. создать свойство для каждого вторичного ссылка, которая используется для сериализация / десериализация внешнего ключа вместо этого для ссылки - префикс с XML_FK. например, XML_FK_Student & XML_FK_Course
  3. Создайте метод XML_FinalizeDeserialization, который вызывается после десериализации для загрузки ссылки с использованием этих иностранных ключевые свойства.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...