按特定顺序将并行数组序列化为C#中的XML

按特定顺序将并行数组序列化为C#中的XML,c#,xml,serialization,xsd,xmlserializer,C#,Xml,Serialization,Xsd,Xmlserializer,我目前的任务是将数据发送到一个web服务,该服务有一种非常奇怪的指定列表的方式。我无法控制模式,我试图让另一方更改模式的尝试也失败了。所以我几乎被这件事缠住了 其模式的定义方式如下(仅包含相关位): 是的,并行阵列。现在,根据他们的文档(以及来自通过其他方式生成XML的另一方的轶事数据),正确/预期的XML应该如下所示(例如,一个包含两个项目的列表-为了说明目的而内联添加注释): 一些测试 123131313 0.11 其他喇嘛 331331313 0.02 但是,我自动生成的C#类序列化为

我目前的任务是将数据发送到一个web服务,该服务有一种非常奇怪的指定列表的方式。我无法控制模式,我试图让另一方更改模式的尝试也失败了。所以我几乎被这件事缠住了

其模式的定义方式如下(仅包含相关位):

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


一些测试
123131313
0.11
其他喇嘛
331331313
0.02
但是,我自动生成的C#类序列化为:

<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>

马科斯试验
佩佩喇嘛
123131313
331331313
0.11
0.02
由于项目的顺序,我的XML无法针对架构进行验证。我正在使用System.Xml.Serialization.XmlSerializer和默认选项序列化该类。我通常不会为其他web服务序列化合理的模式。但出于某种原因,我一辈子都不知道该怎么做(如果可能的话)


有什么想法吗?我已经尝试过使用XmlOrderAttribute,但没有改变结果的顺序

这里的基本问题是,
XmlSerializer
递归地降低对象图并将对象映射到XML元素块,但您希望交错由某些对象生成的元素,即
公共字符串[]
属性。不幸的是,这并不是只使用一种开箱即用的方法来实现的

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

  • 演示如何使用
    XmlSerializer
    从多个集合生成元素的交错列表

  • 演示如何使用属性生成元素的交错列表

  • 例如,下面是方法1的一个实现:

    公共部分类零件列表
    {
    [XmlIgnore]
    公共列表名称{get;}=new List();
    [XmlIgnore]
    公共列表数据{get;}=new List();
    [XmlIgnore]
    public List OtherData{get;}=new List();
    [System.Xml.Serialization.xmlementAttribute(“Name”,typeof(Name),Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    [System.Xml.Serialization.xmlementAttribute(“数据”,typeof(数据),Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    [System.Xml.Serialization.xmlementAttribute(“OtherData”,typeof(OtherData),Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    公共价值包装[]价值
    {
    得到
    {
    var list=新列表();
    for(int i=0,count=Math.Max(Name.count,Math.Max(Data.count,OtherData.count));iv.value));
    AddRange(value.OfType().Select(v=>v.value));
    AddRange(value.OfType().Select(v=>v.value));
    }
    }
    }
    公共类名称:ValueWrapper{}
    公共类数据:ValueWrapper{}
    公共类OtherData:ValueWrapper{}
    公共抽象类ValueWrapper:ValueWrapper,其中T:IConvertible
    {
    公共覆盖对象GetValue()=>Value;
    [XmlText]
    公共T值{get;set;}
    }
    公共抽象类ValueWrapper
    {
    公共抽象对象GetValue();
    }
    
    注:

    • 原始集合用
      [XmlIgnore]
      标记

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

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


    示例。

    我将dbc的答案标记为正确,因为他的评论和答案是导致我最终实现的原因。为了完整性起见(并且为了有一些文档以备将来遇到),我最终实现了这个功能

    我利用了xsd.exe将所有类都作为部分类生成的事实。这使我更容易添加新的列表/集合属性,这些属性最终将被序列化,并且不会在模式更改时丢失更改。例如,要覆盖
    RemoteServiceTypePart1
    类的
    List
    属性:

    public partial class RemoteServiceTypePart1
    {
        // Tell the Xml serializer to use "List" instead of "List_Override" 
        // as the element name.  
        [XmlAnyElement("List")]
        public XElement List_Override
        {
            get {
                var result = new List<XElement>(); 
    
                for (int i = 0; i < List.Name.Length; i++)
                {
                    result.Add(new XElement("Name", List.Name[i]));
                    result.Add(new XElement("Data", List.Data[i]));
                    result.Add(new XElement("OtherData", List.OtherData[i]));
                }
    
                return new XElement("List", result.ToArray());
            }
    
            set { }
        }
    }
    
    就这样。现在,输出XML如下所示:

    <RemoteServiceTypePart1 xmlns="">
      <List>
        <Name>Marcos Test</Name>
        <Data>123131313</Data>
        <OtherData>0.11</OtherData>
        <Name>Pepe Lama</Name>
        <Data>331331313</Data>
        <OtherData>0.02</OtherData>
      </List>
    </RemoteServiceTypePart1>
    
    
    马科斯试验
    123131313
    0.11
    佩佩喇嘛
    331331313
    0.02
    
    您的问题与或的问题非常相似。两者的答案都显示了使用
    XmlSerializer
    反序列化成对元素序列的两种不同方式。这些答案足够了吗,或者你需要更多的帮助吗?如果这是一个列表,我会马上做。但是在整个模式中有很多这样的文件(这是一个5000行的模式文件)。我希望有一个更简单的方法来实现这一点。无论如何,如果这是唯一的办法,我建议你把它作为一个答案贴出来
    <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>
    
    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();
    }
    
    public partial class RemoteServiceTypePart1
    {
        // Tell the Xml serializer to use "List" instead of "List_Override" 
        // as the element name.  
        [XmlAnyElement("List")]
        public XElement List_Override
        {
            get {
                var result = new List<XElement>(); 
    
                for (int i = 0; i < List.Name.Length; i++)
                {
                    result.Add(new XElement("Name", List.Name[i]));
                    result.Add(new XElement("Data", List.Data[i]));
                    result.Add(new XElement("OtherData", List.OtherData[i]));
                }
    
                return new XElement("List", result.ToArray());
            }
    
            set { }
        }
    }
    
    var xmlIgnoreAttr = new XmlAttributes { XmlIgnore = true };
    
    var overrides = new XmlAttributeOverrides();
    // Add an override for each class that requires properties to be ignored.
    overrides.Add(typeof(RemoteServiceTypePart1), "List", xmlIgnoreAttr);
    overrides.Add(typeof(RemoteServiceTypePart2), "List", xmlIgnoreAttr);
    overrides.Add(typeof(RemoteServiceTypePart3), "List", xmlIgnoreAttr);
    
    // In a real-world implementation, you will need to cache this object to 
    // avoid a memory leak. Read: https://stackoverflow.com/a/23897411/3744182
    // Thanks to dbc for letting me know in the comments.
    var ser = new XmlSerializer(typeof(RemoteServiceType), overrides);
    // serialize, send xml, do whatever afterwards
    
    <RemoteServiceTypePart1 xmlns="">
      <List>
        <Name>Marcos Test</Name>
        <Data>123131313</Data>
        <OtherData>0.11</OtherData>
        <Name>Pepe Lama</Name>
        <Data>331331313</Data>
        <OtherData>0.02</OtherData>
      </List>
    </RemoteServiceTypePart1>