1

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

1 に答える 1

3

カスタム シリアライザーを使用しなくても、標準の XML シリアライゼーション属性だけで、やりたいことがすべてできると思います。以下の例を参照してください。

public class Container
{
    [XmlElement("ElementType1", typeof(ElementType1))]
    [XmlElement("ElementType2", typeof(ElementType2))]
    public ElementBase[] Elements { get; set; }
}

[XmlInclude(typeof(ElementType1)),XmlInclude(typeof(ElementType2))]
public abstract class ElementBase
{
    public string Name { get; set; }
}

public class ElementType1 : ElementBase
{
    public int ID1 { get; set; }
}

public class ElementType2 : ElementBase
{
    public int ID2 { get; set; }
}

デフォルトのシリアライザを使用していくつかのテスト データをシリアライズしています...

var container = new Container
{
    Elements = new ElementBase[] {
        new ElementType1 { Name = "first object", ID1 = 999 },
        new ElementType2 { Name = "second object", ID2 = 31337 }
    }
};
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(Container));
serializer.Serialize(stream, container);

...そして、必要な形式のように見える次の出力が得られます。

<?xml version="1.0" encoding="utf-8"?>
<Container xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <ElementType1>
        <Name>first object</Name>
        <ID1>999</ID1>
    </ElementType1>
    <ElementType2>
        <Name>second object</Name>
        <ID2>31337</ID2>
    </ElementType2>
</Container>
于 2013-04-24T17:31:31.130 に答える