2012-09-14 57 views
13

有沒有一種方法序列化鍵/值對(最好強類型,但也可能來源於一個字典)成以下所需的格式?如何序列化一個對象集合/字典到<key>值</key>

public List<Identifier> Identifiers = new List<Identifiers>(); 

public class Identifier 
{ 
    public string Name { get; set; } 
    public string Description { get; set; } 
} 

這通常連載於以下內容:

<Identifiers> 
    <Identifier> 
    <Name>somename</Name> 
    <Description>somedescription</Description> 
    </Identifier> 
    <Identifier> 
    ... 
    </Identifier> 
</Identifiers> 

我們思考其他可能的方法是使用一個哈希表/字典:

public Dictionary<string, string> Identifiers = new Dictionary<string,string> 
{ 
    { "somename", "somedescription"}, 
    { "anothername", "anotherdescription" } 
}; 

但這要麼需要自定義序列化詞典或自定義XmlWriter

我們想實現的輸出是:

<Identifiers> 
    <somename>somedescription</somename> 
    <anothername>anotherdescription</anothername> 
</Identifiers> 

所以我們要找的代碼示例,以如何更好處理這個來得到我們想要的輸出。

編輯:也許我應該更好地解釋。我們已經知道如何序列化對象。我們正在尋找的答案是特定類型的系列化的...我會擴大上述

+0

看看這個 http://www.dacris.com/blog/2010/07/31/c-serializable-dictionary-a-working-example/ – Chatumbabub

回答

7

這很難回答,因爲你沒有真正弄清楚'最好'對你意味着什麼。

最快很可能是原始寫出來的字符串:

var sb = new StringBuilder(); 
sb.Append("<identifiers>"); 
foreach(var pair in identifiers) 
{ 
    sb.AppendFormat("<{0}>{1}</{0}>", pair.Key, pair.Value); 
} 
sb.Append("</identifiers>"); 

顯然,這不處理任何逃逸的XML,但隨後可能不會是一個問題,這完全取決於內容的字典。

最少行代碼?如果這是你的要求,那麼L.B.'s Linq to XML answer's可能是最好的。

最小的內存佔用量?在那裏,我會考慮刪除Dictionary並創建自己的可序列化類,以減少散列開銷和集合功能,而只是存儲名稱和值。這可能也是最快的。

如果代碼簡單是您的需求然後如何使用dynamic或匿名類型而不是Dictionary

var anonType = new 
{ 
    somename = "somedescription", 
    anothername = "anotherdescription" 
} 

// Strongly typed at compile time 
anonType.anothername = "new value"; 

你不處理「魔弦」爲您的收藏屬性的名稱這樣 - 它會在你的代碼是強類型(如果這對你很重要)。

但是匿名類型不具有內置串行器 - 你必須寫東西給自己,用的manyopensourcealternatives一個甚至使用XmlMediaTypeFormatter

加載的方法來做到這一點,哪一個最好取決於你將如何使用它。

+0

很好的答案,有很多選擇需要考慮。謝謝 – mwjackson

0

的問題,您可以使用C#XML系列化:

private static String SerializeObject<T>(T myObj, bool format) { 
    try { 
     String xmlizedString = null; 
     MemoryStream memoryStream = new MemoryStream(); 
     XmlSerializer xs = null; 
     XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8); 
     if (format) 
      xmlTextWriter.Formatting = Formatting.Indented; 

     xs = new XmlSerializer(typeof(T), "MyXmlData"); 

     xs.Serialize(xmlTextWriter, myObj); 

     memoryStream = (MemoryStream)xmlTextWriter.BaseStream; 
     //eventually 
     xmlizedString = UTF8ByteArrayToString(memoryStream.ToArray()); 
     return xmlizedString; 
    } 
    catch (Exception e) { 
     //return e.ToString(); 
     throw; 
    } 
} 

