我目前的任务是将数据发送到具有指定列表的奇怪方式的Web服务。我无法控制架构,我尝试让另一方更改架构失败了。所以我几乎坚持这一点。
它们的模式定义方式是这样的(仅包括相关位):
<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>
我使用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")]
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; }
}
是的,并行数组。现在,根据他们的文档(以及来自通过其他方式生成XML的另一方的轶事数据),正确/预期的xml应该如下所示(例如,包含两个项目的列表 - 为了说明目的添加注释内联): / p>
<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>
但是,我自动生成的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>
由于项目的排序,我的XML无法对模式进行验证。我正在使用带有默认选项的System.Xml.Serialization.XmlSerializer序列化该类。我通常不会为其他Web服务序列化合理的模式。但由于某种原因,我不能为我的生活弄清楚如何做到这一点(如果它甚至可能)。
有什么想法吗?我已经尝试过使用XmlOrderAttribute,但是没有对结果的顺序产生影响。
答案 0 :(得分:1)
这里的基本问题是XmlSerializer
递归地下降对象图并将对象映射到XML元素块,但是你想要交错由某些对象生成的元素,即public string[]
属性。不幸的是,这并不仅仅使用XML serializer attributes实现。
但是,您可以通过引入可以所需格式序列化的代理属性来生成所需的XML。有两种不同的方法可以做到这一点,如以下两个问题所示:
Serializing a list of KeyValuePair to XML显示了如何使用XmlSerializer
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();
}
来生成多个集合中交错的元素列表。
polymorphic list functionality展示了如何使用Xml Sequence deserialization with RestSharp属性生成交错的元素列表。
例如,这是方法#1的实现:
[XmlIgnore]
注意:
原始馆藏标有ValueWrapper<string>[] Values
。
引入了一个代理多态数组属性<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:a="http://pia.com/xml/ns" exclude-result-prefixes="a">
<xsl:output method="xml" version="1.0" encoding="UTF-8" omit-xml-declaration="no" indent="yes"/>
<xsl:template match="a:TransactionReferenceNumber">
<TxId>
<xsl:value-of select="."/>
<xsl:text>
</xsl:text>
</TxId>
</xsl:template>
<xsl:template match="a:ExecutingEntityIDCode">
<ExctgPty>
<xsl:value-of select="."/>
<xsl:text>
</xsl:text>
</ExctgPty>
</xsl:template>
</xsl:stylesheet>
,它可以包含多个类型,每个类型对应一个可能的元素名称。
在创建和返回数组时,三个集合中的值是交错的。在数组设置器中,值按类型拆分并定向到相关集合。
答案 1 :(得分:1)
我将dbc的答案标记为正确,因为他的评论和答案是导致我最终实施的原因。为了完整起见(以及在将来遇到这种情况时提供某种文档),这就是我最终实现此功能的方式。
我利用了xsd.exe将所有类生成为部分类的事实。这使我更容易添加最终被序列化的新列表/集合属性,并且在架构更改时不会丢失更改。例如,要覆盖List
类的RemoteServiceTypePart1
属性:
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 { }
}
}
现在唯一剩下的问题是如何将[XmlIgnore]
属性添加到原始属性,而不必在每次架构更改时手动添加属性。为此,我使用XmlAttributeOverrides
类并将其放在xml序列化逻辑所在的位置:
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
就是这样。现在输出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>