简而言之,我希望从一组对象创建 XML 模式,如下所示;
<?xml version="1.0" encoding="utf-16"?>
<QBXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<QBXMLMsgsRq>
<InvoiceQueryRq>
<TxnID>1</TxnID>
</InvoiceQueryRq>
<InvoiceAddRq>
<TxnID>2</TxnID>
</InvoiceAddRq>
</QBXMLMsgsRq>
</QBXML>
但我得到的是;
<?xml version="1.0" encoding="utf-16"?>
<QBXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<QBXMLMsgsRq>
<Requests>
<AbstractXmlSerializerOfQBBaseMessageRequest>
<InvoiceQueryRq>
<TxnID>1</TxnID>
</InvoiceQueryRq>
<InvoiceAddRq>
<TxnID>2</TxnID>
</InvoiceAddRq>
</AbstractXmlSerializerOfQBBaseMessageRequest>
</Requests>
</QBXMLMsgsRq>
</QBXML>
QBXMLMsgsRq 实际上是一个集合,abstract class
因为在不同类型的集合中可能有许多请求(此处为 InvoiceQueryRq,但也可能有 InvoiceAddRq、InvoiceDeleteRq 等)。默认情况下,XML 序列化程序不允许这样做,但经过一番研究后,我发现了这个链接;XML Serialize 可序列化对象的通用列表
我调整了AbstractXmlSerializer
那里
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
public class AbstractXmlSerializer<AbstractType> : IXmlSerializable
{
public static implicit operator AbstractType(AbstractXmlSerializer<AbstractType> o)
{
return o.Data;
}
public static implicit operator AbstractXmlSerializer<AbstractType>(AbstractType o)
{
return o == null ? null : new AbstractXmlSerializer<AbstractType>(o);
}
private AbstractType _data;
public AbstractType Data
{
get { return _data; }
set { _data = value; }
}
/// <summary>
/// **DO NOT USE** This is only added to enable XML Serialization.
/// </summary>
/// <remarks>DO NOT USE THIS CONSTRUCTOR</remarks>
public AbstractXmlSerializer()
{
// Default Ctor (Required for Xml Serialization - DO NOT USE)
}
public AbstractXmlSerializer(AbstractType data)
{
_data = data;
}
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null; // this is fine as schema is unknown.
}
public void ReadXml(System.Xml.XmlReader reader)
{
// Cast the Data back from the Abstract Type.
string typeAttrib = reader.LocalName; // reader.GetAttribute("type");
// Ensure the Type was Specified
if (typeAttrib == null)
throw new ArgumentNullException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
"' because no 'type' attribute was specified in the XML.");
Type type = Type.GetType(typeAttrib);
// Check the Type is Found.
if (type == null)
throw new InvalidCastException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
"' because the type specified in the XML was not found.");
// Check the Type is a Subclass of the AbstractType.
if (!type.IsSubclassOf(typeof(AbstractType)))
throw new InvalidCastException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
"' because the Type specified in the XML differs ('" + type.Name + "').");
// Read the Data, Deserializing based on the (now known) concrete type.
reader.ReadStartElement();
this.Data = (AbstractType)new
XmlSerializer(type).Deserialize(reader);
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
// Write the Type Name to the XML Element as an Attrib and Serialize
Type type = _data.GetType();
// BugFix: Assembly must be FQN since Types can/are external to current.
new XmlSerializer(type).Serialize(writer, _data);
}
#endregion
}
为了方便和测试任何帮助的人,我正在处理的对象是;
[Serializable]
public class QBXML
{
[XmlElement("QBXMLMsgsRq")]
public QBXMLMsgsRq MessageRequests { get; set; }
}
[Serializable]
public class QBXMLMsgsRq
{
public QBXMLMsgsRq()
: base()
{
Requests = new List<QBBaseMessageRequest>();
}
[XmlArray(""), XmlArrayItem("", Type = typeof(AbstractXmlSerializer<QBBaseMessageRequest>))]
public List<QBBaseMessageRequest> Requests { get; set; }
}
public abstract class QBBaseMessageRequest
{
[DefaultValue(""), XmlAttribute("requestID")]
public string RequestID { get; set; }
}
[Serializable]
public class InvoiceQueryRq : QBBaseMessageRequest
{
[DefaultValue(0), XmlElement("TxnID")]
public int TransactionID { get; set; }
}
[Serializable]
public class InvoiceAddRq : QBBaseMessageRequest
{
[DefaultValue(0), XmlElement("TxnID")]
public int TransactionID { get; set; }
}