在 C# 中以特定顺序将并行数组序列化为 XML [英] Serialize parallel arrays to XML in C# in a specific order

查看:37
本文介绍了在 C# 中以特定顺序将并行数组序列化为 XML的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前的任务是将数据发送到具有非常奇怪的指定列表方式的 Web 服务.我无法控制架构,我试图让对方更改架构的尝试失败了.所以我几乎坚持这个.

他们的架构的定义方式是这样的(只包括相关位):

<xs:complexType><xs:序列><xs:element name="列表"><xs:complexType><xs:sequence maxOccurs="4"><xs:element name="Name" type="xs:string"/><xs:element name="Data" type="xs:string"/><xs:element name="OtherData" type="xs:string"/></xs:sequence></xs:complexType></xs:element></xs:sequence></xs:complexType></xs:element>

我使用 xsd.exe 生成一个 C# 类来轻松序列化结构.生成的位如下:

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")][System.SerializableAttribute()][System.Diagnostics.DebuggerStepThroughAttribute()][System.ComponentModel.DesignerCategoryAttribute("code")][System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="the namespace")]公共部分类 PartList {[System.Xml.Serialization.XmlElementAttribute("Name", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]公共字符串[]名称{获取;放;}[System.Xml.Serialization.XmlElementAttribute("Data", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]公共字符串[]数据{获取;放;}[System.Xml.Serialization.XmlElementAttribute("OtherData", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]公共字符串 [] 其他数据 { 获取;放;}}

是的,并行阵列.现在,根据他们的文档(以及通过其他方式生成 XML 的另一方的轶事数据),正确/预期的 xml 应该如下所示(例如,包含两个项目的列表 - 为说明目的添加了内联注释):

<部分 xmlns=""><列表><名称>一些测试</名称><!-- 第一个项目--><数据>123131313</数据><!-- 第一个项目--><其他数据>0.11</其他数据><!-- 第一个项目--><姓名>其他喇嘛</姓名><!-- 第二项--><数据>331331313</数据><!-- 第二项--><其他数据>0.02</其他数据><!-- 第二项--></列表></部分>

但是,我自动生成的 C# 类序列化为:

<部分 xmlns=""><列表><姓名>马科斯测试</姓名><!-- 第一个项目--><姓名>佩佩喇嘛</姓名><!-- 第二项--><数据>123131313</数据><!-- 第一个项目--><数据>331331313</数据><!-- 第二项--><其他数据>0.11</其他数据><!-- 第一个项目--><其他数据>0.02</其他数据><!-- 第二项--></列表></部分>

由于项目的顺序,我的 XML 无法针对架构进行验证.我正在使用带有默认选项的 System.Xml.Serialization.XmlSerializer 序列化类.我通常在为其他 Web 服务序列化合理的模式时没有任何问题.但出于某种原因,我终其一生都无法弄清楚如何做到这一点(如果可能的话).

有什么想法吗?我已经尝试使用 XmlOrderAttribute,但对结果的顺序没有影响.

解决方案

这里的基本问题是 XmlSerializer 递归下降对象图并将对象映射到 XML 元素块,但是你想要交错某些对象生成的元素,即public string[]属性.不幸的是,这不是仅使用 XML 序列化程序属性.

但是,您可以通过引入可以以所需格式序列化的代理属性来生成所需的 XML.有两种不同的方法可以做到这一点,如以下两个问题所示:

  1. 将 KeyValuePair 列表序列化为 XML 展示了如何使用 <XmlSerializer 的 href="https://stackoverflow.com/questions/4260338/xmlserializer-polymorphism">多态列表功能,用于从多个集合中生成交错的元素列表.p>

  2. 使用 RestSharp 进行 Xml 序列反序列化 展示了如何使用 [XmlAnyElement] 属性生成交错的元素列表.

例如,这是方法 #1 的实现:

