如何在C#中对XML序列化期间的XML元素进行交替?

3

我有一个由数据提供商提供的XSD模式。我无法修改它。我使用XSD.exe命令行工具生成了类。对于所有内容,它都可以正常工作,我可以在C#中创建我的对象,将其序列化为XML并根据XSD验证它。

我遇到了一些小问题。期望的输出是:

    <Physical>
        <Class>P</Class>
        <Capacity>14</Capacity>
        <Class>J</Class>
        <Capacity>64</Capacity>
        <Class>W</Class>
        <Capacity>1</Capacity>
        <Class>Y</Class>
        <Capacity>2</Capacity>
    </Physical>
    <Saleable Protected="true">
        <Class>P</Class>
        <Capacity>14</Capacity>
        <Class>J</Class>
        <Capacity>64</Capacity>
        <Class>W</Class>
        <Capacity>1</Capacity>
        <Class>Y</Class>
        <Capacity>2</Capacity>
    </Saleable>

正如您所看到的,Physical和Sealable的子元素交替出现(即Class,然后是Capacity,然后是Class,然后是Capacity等)。

这是由XSD.exe生成的类代码:

public partial class ClassA
{
    private string[] classField;

    private Integerctype[] capacityField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("Class", DataType = "token")]
    public string[] Class
    {
        get
        {
            return this.classField;
        }
        set
        {
            this.classField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("Capacity", IsNullable = true)]
    public Integerctype[] Capacity
    {
        get
        {
            return this.capacityField;
        }
        set
        {
            this.capacityField = value;
        }
    }
}

序列化后我收到的输出:

<Physical>
    <Class>P</Class>
    <Class>J</Class>
    <Class>W</Class>
    <Class>Y</Class>
    <Capacity>14</Capacity>
    <Capacity>64</Capacity>
    <Capacity>1</Capacity>
    <Capacity>2</Capacity>
</Physical>
<Saleable>
    <Class>P</Class>
    <Class>J</Class>
    <Class>W</Class>
    <Class>Y</Class>
    <Capacity>14</Capacity>
    <Capacity>64</Capacity>
    <Capacity>1</Capacity>
    <Capacity>2</Capacity>
</Saleable>

如您所见,我们失去了类和容量之间的交替......

我尝试使用XmlElementAttribute的Order属性:Class属性被装饰为Order = 1,Capacity属性被装饰为Order = 2,但是没有帮助。例如:

[System.Xml.Serialization.XmlElementAttribute("Class", DataType = "token", Order = 1)]
public string[] Class

验证过程中,无论是否有Order属性,我都会收到以下错误信息:

命名空间为'xxx'的元素'Physical'具有无效的子元素'Class',可能期望的元素列表为'xxx'命名空间中的'Capacity'。

最后,这是XSD的一部分:

<xsd:element name="ClassA" minOccurs="0">
    <xsd:complexType>
        <xsd:all>
            <xsd:element name="Physical" minOccurs="0">
                <xsd:annotation>
                    <xsd:documentation>True, physical class A configuration</xsd:documentation>
                </xsd:annotation>
                <xsd:complexType>
                    <xsd:sequence minOccurs="0" maxOccurs="unbounded">
                        <xsd:element name="Class" type="CabinClass.type" />
                        <xsd:element name="Capacity" type="Integer.ctype" nillable="true" />
                    </xsd:sequence>
                    <xsd:attributeGroup ref="Array.attgroup" />
                </xsd:complexType>
            </xsd:element>
            <xsd:element name="Saleable" minOccurs="0">
                <xsd:annotation>
                    <xsd:documentation>Class A configuration for sales purposes</xsd:documentation>
                </xsd:annotation>
                <xsd:complexType>
                    <xsd:sequence minOccurs="0" maxOccurs="unbounded">
                        <xsd:element name="Class" type="CabinClass.type" />
                        <xsd:element name="Capacity" type="Integer.ctype" nillable="true" />
                    </xsd:sequence>
                    <xsd:attributeGroup ref="Array.attgroup" />
                </xsd:complexType>
            </xsd:element>
        </xsd:all>
        <xsd:attributeGroup ref="Container.attgroup" />
    </xsd:complexType>
</xsd:element>

我的猜测是这与xsd:sequence的存在有关。但是我不想修改XSD,因为它是由数据提供者提供的,我们必须确保生成的XML完全兼容。
你有什么解决问题的想法吗?

在XML中,顺序并不重要,但是由于模式问题,在你的情况下顺序非常重要。确保顺序的最佳方式是使用XDocument或XmlDocument方法编写自定义序列化。您还可以使用XmlWriter。 - jdweng
@jdweng,感谢您的建议。是的,他们期望这些元素按照精确的顺序排列,这确实很奇怪,但不幸的是事实就是如此。对于我们来说,自定义序列化器可能不是最好的选择,因为XSD和生成的XML文件非常庞大。我们正在尝试使用标准序列化,并仅在必要时自定义由XSD.exe生成的类,以简化操作。 - DotNetMatt
如果文件太大,使用标准序列化会导致内存溢出错误。因此,你必须使用XmlReader。没有其他选择。 - jdweng
1个回答

2
简化后的代码可能是这样的:
public class Physical
{
    [XmlElement("Capacity", typeof(int))]
    [XmlElement("Class", typeof(string))]
    public object[] Items { get; set; }
}

这将确保正确的反序列化,并按照数组中的顺序给出元素的序列化。

一个可行的版本可能如下所示:

public class Physical
{
    [EditorBrowsable(EditorBrowsableState.Never)]
    [XmlElement("Capacity", typeof(int))]
    [XmlElement("Class", typeof(string))]
    public object[] Items
    {
        get
        {
            object[] items = new object[Class.Length * 2];
            for (int i = 0; i < items.Length; i += 2)
            {
                items[i] = Class[i / 2];
                items[i + 1] = Capacity[i / 2];
            }
            return items;
        }
        set
        {
            Class = new string[value.Length / 2];
            Capacity = new int[value.Length / 2];
            for (int i = 0; i < value.Length; i += 2)
            {
                Class[i / 2] = (string)value[i];
                Capacity[i / 2] = (int)value[i + 1];
            }
        }
    }

    [XmlIgnore]
    public string[] Class { get; set; }
    [XmlIgnore]
    public int[] Capacity { get; set; }
}

int改为Integerctype,添加DataType参数。

同样地,修改第二个类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接