(來源:博客條目Serialize and deserialize objects as Xml using generic types in C# 2.0

要序列化一個Dictionary,我建議將它轉換成夫婦列表(可能與LINQ),因爲它不是可序列化的。

還請檢查Controlling XML Serialization Using Attributes編輯條目的名稱。 OK,經過您的澄清之後,我想到的第一個荒謬難堪(或者根本不可行)的任務是以某種方式以編程方式更改元素的Type以反映使用標準序列化程序標記的名稱。我不知道它是否會起作用。

+0

請閱讀我的編輯 – mwjackson

+0

好吧,我會看看問題,我以前見過類似的東西,只需要找到 – Gabber

+0

btw抱歉的誤解 – Gabber

3

前段時間我有類似的問題。最後我用這個(從here拍攝)

using System; 
using System.Runtime.Serialization; 
using System.Xml; 
using System.Xml.Serialization; 
using System.Collections.Generic; 
using System.Text; 

[Serializable()] 
public class SerializableDictionary<TKey, TVal> : Dictionary<TKey, TVal>, IXmlSerializable, ISerializable 
{ 
     #region Constants 
     private const string DictionaryNodeName = "Dictionary"; 
     private const string ItemNodeName = "Item"; 
     private const string KeyNodeName = "Key"; 
     private const string ValueNodeName = "Value"; 
     #endregion 
     #region Constructors 
     public SerializableDictionary() 
     { 
     } 

     public SerializableDictionary(IDictionary<TKey, TVal> dictionary) 
      : base(dictionary) 
     { 
     } 

     public SerializableDictionary(IEqualityComparer<TKey> comparer) 
      : base(comparer) 
     { 
     } 

     public SerializableDictionary(int capacity) 
      : base(capacity) 
     { 
     } 

     public SerializableDictionary(IDictionary<TKey, TVal> dictionary, IEqualityComparer<TKey> comparer) 
      : base(dictionary, comparer) 
     { 
     } 

     public SerializableDictionary(int capacity, IEqualityComparer<TKey> comparer) 
      : base(capacity, comparer) 
     { 
     } 

     #endregion 
     #region ISerializable Members 

     protected SerializableDictionary(SerializationInfo info, StreamingContext context) 
     { 
      int itemCount = info.GetInt32("ItemCount"); 
      for (int i = 0; i < itemCount; i++) 
      { 
       KeyValuePair<TKey, TVal> kvp = (KeyValuePair<TKey, TVal>)info.GetValue(String.Format("Item{0}", i), typeof(KeyValuePair<TKey, TVal>)); 
       this.Add(kvp.Key, kvp.Value); 
      } 
     } 

     void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) 
     { 
      info.AddValue("ItemCount", this.Count); 
      int itemIdx = 0; 
      foreach (KeyValuePair<TKey, TVal> kvp in this) 
      { 
       info.AddValue(String.Format("Item{0}", itemIdx), kvp, typeof(KeyValuePair<TKey, TVal>)); 
       itemIdx++; 
      } 
     } 

     #endregion 
     #region IXmlSerializable Members 

     void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) 
     { 
      //writer.WriteStartElement(DictionaryNodeName); 
      foreach (KeyValuePair<TKey, TVal> kvp in this) 
      { 
       writer.WriteStartElement(ItemNodeName); 
       writer.WriteStartElement(KeyNodeName); 
       KeySerializer.Serialize(writer, kvp.Key); 
       writer.WriteEndElement(); 
       writer.WriteStartElement(ValueNodeName); 
       ValueSerializer.Serialize(writer, kvp.Value); 
       writer.WriteEndElement(); 
       writer.WriteEndElement(); 
      } 
      //writer.WriteEndElement(); 
     } 

     void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) 
     { 
      if (reader.IsEmptyElement) 
      { 
       return; 
      } 

      // Move past container 
      if (!reader.Read()) 
      { 
       throw new XmlException("Error in Deserialization of Dictionary"); 
      } 

      //reader.ReadStartElement(DictionaryNodeName); 
      while (reader.NodeType != XmlNodeType.EndElement) 
      { 
       reader.ReadStartElement(ItemNodeName); 
       reader.ReadStartElement(KeyNodeName); 
       TKey key = (TKey)KeySerializer.Deserialize(reader); 
       reader.ReadEndElement(); 
       reader.ReadStartElement(ValueNodeName); 
       TVal value = (TVal)ValueSerializer.Deserialize(reader); 
       reader.ReadEndElement(); 
       reader.ReadEndElement(); 
       this.Add(key, value); 
       reader.MoveToContent(); 
      } 
      //reader.ReadEndElement(); 

      reader.ReadEndElement(); // Read End Element to close Read of containing node 
     } 

     System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() 
     { 
      return null; 
     } 

     #endregion 
     #region Private Properties 
     protected XmlSerializer ValueSerializer 
     { 
      get 
      { 
       if (valueSerializer == null) 
       { 
        valueSerializer = new XmlSerializer(typeof(TVal)); 
       } 
       return valueSerializer; 
      } 
     } 

     private XmlSerializer KeySerializer 
     { 
      get 
      { 
       if (keySerializer == null) 
       { 
        keySerializer = new XmlSerializer(typeof(TKey)); 
       } 
       return keySerializer; 
      } 
     } 
     #endregion 
     #region Private Members 
     private XmlSerializer keySerializer = null; 
     private XmlSerializer valueSerializer = null; 
     #endregion 
} 

