簡単に言うと、次のような一連のオブジェクトから 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; }
}