公共分部类 PartList{[XmlIgnore]公共列表<字符串>名称 { 得到;} = new List();[XmlIgnore]公共列表<字符串>数据{得到;} = new List();[XmlIgnore]公共列表<字符串>其他数据{得到;} = new List();[System.Xml.Serialization.XmlElementAttribute("Name", typeof(Name), Form = System.Xml.Schema.XmlSchemaForm.Unqualified)][System.Xml.Serialization.XmlElementAttribute("Data", typeof(Data), Form = System.Xml.Schema.XmlSchemaForm.Unqualified)][System.Xml.Serialization.XmlElementAttribute("OtherData", typeof(OtherData), Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]公共 ValueWrapper[] 值{得到{var list = new List>();for (int i = 0, count = Math.Max(Name.Count, Math.Max(Data.Count, OtherData.Count)); i ().Select(v => v.Value));Data.AddRange(value.OfType().Select(v => v.Value));OtherData.AddRange(value.OfType().Select(v => v.Value));}}}公共类名称:ValueWrapper{ }公共类数据:ValueWrapper{ }公共类 OtherData : ValueWrapper{ }公共抽象类 ValueWrapper<T>: ValueWrapper 其中 T : IConvertible{公共覆盖对象 GetValue() =>价值;[XML文本]公共 T 值 { 得到;放;}}公共抽象类 ValueWrapper{公共抽象对象 GetValue();}

注意事项:

  • 原始集合用 [XmlIgnore] 标记.

  • 引入了一个代理多态数组属性 ValueWrapper[] Values,它可以包含多种类型,每个可能的元素名称一个.

  • 在创建和返回数组时,三个集合中的值是交错的.在数组 setter 中,值按类型拆分并定向到相关集合.

示例 fiddle.

I'm currently tasked with sending data to a web service that has a very odd way of specifying lists. I am not in control of the schema, and my attempts to make the other party change the schema have failed. So I'm pretty much stuck with this.

The way their schema is defined is this (only the relevant bit is included):

<xs:element name="Part">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="List">
        <xs:complexType>
          <xs:sequence maxOccurs="4">
            <xs:element name="Name" type="xs:string" />
            <xs:element name="Data" type="xs:string" />
            <xs:element name="OtherData" type="xs:string" />
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
</xs:element>

I used xsd.exe to generate a C# class to serialize the structure easily. The generated bit is as follows:

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="the namespace")]
public partial class PartList {
    [System.Xml.Serialization.XmlElementAttribute("Name", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public string[] Name { get; set; }

    [System.Xml.Serialization.XmlElementAttribute("Data", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public string[] Data { get; set; }

    [System.Xml.Serialization.XmlElementAttribute("OtherData", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public string[] OtherData  { get; set; }
}

Yes, parallel arrays. Now, according to their documents (and anecdotal data from another party that generates the XML through other means), the correct/expected xml should look like this (for example, a list with two items - comments inline added for illustration purposes):

<Part xmlns="">
    <List>
        <Name>Some Test</Name> <!-- first item -->
        <Data>123131313</Data> <!-- first item -->        
        <OtherData>0.11</OtherData> <!-- first item -->        
        <Name>Other Lama</Name> <!-- second item -->
        <Data>331331313</Data> <!-- second item -->
        <OtherData>0.02</OtherData> <!-- second item -->
    </List>
</Part>

However, my autogenerated C# class serializes to:

<Part xmlns="">
    <List>
        <Name>Marcos Test</Name> <!-- first item -->
        <Name>Pepe Lama</Name> <!-- second item -->
        <Data>123131313</Data> <!-- first item -->
        <Data>331331313</Data> <!-- second item -->
        <OtherData>0.11</OtherData> <!-- first item -->
        <OtherData>0.02</OtherData> <!-- second item -->
    </List>
</Part>

My XML fails validation against the schema because of the ordering of the items. I'm serializing the class using System.Xml.Serialization.XmlSerializer with the default options. I usually don't have any trouble serializing reasonable schemas for other web services. But for some reason I just can't for the life of me figure out how to do this (if it's even possible).

Any ideas? I already tried using the XmlOrderAttribute, but didn't make a difference in the order of the result.

解决方案

The basic problem here is that XmlSerializer recursively descends the object graph and maps objects to blocks of XML element(s), but you want to interleave the elements generated by certain objects, namely the public string[] properties. Unfortunately, this isn't implemented out of the box using only XML serializer attributes.

However, you can generate the XML you need by introducing a surrogate property that can be serialized in the required format. There are two different ways to do this, as shown in the following two questions:

  1. Serializing a list of KeyValuePair to XML shows how to use the polymorphic list functionality of XmlSerializer to generate an interleaved list of elements from multiple collections.

  2. Xml Sequence deserialization with RestSharp shows how to use an [XmlAnyElement] property to generate an interleaved list of elements.

For instance, here is an implementation of approach #1:

public partial class PartList
{
    [XmlIgnore]
    public List<string> Name { get; } = new List<string>();

    [XmlIgnore]
    public List<string> Data { get; } = new List<string>();

    [XmlIgnore]
    public List<string> OtherData { get; } = new List<string>();

    [System.Xml.Serialization.XmlElementAttribute("Name", typeof(Name), Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    [System.Xml.Serialization.XmlElementAttribute("Data", typeof(Data), Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    [System.Xml.Serialization.XmlElementAttribute("OtherData", typeof(OtherData), Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public ValueWrapper<string>[] Values
    {
        get
        {
            var list = new List<ValueWrapper<string>>();
            for (int i = 0, count = Math.Max(Name.Count, Math.Max(Data.Count, OtherData.Count)); i < count; i++)
            {
                if (i < Name.Count)
                    list.Add(new Name { Value = Name[i] });
                if (i < Data.Count)
                    list.Add(new Data { Value = Data[i] });
                if (i < OtherData.Count)
                    list.Add(new OtherData { Value = OtherData[i] });
            }
            return list.ToArray();
        }
        set
        {
            if (value == null)
                return;
            Name.AddRange(value.OfType<Name>().Select(v => v.Value));
            Data.AddRange(value.OfType<Data>().Select(v => v.Value));
            OtherData.AddRange(value.OfType<OtherData>().Select(v => v.Value));
        }
    }
}

public class Name : ValueWrapper<string> { }

public class Data : ValueWrapper<string> { }

public class OtherData : ValueWrapper<string> { }

public abstract class ValueWrapper<T> : ValueWrapper where T : IConvertible
{
    public override object GetValue() => Value; 

    [XmlText]
    public T Value { get; set; }
}

public abstract class ValueWrapper
{
    public abstract object GetValue();
}

Notes:

  • The original collections are marked with [XmlIgnore].

  • A surrogate polymorphic array property ValueWrapper<string>[] Values is introduced that can contain multiple types, one for each possible element name.

  • In creating and returning the array the values from the three collections are interleaved. In the array setter, the values are split by type and directed to the relevant collections.

Sample fiddle.

这篇关于在 C# 中以特定顺序将并行数组序列化为 XML的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