13

キーと値のペア(できれば強く型付けされていますが、辞書から供給されることもあります)を以下の目的の形式にシリアル化する方法はありますか?

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>

したがって、必要な出力を取得するためにこれにどのようにアプローチするのが最善かについてのコードサンプルを探しています。

編集:たぶん私はもっとよく説明する必要があります。オブジェクトをシリアル化する方法はすでに知っています。私たちが探しているのは、特定のタイプのシリアル化に対する答えです...上記の質問を拡張します

4

9 に答える 9

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>
于 2012-09-20T18:27:44.190 に答える
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へのエスケープを処理していませんが、それは問題ではないかもしれません。それは完全に辞書の内容に依存します。

最少行のコードはどうですか?それがあなたの要件であるなら、LBのLinqtoXMLの答えがおそらく最良です。

最小のメモリフットプリントはどうですか?そこで、Dictionary名前と値を格納するだけで、ハッシュオーバーヘッドとコレクション機能を削除する独自のシリアル化可能なクラスを削除して作成することを検討します。それも最速かもしれません。

コードの単純さが要件である場合は、dynamic代わりに匿名タイプを使用するのはDictionaryどうですか?

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

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

そうすれば、コレクション内のプロパティの名前に「マジックストリング」を使用する必要がなくなります。コードに強く入力されます(それが重要な場合)。

ただし、匿名型にはシリアライザーが組み込まれていません。自分で何かを作成するか、多くの オープン ソース の代替手段の1つを使用するか、またはを使用する必要がありXmlMediaTypeFormatterます。

これを行う方法はたくさんありますが、どれを使用するかによって最適です。

于 2012-09-25T09:57:25.783 に答える
3

少し前に私は同様の問題を抱えていました。私はこれを使用することになりました(ここから取得)

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が彼のコメントに同じリンクを提供していることに気づきました。それはあなたのためのトリックをしませんでしたか?

于 2012-09-19T12:04:46.513 に答える
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>

あなたはあなたの意志に適応することができます。

于 2012-09-19T15:51:55.763 に答える
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();
    }
}
于 2012-09-22T07:30:34.113 に答える
1

DataContractSerializerを使用して、シリアル化および逆シリアル化できますDictionary<string, string>

コード:

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();
于 2012-09-22T11:03:13.410 に答える
1

もう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());
于 2012-09-24T03:12:25.297 に答える
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;
    }
}

(クレジット:ブログエントリC#2.0のジェネリック型を使用してオブジェクトをXmlとしてシリアル化および逆シリアル化します。)

をシリアル化するDictionaryには、シリアル化できないため、カップルのリストに変換することをお勧めします(おそらくLINQを使用)。

エントリの名前を編集するには、属性を使用したXMLシリアル化の制御 も確認してください。

わかりました。説明した後、私の頭に浮かぶ最初の、ばかばかしいほど難しい(またはまったく実行不可能な)タスクはType、標準のシリアライザーを使用してタグの名前を反映するように要素をプログラムで変更することです。それがうまくいくかどうかはわかりませんが。

于 2012-09-14T10:01:05.783 に答える
0

別のオプションは、カスタムシリアライザーでリフレクションを使用して、プロパティまたは変数の名前とその値を取得し、それを使用してXMLを構築することです。このようにして、渡したものやそのプロパティの名前を知る必要はありません。リフレクションの使用は遅いので、多数のオブジェクトをシリアル化する場合、これはうまくいかない可能性があることに注意してください。

于 2012-09-20T18:23:10.650 に答える