2013-04-24 66 views
1

簡而言之,我期望從一組對象中創建一個XML模式,如下所示:序列化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 generic list of serializable objects

我調整了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; } 
} 
+1

任何不使用LinqToXml的理由?因爲你的代碼對於簡單的xml似乎太複雜了。 – I4V 2013-04-24 18:57:00

+0

@ I4V,原因是我不知道它存在:-) – DiskJunky 2013-04-25 08:46:29

回答

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> 
+0

RogerN,你的解決方案確實有效,但我希望有一個更優雅的方式。目前的問題是,有40多個「ElementTypeX」對象,並將它們添加到兩個不同的位置,因爲我實施每個對象都非常痛苦。如果不是其他解決方案即將出現,我會將其標記爲已接受 - 它確實有效:-) – DiskJunky 2013-05-01 11:08:22

相關問題