我只注意到@Chatumbabub在他的評論中提供的相同的鏈接。這不是爲你做的伎倆嗎?

+0

這就是我使用的。它工作得很好。 – Bobson

1

我不認爲你可以用非常「靜態」的XmlSerializer做你想做的事情。這裏有幾個助手,你可以讓你開始用一本字典(通用與否):

public static string Serialize(IDictionary dictionary) 
    { 
     using (StringWriter writer = new StringWriter()) 
     { 
      Serialize(writer, dictionary); 
      return writer.ToString(); 
     } 
    } 

    public static void Serialize(TextWriter writer, IDictionary dictionary) 
    { 
     if (writer == null) 
      throw new ArgumentNullException("writer"); 

     using (XmlTextWriter xwriter = new XmlTextWriter(writer)) 
     { 
      Serialize(xwriter, dictionary); 
     } 
    } 

    public static void Serialize(XmlWriter writer, IDictionary dictionary) 
    { 
     if (writer == null) 
      throw new ArgumentNullException("writer"); 

     if (dictionary == null) 
      throw new ArgumentNullException("dictionary"); 

     foreach (DictionaryEntry entry in dictionary) 
     { 
      writer.WriteStartElement(string.Format("{0}", entry.Key)); 
      writer.WriteValue(entry.Value); 
      writer.WriteEndElement(); 
     } 
    } 

有了這些幫助,下面的代碼:

 Dictionary<string, string> Identifiers = new Dictionary<string,string> 
     { 
      { "somename", "somedescription"}, 
      { "anothername", "anotherdescription" } 
     }; 
     Console.WriteLine(Serialize(Identifiers)); 

將輸出這樣的:

<somename>somedescription</somename><anothername>anotherdescription</anothername> 

您可以適應您的意願。

0

另一種選擇是在自定義序列化程序中使用反射來獲取屬性或變量的名稱以及它的值並使用它來構建XML。這樣你就不需要知道你傳遞了什麼或者它的屬性名稱。請注意,使用反射速度很慢,所以如果你序列化大量的對象,這可能不是要走的路。

22

這是很容易與LINQ to XML

Dictionary<string, string> Identifiers = new Dictionary<string,string>() 
{ 
    { "somename", "somedescription"}, 
    { "anothername", "anotherdescription" } 
}; 

XElement xElem = new XElement("Identifiers", 
           Identifiers.Select(x=>new XElement(x.Key,x.Value))); 

string xml = xElem.ToString(); //xElem.Save(.....); 

OUTPUT:

<Identifiers> 
    <somename>somedescription</somename> 
    <anothername>anotherdescription</anothername> 
</Identifiers> 
1

這是否幫助呢?

public class CustomDictionary<TValue> : Dictionary<string, TValue>, IXmlSerializable 
{ 
    private static readonly XmlSerializer ValueSerializer; 

    private readonly string _namespace; 

    static CustomDictionary() 
    { 
     ValueSerializer = new XmlSerializer(typeof(TValue)); 
     ValueSerializer.UnknownNode += ValueSerializerOnUnknownElement; 
    } 

    private static void ValueSerializerOnUnknownElement(object sender, XmlNodeEventArgs xmlNodeEventArgs) 
    { 
     Debugger.Break(); 
    } 

    public CustomDictionary() 
     : this("") 
    { 
    } 

    public CustomDictionary(string @namespace) 
    { 
     _namespace = @namespace; 
    } 

    public XmlSchema GetSchema() 
    { 
     return null; 
    } 

    public void ReadXml(XmlReader reader) 
    { 
     reader.Read(); 
     var keepGoing = true; 

     while(keepGoing) 
     { 
      if (reader.NodeType == XmlNodeType.Element) 
      { 
       this[reader.Name] = (TValue) reader.ReadElementContentAs(typeof (TValue), null); 
      } 
      else 
      { 
       keepGoing = reader.Read(); 
      } 
     } 
    } 

    public void WriteXml(XmlWriter writer) 
    { 
     foreach(var kvp in this) 
     { 
      var document = new XDocument(); 

      using(var stringWriter = document.CreateWriter()) 
      { 
       ValueSerializer.Serialize(stringWriter, kvp.Value); 
      } 

      var serializedValue = document.Root.Value; 
      writer.WriteElementString(kvp.Key, _namespace, serializedValue); 
     } 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var dict = new CustomDictionary<string> 
     { 
      {"Hello", "World"}, 
      {"Hi", "There"} 
     }; 

     var serializer = new XmlSerializer(typeof (CustomDictionary<string>)); 

     serializer.Serialize(Console.Out, dict); 
     Console.ReadLine(); 
    } 
} 
1

