2012-12-23 88 views
9

我有下面的代碼:泛型列表XML序列化

BaseContent.cs

public class BaseContent 
{ 
    // Some auto properties 
} 

News.cs

public class News : BaseContent 
{ 
    // Some more auto properties 
} 

Events.cs

public class Event : BaseContent 
{ 
    // Some more auto properites 
} 

GenericResponse.cs

public class GenericResponse<T> 
{ 
    [XmlArray("Content")] 
    [XmlArrayItem("NewsObject", typeof(News)] 
    [XmlArrayItem("EventObject", typeof(Event)] 
    public List<T> ContentItems { get; set; } 
} 

NewsResponse.cs

public class NewsResponse : GenericResponse<News> {} 

EventResponse.cs

public class EventResponse : GenericResponse<Event> {} 

正如你所看到的,我有一個基類BaseContent和從它派生的兩個類。接下來我有一個通用的響應類,因爲xml文件的結構總是相同的,但它們在一些屬性上有所不同。

我想我可以用[XmlArrayItem]指定哪個名稱用於特定的類。但現在我得到的錯誤:

System.InvalidOperationException: Unable to generate a temporary class (result=1). error CS0012: The type 'System.Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

我不能添加此引用,因爲我正在處理Windows 8應用程序。

如果我註釋掉其中一個[XmlArrayItem]它運行良好。

有人有想法解決這個問題嗎?

更新使用DataContractSerializer的,因爲我必須使用XMLATTRIBUTES

回答

11

編輯:隨意下載demo project

您沒有爲對象的所有屬性,所以請允許我補充一些 - 只是作爲一個例子:

public class BaseContent 
{ 
    [XmlAttribute("Name")] 
    public string Name { get; set; } 
} 

[XmlType(TypeName = "EventObject")] 
public class Event : BaseContent 
{ 
    [XmlAttribute("EventId")] 
    public int EventId { get; set; } 
} 

[XmlType(TypeName = "NewsObject")] 
public class News : BaseContent 
{ 
    [XmlAttribute("NewsId")] 
    public int NewsId { get; set; } 
} 

GenericResponse.cs可以這樣來定義 - 無需指定將typeof的數組項:

public class GenericResponse<T> 
{ 
    [XmlArray("Content")] 
    public List<T> ContentItems { get; set; } 

    public GenericResponse() 
    { 
     this.ContentItems = new List<T>(); 
    } 
} 

然後你會有反應類:

public class EventResponse : GenericResponse<Event> 
{ 
} 

public class NewsResponse : GenericResponse<News> 
{ 
} 

例1:序列化的顯示eventResponse對象

var response = new EventResponse 
{ 
    ContentItems = new List<Event> 
    { 
     new Event { 
      EventId = 1, 
      Name = "Event 1" 
     }, 

     new Event { 
      EventId = 2, 
      Name = "Event 2" 
     } 
    } 
}; 

string xml = XmlSerializer<EventResponse>.Serialize(response); 

輸出XML:

<?xml version="1.0" encoding="utf-8"?> 
<EventResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Content> 
     <EventObject Name="Event 1" EventId="1" /> 
     <EventObject Name="Event 2" EventId="2" /> 
    </Content> 
</EventResponse> 

如果你嘗試與NewsResponse相同,它將工作fi東北。順便說一句我正在使用我的generic XmlSerializer,點擊鏈接瞭解更多關於它。

XmlSerializer.cs:

/// <summary> 
/// XML serializer helper class. Serializes and deserializes objects from/to XML 
/// </summary> 
/// <typeparam name="T">The type of the object to serialize/deserialize. 
/// Must have a parameterless constructor and implement <see cref="Serializable"/></typeparam> 
public class XmlSerializer<T> where T: class, new() 
{ 
    /// <summary> 
    /// Deserializes a XML string into an object 
    /// Default encoding: <c>UTF8</c> 
    /// </summary> 
    /// <param name="xml">The XML string to deserialize</param> 
    /// <returns>An object of type <c>T</c></returns> 
    public static T Deserialize(string xml) 
    { 
     return Deserialize(xml, Encoding.UTF8, null); 
    } 

    /// <summary> 
    /// Deserializes a XML string into an object 
    /// Default encoding: <c>UTF8</c> 
    /// </summary> 
    /// <param name="xml">The XML string to deserialize</param> 
    /// <param name="encoding">The encoding</param> 
    /// <returns>An object of type <c>T</c></returns> 
    public static T Deserialize(string xml, Encoding encoding) 
    { 
     return Deserialize(xml, encoding, null); 
    } 

    /// <summary> 
    /// Deserializes a XML string into an object 
    /// </summary> 
    /// <param name="xml">The XML string to deserialize</param> 
    /// <param name="settings">XML serialization settings. <see cref="System.Xml.XmlReaderSettings"/></param> 
    /// <returns>An object of type <c>T</c></returns> 
    public static T Deserialize(string xml, XmlReaderSettings settings) 
    { 
     return Deserialize(xml, Encoding.UTF8, settings); 
    } 

    /// <summary> 
    /// Deserializes a XML string into an object 
    /// </summary> 
    /// <param name="xml">The XML string to deserialize</param> 
    /// <param name="encoding">The encoding</param> 
    /// <param name="settings">XML serialization settings. <see cref="System.Xml.XmlReaderSettings"/></param> 
    /// <returns>An object of type <c>T</c></returns> 
    public static T Deserialize(string xml, Encoding encoding, XmlReaderSettings settings) 
    { 
     if (string.IsNullOrEmpty(xml)) 
      throw new ArgumentException("XML cannot be null or empty", "xml"); 

     XmlSerializer xmlSerializer = new XmlSerializer(typeof(T)); 

     using (MemoryStream memoryStream = new MemoryStream(encoding.GetBytes(xml))) 
     { 
      using (XmlReader xmlReader = XmlReader.Create(memoryStream, settings)) 
      { 
       return (T) xmlSerializer.Deserialize(xmlReader); 
      } 
     } 
    } 

    /// <summary> 
    /// Deserializes a XML file. 
    /// </summary> 
    /// <param name="filename">The filename of the XML file to deserialize</param> 
    /// <returns>An object of type <c>T</c></returns> 
    public static T DeserializeFromFile(string filename) 
    { 
     return DeserializeFromFile(filename, new XmlReaderSettings()); 
    } 

    /// <summary> 
    /// Deserializes a XML file. 
    /// </summary> 
    /// <param name="filename">The filename of the XML file to deserialize</param> 
    /// <param name="settings">XML serialization settings. <see cref="System.Xml.XmlReaderSettings"/></param> 
    /// <returns>An object of type <c>T</c></returns> 
    public static T DeserializeFromFile(string filename, XmlReaderSettings settings) 
    { 
     if (string.IsNullOrEmpty(filename)) 
      throw new ArgumentException("filename", "XML filename cannot be null or empty"); 

     if (! File.Exists(filename)) 
      throw new FileNotFoundException("Cannot find XML file to deserialize", filename); 

     // Create the stream writer with the specified encoding 
     using (XmlReader reader = XmlReader.Create(filename, settings)) 
     { 
      System.Xml.Serialization.XmlSerializer xmlSerializer = new System.Xml.Serialization.XmlSerializer(typeof(T)); 
      return (T) xmlSerializer.Deserialize(reader); 
     } 
    } 

    /// <summary> 
    /// Serialize an object 
    /// </summary> 
    /// <param name="source">The object to serialize</param> 
    /// <returns>A XML string that represents the object to be serialized</returns> 
    public static string Serialize(T source) 
    { 
     // indented XML by default 
     return Serialize(source, null, GetIndentedSettings()); 
    } 

    /// <summary> 
    /// Serialize an object 
    /// </summary> 
    /// <param name="source">The object to serialize</param> 
    /// <param name="namespaces">Namespaces to include in serialization</param> 
    /// <returns>A XML string that represents the object to be serialized</returns> 
    public static string Serialize(T source, XmlSerializerNamespaces namespaces) 
    { 
     // indented XML by default 
     return Serialize(source, namespaces, GetIndentedSettings()); 
    } 

    /// <summary> 
    /// Serialize an object 
    /// </summary> 
    /// <param name="source">The object to serialize</param> 
    /// <param name="settings">XML serialization settings. <see cref="System.Xml.XmlWriterSettings"/></param> 
    /// <returns>A XML string that represents the object to be serialized</returns> 
    public static string Serialize(T source, XmlWriterSettings settings) 
    { 
     return Serialize(source, null, settings); 
    } 

    /// <summary> 
    /// Serialize an object 
    /// </summary> 
    /// <param name="source">The object to serialize</param> 
    /// <param name="namespaces">Namespaces to include in serialization</param> 
    /// <param name="settings">XML serialization settings. <see cref="System.Xml.XmlWriterSettings"/></param> 
    /// <returns>A XML string that represents the object to be serialized</returns> 
    public static string Serialize(T source, XmlSerializerNamespaces namespaces, XmlWriterSettings settings) 
    { 
     if (source == null) 
      throw new ArgumentNullException("source", "Object to serialize cannot be null"); 

     string xml = null; 
     XmlSerializer serializer = new XmlSerializer(source.GetType()); 

     using (MemoryStream memoryStream = new MemoryStream()) 
     { 
      using (XmlWriter xmlWriter = XmlWriter.Create(memoryStream, settings)) 
      { 
       System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(T)); 
       x.Serialize(xmlWriter, source, namespaces); 

       memoryStream.Position = 0; // rewind the stream before reading back. 
       using (StreamReader sr = new StreamReader(memoryStream)) 
       { 
        xml = sr.ReadToEnd(); 
       } 
      } 
     } 

     return xml; 
    } 

    /// <summary> 
    /// Serialize an object to a XML file 
    /// </summary> 
    /// <param name="source">The object to serialize</param> 
    /// <param name="filename">The file to generate</param> 
    public static void SerializeToFile(T source, string filename) 
    { 
     // indented XML by default 
     SerializeToFile(source, filename, null, GetIndentedSettings()); 
    } 

    /// <summary> 
    /// Serialize an object to a XML file 
    /// </summary> 
    /// <param name="source">The object to serialize</param> 
    /// <param name="filename">The file to generate</param> 
    /// <param name="namespaces">Namespaces to include in serialization</param> 
    public static void SerializeToFile(T source, string filename, XmlSerializerNamespaces namespaces) 
    { 
     // indented XML by default 
     SerializeToFile(source, filename, namespaces, GetIndentedSettings()); 
    } 

    /// <summary> 
    /// Serialize an object to a XML file 
    /// </summary> 
    /// <param name="source">The object to serialize</param> 
    /// <param name="filename">The file to generate</param> 
    /// <param name="settings">XML serialization settings. <see cref="System.Xml.XmlWriterSettings"/></param> 
    public static void SerializeToFile(T source, string filename, XmlWriterSettings settings) 
    { 
     SerializeToFile(source, filename, null, settings); 
    } 

    /// <summary> 
    /// Serialize an object to a XML file 
    /// </summary> 
    /// <param name="source">The object to serialize</param> 
    /// <param name="filename">The file to generate</param> 
    /// <param name="namespaces">Namespaces to include in serialization</param> 
    /// <param name="settings">XML serialization settings. <see cref="System.Xml.XmlWriterSettings"/></param> 
    public static void SerializeToFile(T source, string filename, XmlSerializerNamespaces namespaces, XmlWriterSettings settings) 
    { 
     if (source == null) 
      throw new ArgumentNullException("source", "Object to serialize cannot be null"); 

     XmlSerializer serializer = new XmlSerializer(source.GetType()); 

     using (XmlWriter xmlWriter = XmlWriter.Create(filename, settings)) 
     { 
      System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(T)); 
      x.Serialize(xmlWriter, source, namespaces); 
     } 
    } 

