XML序列化导致重复的节点

时间:2011-12-27 21:42:20

标签: c# .net xml-serialization

我有一个对象结构,我正在尝试序列化为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>

根对象包含一组练习。 Exercise是我的基类,而子类由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 个答案:

答案 0 :(得分:1)

我认为你的问题出在这一行:

[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,则可以使用this question的答案。

答案 1 :(得分:0)

您应该可以使用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>