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