您可以使用DataContractSerializer的序列化&反序列化Dictionary<string, string>

CODE:

Dictionary<string, string> dictionary = new Dictionary<string, string>(); 

dictionary.Add("k1", "valu1"); 
dictionary.Add("k2", "valu2"); 

System.Runtime.Serialization.DataContractSerializer serializer = new System.Runtime.Serialization.DataContractSerializer(typeof(Dictionary<string, string>)); 
System.IO.MemoryStream stream = new System.IO.MemoryStream(); 

serializer.WriteObject(stream, dictionary); 

System.IO.StreamReader reader = new System.IO.StreamReader(stream); 

stream.Position = 0; 
string xml = reader.ReadToEnd(); 
1

另一種方法是子類的XmlTextWriter和序列標識符類型時控制輸出。有點駭人,但可能會給你另一條途徑。不需要向類型添加任何屬性元數據。

public class IdentifierXmlWriter : XmlTextWriter 
{ 
    private bool isIdentifier = false; 
    private bool isName = false; 
    private bool isDescription = false; 

    private readonly string identifierElementName; 
    private readonly string nameElementName; 
    private readonly string descElementName; 

    public IdentifierXmlWriter(TextWriter w) : base(w) 
    { 
     Type identitierType = typeof (Identifier); 

     identifierElementName = (identitierType.GetCustomAttributes(typeof(XmlElementAttribute), true).FirstOrDefault() as XmlElementAttribute ?? new XmlElementAttribute("Identifier")).ElementName; 
     nameElementName = (identitierType.GetProperty("Name").GetCustomAttributes(typeof(XmlElementAttribute), true).FirstOrDefault() as XmlElementAttribute ?? new XmlElementAttribute("Name")).ElementName; 
     descElementName = (identitierType.GetProperty("Description").GetCustomAttributes(typeof(XmlElementAttribute), true).FirstOrDefault() as XmlElementAttribute ?? new XmlElementAttribute("Description")).ElementName; 
    } 

    public override void WriteStartElement(string prefix, string localName, string ns) 
    { 
     // If Identifier, set flag and ignore. 
     if (localName == identifierElementName) 
     { 
      isIdentifier = true; 
     } 
     // if inside Identifier and first occurance of Name, set flag and ignore. This will be called back with the element name in the Name's Value write call 
     else if (isIdentifier && !isName && !isDescription && localName == this.nameElementName) 
     { 
      isName = true; 
     } 
     // if inside Identifier and first occurance of Description, set flag and ignore 
     else if (isIdentifier && !isName && !isDescription && localName == this.descElementName) 
     { 
      isDescription = true; 
     } 
     else 
     { 
      // Write the element 
      base.WriteStartElement(prefix, localName, ns); 
     } 
    } 

    public override void WriteString(string text) 
    { 
     if (this.isIdentifier && isName) 
      WriteStartElement(text);   // Writing the value of the Name property - convert to Element 
     else 
      base.WriteString(text); 
    } 

    public override void WriteEndElement() 
    { 
     // Close element from the Name property - Ignore 
     if (this.isIdentifier && this.isName) 
     { 
      this.isName = false; 
      return; 
     } 

     // Cliose element from the Description - Closes element started with the Name value write 
     if (this.isIdentifier && this.isDescription) 
     { 
      base.WriteEndElement(); 
      this.isDescription = false; 
      return; 
     } 

     // Close element of the Identifier - Ignore and reset 
     if (this.isIdentifier) 
     { 
      this.isIdentifier = false; 
     } 
     else 
      base.WriteEndElement(); 
    } 
} 

     List<Identifier> identifiers = new List<Identifier>() 
              { 
               new Identifier() { Name = "somename", Description = "somedescription"}, 
               new Identifier() { Name = "anothername", Description = "anotherdescription"}, 
               new Identifier() { Name = "Name", Description = "Description"}, 
              }; 

這運行上面的代碼並生成所需的格式,儘管沒有換行符和縮進。

 StringBuilder sb = new StringBuilder(); 
     using (var writer = new IdentifierXmlWriter(new StringWriter(sb))) 
     { 
      XmlSerializer xmlSerializer = new XmlSerializer(identifiers.GetType(), new XmlRootAttribute("Identifiers")); 
      xmlSerializer.Serialize(writer, identifiers); 
     } 

     Console.WriteLine(sb.ToString()); 
相關問題