2012-09-14 32 views
13

में ऑब्जेक्ट संग्रह/शब्दकोश को क्रमबद्ध करने के लिए कैसे करें क्या वांछित प्रारूप में कुंजी/मूल्य जोड़े (अधिमानतः दृढ़ता से टाइप किया गया है, लेकिन संभावित रूप से एक शब्दकोश से भी सोर्स किया गया है) को क्रमबद्ध करने का कोई तरीका है?<key> मूल्य

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

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

यह सामान्य रूप से करने के लिए serializes निम्नलिखित:

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

अन्य संभावित दृष्टिकोण के बारे में हम सोच रहे थे एक hashtable/शब्दकोश का उपयोग करना है:

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>"); 

जाहिर है कि किसी भी एक्सएमएल से बचने, लेकिन तब है कि एक समस्या नहीं हो सकता से निपटने नहीं कर रहा है, यह पूरी तरह की सामग्री पर निर्भर करता है आपके शब्दकोश।

कोड कोड की सबसे कम लाइनों के बारे में क्या? यदि यह आपकी आवश्यकता है तो L.B.'s Linq to XML answer's शायद सबसे अच्छा है।

सबसे छोटी मेमोरी पदचिह्न के बारे में क्या? वहां मैं Dictionary छोड़ने और अपनी खुद की धारावाहिक कक्षा बनाने के लिए देख रहा हूं जो हैश ओवरहेड और संग्रह कार्यक्षमता को केवल नाम और मूल्य संग्रहीत करने के पक्ष में छोड़ देता है। यह भी सबसे तेज़ हो सकता है।

तो कोड सादगी कैसे करें के बारे Dictionary बजाय dynamic या अनाम प्रकार का उपयोग कर तो आपकी आवश्यकता है?

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

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

इस तरह आप अपने संग्रह में गुण के नाम के लिए 'जादू तार' के साथ काम कर नहीं कर रहे हैं - यह दृढ़ता से अपने कोड में टाइप दिया जाएगा (अगर है कि आप के लिए महत्वपूर्ण है)।

हालांकि गुमनाम प्रकार एक serialiser में बनाया नहीं है - आप, अपने लिए कुछ लिखना 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 जांचें।

ठीक है, आपके स्पष्टीकरण के बाद, मेरे दिमाग में आने वाले पहले, बेतुका कड़ी मेहनत (या बिल्कुल व्यवहार्य नहीं) कार्य किसी भी तरह से मानक धारावाहिक का उपयोग कर टैग के नाम को प्रतिबिंबित करने के लिए तत्वों के Type को प्रोग्रामेटिक रूप से बदलना है। मुझे नहीं पता कि यह काम करेगा या नहीं।

+0

मेरे संपादन – mwjackson

+0

ठीक है, मैं हूँ पढ़ कृपया समस्या को देखो, मैंने पहले ऐसा कुछ देखा है, केवल – Gabber

+0

बीटीडब्ल्यू को गलतफहमी के लिए खेद है – 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 के साथ चाहते हैं वह कर सकते हैं।

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

इच्छा उत्पादन इस:

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(); 
     } 
    } 
इन सहायकों, निम्नलिखित कोड के साथ

: यहाँ सहायकों की एक जोड़ी है कि आप हो सकता है आप एक शब्दकोश (सामान्य या नहीं) के साथ शुरू कर रहे हैं

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

आप अपनी इच्छानुसार अनुकूलित कर सकते हैं।

0

एक वैकल्पिक विकल्प आपके कस्टम सीरियलाइज़र में प्रतिबिंब का उपयोग करने के लिए संपत्ति या चर के नाम के रूप में कुएं के रूप में इसका मूल्य प्राप्त करने के लिए होगा और इसका उपयोग एक्सएमएल बनाने के लिए किया जाएगा। इस तरह आपको यह जानने की आवश्यकता नहीं होगी कि आप क्या गुजर रहे थे या इसके गुणों का नाम क्या था। ध्यान रखें कि प्रतिबिंब का उपयोग धीमा है इसलिए यदि आप बड़ी संख्या में ऑब्जेक्ट्स को क्रमबद्ध कर रहे हैं तो यह जाने का तरीका नहीं हो सकता है।

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(.....); 

उत्पादन:

<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

आप & deserialize Dictionary<string, string> क्रमानुसार करने DataContractSerializer उपयोग कर सकते हैं।

कोड:

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 उपवर्ग और जब पहचानकर्ता प्रकार serializing उत्पादन को नियंत्रित करने के लिए है। थोड़ा सा हैक लेकिन आपको एक और एवेन्यू दे सकता है। किसी भी विशेषता मेटाडेटा को प्रकारों में जोड़ने की आवश्यकता नहीं है।

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()); 
संबंधित मुद्दे