    #region Private methods 


    private static XmlWriterSettings GetIndentedSettings() 
    { 
     XmlWriterSettings xmlWriterSettings = new XmlWriterSettings(); 
     xmlWriterSettings.Indent = true; 
     xmlWriterSettings.IndentChars = "\t"; 

     return xmlWriterSettings; 
    } 

    #endregion 
} 
+0

真的很好的答案,但目前不完全是我需要的。在你的情況下,XML將會是等等。這對我不起作用。在事件我需要EventObject的情況下,在新聞的情況下,我需要NewsObject。我試圖通過向事件類添加[XmlType(「EventObject」)]來做到這一點,但也沒有幫助。那麼,對我來說最簡單的事情就是將我的類「event」重命名爲「EventObject」,並刪除[XmlArrayItem] -Attribute。但我不喜歡命名對象是「* Object」。 –

+0

@Raubi:這只是一個例子,你可以按你想要的方式自定義XML。我不能給你一個更好的答案,因爲我不知道你想要什麼XML輸出。請編輯您的答案,併爲您的課程添加預期的XML輸出。 –

+0

@Raubi:編輯我的答案。我不確定XML是否正是您所需要的,但我認爲您會在我的答案中找到所需的所有內容來自定義您的XML。隨意下載演示項目,鏈接位於頂部。 PS:新年快樂! –

