XML序列化导致重复节点

本文关键字:节点 序列化 XML | 更新日期: 2023-09-27 18:18:57

我有一个对象结构,我试图序列化到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的不同派生类型的对象,因为它允许我使用反射来匹配大约24种潜在派生类型中的任何一种,这些类型在应用程序第一次读取时顺序和数量未知。

下面是根元素的集合,使用自定义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()方法中,类型变量被正确地设置为派生类型,那么为什么要为基类型和派生类型创建节点级别呢?我如何防止它这样做?

XML序列化导致重复节点

我想你的问题出在这一行:

[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,您可以使用此问题的答案

你应该能够使用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>