XML-сериализация приводит к дублированию узлов - PullRequest
5 голосов
/ 28 декабря 2011

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

Вот как выглядит неисправный вывод xml:

<Keyboarding xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Exercises>
    <Exercise>
      <Exercise Id="3" ActivityNumber="5" SubActivityNumber="b" Type="Standard">
        <Title>Test Title</Title>
        <Instructions>Downloaded Update Instructions</Instructions>
      </Exercise>
    </Exercise>
  </Exercises>
</Keyboarding>

Вот чтоэто должно выглядеть (и похоже на первоначальную десериализацию):

<Keyboarding xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Exercises>
      <Exercise Id="3" ActivityNumber="5" SubActivityNumber="b" Type="Standard">
        <Title>Test Title</Title>
        <Instructions>Downloaded Update Instructions</Instructions>
      </Exercise>
  </Exercises>
</Keyboarding>

Корневой объект содержит набор упражнений.Упражнения - это мой базовый класс, а подклассы определяются атрибутом Type.Я использую собственный сериализатор xml для создания объектов различных производных типов из Exercise, потому что он позволяет мне использовать отражение, чтобы соответствовать любому из 2 дюжин или около того потенциальных производных типов, которые находятся в неизвестном порядке и количестве при первом чтении впо моему заявлению.

Вот коллекция корневого элемента с использованием пользовательского сериализатора xml:

[XmlArray("Exercises")]
[XmlArrayItem("Exercise", Type = typeof (ExerciseXmlSerializer<Exercise>))]
public Collection<Exercise> UnprocessedExercises { get; set; }

Мой базовый класс упражнений объявлен следующим образом:

[Serializable]
[XmlType(AnonymousType = true)]
public class Exercise

И мой производный классобъявлено следующим образом:

[Serializable]
[XmlType(AnonymousType = true)]
[XmlRoot(ElementName = "Exercise", IsNullable = false)]
public class StandardExercise : Exercise

Вот часть записи моего пользовательского сериализатора xml:

public class ExerciseXmlSerializer<T> : IXmlSerializable where T : class
{
    private T _data;
    ...
    public void WriteXml(XmlWriter writer)
    {
        Type type = _data.GetType();

        new XmlSerializer(type).Serialize(writer, _data);
    }
    ...
}

В методе WriteXml () переменная типа правильно установлена ​​в производный тип,так почему он создает уровень узла как для базового типа, так и для производного типа?Как мне предотвратить это?

Ответы [ 2 ]

1 голос
/ 28 декабря 2011

Я думаю, что ваша проблема с этой строкой:

[XmlArrayItem("Exercise", Type = typeof (ExerciseXmlSerializer<Exercise>))]

Не думаю, что вы хотите указать, что тип элементов является типом сериализатора.Если вы хотите реализовать IXmlSerializable, он должен быть в сериализуемом классе.

Вот что мне нужно для работы:

public class Keyboarding
{
    [XmlArray("Exercises")]
    [XmlArrayItem("Exercise")]
    public Collection<Exercise> UnprocessedExercises { get; set; }

    public Keyboarding()
    {
        UnprocessedExercises = new Collection<Exercise>();
        UnprocessedExercises.Add(new StandardExercise());
    }
}

[XmlInclude(typeof(StandardExercise))]
public class Exercise
{
}
[Serializable]
public class StandardExercise : Exercise
{
}

Это производит вывод, подобный следующему:

<?xml version=\"1.0\"?>
<Keyboarding xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
  <Exercises>
    <Exercise xsi:type=\"StandardExercise\" />
  </Exercises>
</Keyboarding>

Если вы не хотите, чтобы xsi:type был включен в вывод, вы можете использовать ответ на этот вопрос .

0 голосов
/ 28 декабря 2011

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

List<Type> extraTypes = new List<Type>();
XmlAttributes attrs = new XmlAttributes();

extraTypes.Add(typeof(Exercise));
extraTypes.Add(typeof(StandardExercise));

XmlElementAttribute attr = new XmlElementAttribute
{
    ElementName = "Exercise",
    Type = typeof(Exercise)
};
attrs.XmlElements.Add(attr);

attr = new XmlElementAttribute
{
    ElementName = "StandardExercise",
    Type = typeof(StandardExercise)
};
attrs.XmlElements.Add(attr);

XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Keyboarding), "Keboarding", attrs);

return new XmlSerializer(typeof(Keyboarding), overrides, extraTypes.ToArray(),
    new XmlRootAttribute("Keyboarding"), string.Empty);

Это должно позволить вам создать дерево XML, которое выглядит следующим образом. Это не совсем то, что вы искали, но так же наглядно.

<Keyboarding xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Exercises>
        <StandardExercise Id="3" ActivityNumber="5" SubActivityNumber="b">
            <Title>Test Title</Title>
            <Instructions>Downloaded Update Instructions</Instructions>
        </StandardExercise >
    </Exercises>
</Keyboarding>
...