Ich benötige ein XML-serialisierbares Wörterbuch. Eigentlich habe ich jetzt zwei ganz unterschiedliche Programme, die eines brauchen. Ich war ziemlich überrascht zu sehen, dass .NET keine hat. Ich stellte die Frage woanders und bekam sarkastische Antworten. Ich verstehe nicht, warum es eine dumme Frage ist.
Kann mich jemand aufklären, warum es kein XML-serialisierbares Wörterbuch gibt, wenn man bedenkt, wie abhängig verschiedene .NET-Funktionen von der XML-Serialisierung sind? Hoffentlich können Sie auch erklären, warum manche Leute das für eine blöde Frage halten. Ich denke, mir muss etwas Grundlegendes fehlen, und ich hoffe, dass Sie die Lücken füllen können.
.net
dictionary
xml-serialization
appsettings
Serialhobbyist
quelle
quelle
XmlSerializer
können Wörterbücher nicht serialisiert werden?" Weil es in .NET viele Möglichkeiten gibt, XML-Serialisierung durchzuführen, und die meisten von ihnen Wörterbücher problemlos serialisieren (DataContractSerializer
,SoapFormatter
...).Antworten:
Bei der XML-Serialisierung geht es nicht nur darum, einen Bytestrom zu erstellen. Es geht auch darum, ein XML-Schema zu erstellen, anhand dessen dieser Bytestrom validiert wird. Im XML-Schema gibt es keine gute Möglichkeit, ein Wörterbuch darzustellen. Das Beste, was Sie tun können, ist zu zeigen, dass es einen eindeutigen Schlüssel gibt.
Sie können jederzeit Ihren eigenen Wrapper erstellen, z. B. One Way to Serialize Dictionaries .
quelle
Ich weiß, dass dies bereits beantwortet wurde, aber da ich eine sehr präzise Methode (Code) für die IDictionary-Serialisierung mit der DataContractSerializer-Klasse habe (von WCF verwendet, aber überall verwendet werden könnte und sollte), konnte ich nicht widerstehen, sie hier beizutragen:
public static class SerializationExtensions { public static string Serialize<T>(this T obj) { var serializer = new DataContractSerializer(obj.GetType()); using (var writer = new StringWriter()) using (var stm = new XmlTextWriter(writer)) { serializer.WriteObject(stm, obj); return writer.ToString(); } } public static T Deserialize<T>(this string serialized) { var serializer = new DataContractSerializer(typeof(T)); using (var reader = new StringReader(serialized)) using (var stm = new XmlTextReader(reader)) { return (T)serializer.ReadObject(stm); } } }
Dies funktioniert perfekt in .NET 4 und sollte auch in .NET 3.5 funktionieren, obwohl ich es noch nicht getestet habe.
UPDATE: Es funktioniert nicht in .NET Compact Framework (nicht einmal NETCF 3.7 für Windows Phone 7), da das
DataContractSerializer
nicht unterstützt wird!Ich habe das Streaming zu String durchgeführt, weil es für mich bequemer war, obwohl ich Stream auf niedrigerer Ebene hätte serialisieren und dann zum Serialisieren in Strings verwenden können, aber ich neige dazu, nur bei Bedarf zu verallgemeinern (genau wie vorzeitige Optimierung böse ist , also ist es vorzeitige Verallgemeinerung ...)
Die Verwendung ist sehr einfach:
// dictionary to serialize to string Dictionary<string, object> myDict = new Dictionary<string, object>(); // add items to the dictionary... myDict.Add(...); // serialization is straight-forward string serialized = myDict.Serialize(); ... // deserialization is just as simple Dictionary<string, object> myDictCopy = serialized.Deserialize<Dictionary<string,object>>();
myDictCopy ist eine wörtliche Kopie von myDict.
Sie werden auch feststellen, dass die bereitgestellten generischen Methoden in der Lage sind, jeden Typ (nach meinem besten Wissen) zu serialisieren, da er nicht auf IDictionary-Schnittstellen beschränkt ist, sondern wirklich jeder generische Typ T sein kann.
Hoffe es hilft jemandem da draußen!
quelle
System.Runtime.Serialization
wenn Sie noch keine haben, diese jedoch im .NET 4.0-Clientprofil verfügbar ist.new XmlTextWriter
undnew XmlTextReader
anstelle vonXmlReader.Create
undXmlWriter.Create
.Sie haben eine in .NET 3.0 hinzugefügt. Wenn Sie können, fügen Sie einen Verweis auf System.Runtime.Serialization hinzu und suchen Sie nach System.Xml.XmlDictionary, System.Xml.XmlDictionaryReader und System.Xml.XmlDictionaryWriter.
Ich würde zustimmen, dass es nicht an einem besonders auffindbaren Ort ist.
quelle
Verwenden Sie den DataContractSerializer! Siehe das Beispiel unten.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Serialization; using System.Xml; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { A a = new A(); a.Value = 1; B b = new B(); b.Value = "SomeValue"; Dictionary<A, B> d = new Dictionary<A,B>(); d.Add(a, b); DataContractSerializer dcs = new DataContractSerializer(typeof(Dictionary<A, B>)); StringBuilder sb = new StringBuilder(); using (XmlWriter xw = XmlWriter.Create(sb)) { dcs.WriteObject(xw, d); } string xml = sb.ToString(); } } public class A { public int Value { get; set; } } public class B { public string Value { get; set; } } }
Der obige Code erzeugt die folgende XML:
<?xml version="1.0" encoding="utf-16"?> <ArrayOfKeyValueOfABHtQdUIlS xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays"> <KeyValueOfABHtQdUIlS> <Key xmlns:d3p1="http://schemas.datacontract.org/2004/07/ConsoleApplication1"> <d3p1:Value>1</d3p1:Value> </Key> <Value xmlns:d3p1="http://schemas.datacontract.org/2004/07/ConsoleApplication1"> <d3p1:Value>SomeValue</d3p1:Value> </Value> </KeyValueOfABHtQdUIlS> </ArrayOfKeyValueOfABHtQdUIlS>
quelle
Erstellen Sie eine eigene :-), die schreibgeschützte Funktion ist ein Bonus, aber wenn Sie einen anderen Schlüssel als eine Zeichenfolge benötigen, muss die Klasse einige Änderungen vornehmen ...
namespace MyNameSpace { [XmlRoot("SerializableDictionary")] public class SerializableDictionary : Dictionary<String, Object>, IXmlSerializable { internal Boolean _ReadOnly = false; public Boolean ReadOnly { get { return this._ReadOnly; } set { this.CheckReadOnly(); this._ReadOnly = value; } } public new Object this[String key] { get { Object value; return this.TryGetValue(key, out value) ? value : null; } set { this.CheckReadOnly(); if(value != null) { base[key] = value; } else { this.Remove(key); } } } internal void CheckReadOnly() { if(this._ReadOnly) { throw new Exception("Collection is read only"); } } public new void Clear() { this.CheckReadOnly(); base.Clear(); } public new void Add(String key, Object value) { this.CheckReadOnly(); base.Add(key, value); } public new void Remove(String key) { this.CheckReadOnly(); base.Remove(key); } public XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { Boolean wasEmpty = reader.IsEmptyElement; reader.Read(); if(wasEmpty) { return; } while(reader.NodeType != XmlNodeType.EndElement) { if(reader.Name == "Item") { String key = reader.GetAttribute("Key"); Type type = Type.GetType(reader.GetAttribute("TypeName")); reader.Read(); if(type != null) { this.Add(key, new XmlSerializer(type).Deserialize(reader)); } else { reader.Skip(); } reader.ReadEndElement(); reader.MoveToContent(); } else { reader.ReadToFollowing("Item"); } reader.ReadEndElement(); } public void WriteXml(XmlWriter writer) { foreach(KeyValuePair<String, Object> item in this) { writer.WriteStartElement("Item"); writer.WriteAttributeString("Key", item.Key); writer.WriteAttributeString("TypeName", item.Value.GetType().AssemblyQualifiedName); new XmlSerializer(item.Value.GetType()).Serialize(writer, item.Value); writer.WriteEndElement(); } } } }
quelle
Ein allgemeiner Helfer zum schnellen Hinzufügen von IXmlSerializable zu einem (vorhandenen) Wörterbuch ohne Verwendung der Vererbung:
using System.Xml; using System.Xml.Serialization; using System.Collections.Generic; namespace GameSpace { public class XmlSerializerForDictionary { public struct Pair<TKey,TValue> { public TKey Key; public TValue Value; public Pair(KeyValuePair<TKey,TValue> pair) { Key = pair.Key; Value = pair.Value; }//method }//struct public static void WriteXml<TKey,TValue>(XmlWriter writer, IDictionary<TKey,TValue> dict) { var list = new List<Pair<TKey,TValue>>(dict.Count); foreach (var pair in dict) { list.Add(new Pair<TKey,TValue>(pair)); }//foreach var serializer = new XmlSerializer(list.GetType()); serializer.Serialize(writer, list); }//method public static void ReadXml<TKey, TValue>(XmlReader reader, IDictionary<TKey, TValue> dict) { reader.Read(); var serializer = new XmlSerializer(typeof(List<Pair<TKey,TValue>>)); var list = (List<Pair<TKey,TValue>>)serializer.Deserialize(reader); foreach (var pair in list) { dict.Add(pair.Key, pair.Value); }//foreach reader.Read(); }//method }//class }//namespace
Und ein praktisches serialisierbares generisches Wörterbuch:
using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; using System.Collections.Generic; namespace GameSpace { public class SerializableDictionary<TKey,TValue> : Dictionary<TKey,TValue>, IXmlSerializable { public virtual void WriteXml(XmlWriter writer) { XmlSerializerForDictionary.WriteXml(writer, this); }//method public virtual void ReadXml(XmlReader reader) { XmlSerializerForDictionary.ReadXml(reader, this); }//method public virtual XmlSchema GetSchema() { return null; }//method }//class }//namespace
quelle
Dies ist meine Implementierung.
using System; using System.Collections.Generic; using System.Text; using System.Xml.Serialization; using System.Xml.Schema; using System.Xml; namespace Rubik.Staging { [XmlSchemaProvider("GetInternalSchema")] public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable { #region IXmlSerializable Members private const string ns = "http://www.rubik.com.tr/staging"; public static XmlQualifiedName GetInternalSchema(XmlSchemaSet xs) { bool keyIsSimple = (typeof(TKey).IsPrimitive || typeof(TKey) == typeof(string)); bool valueIsSimple = (typeof(TValue).IsPrimitive || typeof(TValue) == typeof(string)); XmlSchemas schemas = new XmlSchemas(); XmlReflectionImporter importer = new XmlReflectionImporter(ns); importer.IncludeType(typeof(TKey)); importer.IncludeType(typeof(TValue)); XmlTypeMapping keyMapping = importer.ImportTypeMapping(typeof(TKey)); XmlTypeMapping valueMapping = importer.ImportTypeMapping(typeof(TValue)); XmlSchemaExporter exporter = new XmlSchemaExporter(schemas); if(!keyIsSimple) exporter.ExportTypeMapping(keyMapping); if(!valueIsSimple) exporter.ExportTypeMapping(valueMapping); XmlSchema schema = (schemas.Count == 0 ? new XmlSchema() : schemas[0]); schema.TargetNamespace = ns; XmlSchemaComplexType type = new XmlSchemaComplexType(); type.Name = "DictionaryOf" + keyMapping.XsdTypeName + "And" + valueMapping.XsdTypeName; XmlSchemaSequence sequence = new XmlSchemaSequence(); XmlSchemaElement item = new XmlSchemaElement(); item.Name = "Item"; XmlSchemaComplexType itemType = new XmlSchemaComplexType(); XmlSchemaSequence itemSequence = new XmlSchemaSequence(); XmlSchemaElement keyElement = new XmlSchemaElement(); keyElement.Name = "Key"; keyElement.MaxOccurs = 1; keyElement.MinOccurs = 1; XmlSchemaComplexType keyType = new XmlSchemaComplexType(); XmlSchemaSequence keySequence = new XmlSchemaSequence(); XmlSchemaElement keyValueElement = new XmlSchemaElement(); keyValueElement.Name = keyMapping.ElementName; keyValueElement.SchemaTypeName = new XmlQualifiedName(keyMapping.XsdTypeName, keyMapping.XsdTypeNamespace); keyValueElement.MinOccurs = 1; keyValueElement.MaxOccurs = 1; keySequence.Items.Add(keyValueElement); keyType.Particle = keySequence; keyElement.SchemaType = keyType; itemSequence.Items.Add(keyElement); XmlSchemaElement valueElement = new XmlSchemaElement(); valueElement.Name = "Value"; valueElement.MaxOccurs = 1; valueElement.MinOccurs = 1; XmlSchemaComplexType valueType = new XmlSchemaComplexType(); XmlSchemaSequence valueSequence = new XmlSchemaSequence(); XmlSchemaElement valueValueElement = new XmlSchemaElement(); valueValueElement.Name = valueMapping.ElementName; valueValueElement.SchemaTypeName = new XmlQualifiedName(valueMapping.XsdTypeName, valueMapping.XsdTypeNamespace); valueValueElement.MinOccurs = 1; valueValueElement.MaxOccurs = 1; valueSequence.Items.Add(valueValueElement); valueType.Particle = valueSequence; valueElement.SchemaType = valueType; itemSequence.Items.Add(valueElement); itemType.Particle = itemSequence; item.SchemaType = itemType; sequence.Items.Add(item); type.Particle = sequence; schema.Items.Add(type); xs.XmlResolver = new XmlUrlResolver(); xs.Add(schema); return new XmlQualifiedName(type.Name, ns); } public void ReadXml(System.Xml.XmlReader reader) { XmlSerializer keySerializer = new XmlSerializer(typeof(TKey)); XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue)); bool wasEmpty = reader.IsEmptyElement; reader.Read(); if (wasEmpty) return; while (reader.NodeType != System.Xml.XmlNodeType.EndElement) { reader.ReadStartElement("Item"); reader.ReadStartElement("Key"); TKey key = (TKey)keySerializer.Deserialize(reader); reader.ReadEndElement(); reader.ReadStartElement("Value"); TValue value = (TValue)valueSerializer.Deserialize(reader); reader.ReadEndElement(); this.Add(key, value); reader.ReadEndElement(); reader.MoveToContent(); } reader.ReadEndElement(); } public void WriteXml(System.Xml.XmlWriter writer) { XmlSerializer keySerializer = new XmlSerializer(typeof(TKey)); XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue)); foreach (TKey key in this.Keys) { writer.WriteStartElement("Item"); writer.WriteStartElement("Key"); keySerializer.Serialize(writer, key); writer.WriteEndElement(); writer.WriteStartElement("Value"); TValue value = this[key]; valueSerializer.Serialize(writer, value); writer.WriteEndElement(); writer.WriteEndElement(); } } #endregion #region IXmlSerializable Members public XmlSchema GetSchema() { return null; } #endregion } }
quelle
Ich weiß, dass dies jetzt zu Tode getan wurde, aber hier ist mein Beitrag. Ich habe die guten Teile aus den Lösungen von @Loudenvier und @Jack genommen und meine eigene serialisierbare (sorry, ich bin Brite) Wörterbuchklasse geschrieben.
public class SerialisableDictionary<T1, T2> : Dictionary<T1, T2>, IXmlSerializable { private static DataContractSerializer serializer = new DataContractSerializer(typeof(Dictionary<T1, T2>)); public void WriteXml(XmlWriter writer) { serializer.WriteObject(writer, this); } public void ReadXml(XmlReader reader) { Dictionary<T1, T2> deserialised = (Dictionary<T1, T2>)serializer.ReadObject(reader); foreach(KeyValuePair<T1, T2> kvp in deserialised) { Add(kvp.Key, kvp.Value); } } public XmlSchema GetSchema() { return null; } }
Ich mag diesen Ansatz, weil Sie nichts explizit serialisieren oder deserialisieren müssen. Pumpen Sie einfach die gesamte Klassenhierarchie durch einen XmlSerializer und Sie sind fertig.
quelle