0

考慮使用,而不是XmlSerializer的DataContractSerializer的。

XmlSerializer在運行時生成一個臨時程序集,以便稍後加快序列化 - 反序列化過程。因此它需要編譯代碼。

DataContractSerializer另一方面沒有。如果您使用DataContractSerializer方法,則必須使用適當的屬性,以便控制「繼承」問題 。

+0

對不起,DataContractSerializer是不可能的。我需要XmlAttributes –

3

我找到了我的解決方案。沒有最好的,但它工作。

我impleted IXmlSerializable的和handeled通過自己的東西:

public void ReadXml(System.Xml.XmlReader reader) 
{ 
    reader.Read(); 
    reader.MoveToContent(); 

    if (reader.LocalName == "AnotherNode") 
    { 
     var innerXml = Serializer<AnotherClass>.CreateSerializer(); 
     Remove = (AnotherClass) innerXml.Deserialize(reader); 
     reader.MoveToContent(); 
    } 

    reader.Read(); 

    // Here is the trick 
    if (reader.IsStartElement()) 
    { 
     do 
     { 
      var innerXml = Serializer<T>.CreateSerializer(); 

      var obj = (T) innerXml.Deserialize(reader); 
      Updates.Add(obj); 
     } while (reader.MoveToContent() == XmlNodeType.Element); 
    } 
} 

public void WriteXml(System.Xml.XmlWriter writer) 
{ 
    var removeSerializer = Serializer<RemoveElement>.CreateSerializer(); 
    removeSerializer.Serialize(writer, Remove); 

    if (Updates.Any()) 
    { 
     var innerXml = Serializer<T>.CreateSerializer(); 
     writer.WriteStartElement("ContentUpdates"); 
     foreach (var update in Updates) 
     { 
      innerXml.Serialize(writer, update); 
     } 
     writer.WriteEndElement(); 
    } 
} 
0

聽起來像Windows應用商店平臺,爲XmlSerializer在代碼發射器的漏洞而言,卻是有點無實際意義,因爲它仍定期失敗。 NET,但有不同的消息:

Unable to generate a temporary class (result=1).

error CS0030: Cannot convert type 'News' to 'Event'

error CS1502: The best overloaded method match for 'System.Collections.Generic.List.Add(News)' has some invalid arguments

error CS1503: Argument 1: cannot convert from 'Event' to 'News'

所以基本上,它看起來像一個不支持的情況,在兩種情況下表現出不同。從根本上說,問題是你的註釋是說(對於T = News)「如果這個NewsNews,調用元素'NewsObject';如果這個NewsEvent,調用元素'EventObject'」 - 這是奇怪,因爲一個News從未Event

不過,好消息是,你想做的事,可以做的事情只是用[XmlType(...)]

[XmlType("NewsObject")] 
public class News : BaseContent 
{ 
    // Some more auto properties 
    public int B { get; set; } 
} 
[XmlType("EventObject")] 
public class Event : BaseContent 
{ 
    // Some more auto properites 
    public int C { get; set; } 
} 
... 
public class GenericResponse<T> 
{ 
    [XmlArray("Content")] 
    public List<T> ContentItems { get; set; } 
} 

注:在AB沒有[XmlArrayItem(...)] OVE。

,然後輸出(經過格式化,清洗等):通過測試代碼

<NewsResponse> 
    <Content> 
     <NewsObject><A>1</A><B>2</B></NewsObject> 
    </Content> 
</NewsResponse> 

var obj = new NewsResponse { ContentItems = new List<News> { 
    new News { A = 1, B = 2 } } }; 

var sw = new StringWriter(); 
using (var xw = System.Xml.XmlWriter.Create(sw)) 
{ 
    var ser = new XmlSerializer(obj.GetType()); 
    ser.Serialize(xw, obj); 
} 
string xml = sw.ToString(); 

如果您需要更多的控制,那麼XmlAttributeOverrides是看的東西在;但你必須必須緩存序列化器與XmlAttributeOverrides創建,以避免出血組裝和獲得內存泄漏。

+0

感謝您的回答。這也有訣竅。 :) –