web-dev-qa-db-ja.com

XElementを使用していないカスタムXMLから `Dictionary <int、string>`にシリアライズ/デシリアライズする方法は?

空のDictionary<int, string>を使用して、XMLのキーと値を入力する方法

<items>
<item id='int_goes_here' value='string_goes_here'/>
</items>

xElementを使用せずにシリアル化してXMLに戻しますか?

43
myWallJSON

一時的なitemクラスの助けを借りて

public class item
{
    [XmlAttribute]
    public int id;
    [XmlAttribute]
    public string value;
}

サンプル辞書:

Dictionary<int, string> dict = new Dictionary<int, string>()
{
    {1,"one"}, {2,"two"}
};

XmlSerializer serializer = new XmlSerializer(typeof(item[]), 
                                 new XmlRootAttribute() { ElementName = "items" });

シリアル化

serializer.Serialize(stream, 
              dict.Select(kv=>new item(){id = kv.Key,value=kv.Value}).ToArray() );

デシリアライゼーション

var orgDict = ((item[])serializer.Deserialize(stream))
               .ToDictionary(i => i.id, i => i.value);

-------------------------------------------------- ----------------------------

気が変わった場合は、次のようにしますsing XElement

シリアル化

XElement xElem = new XElement(
                    "items",
                    dict.Select(x => new XElement("item",new XAttribute("id", x.Key),new XAttribute("value", x.Value)))
                 );
var xml = xElem.ToString(); //xElem.Save(...);

デシリアライゼーション

XElement xElem2 = XElement.Parse(xml); //XElement.Load(...)
var newDict = xElem2.Descendants("item")
                    .ToDictionary(x => (int)x.Attribute("id"), x => (string)x.Attribute("value"));
98
L.B

Paul Welterの ASP.NETブログ には、シリアライズ可能な辞書があります。ただし、属性は使用しません。コードの下にその理由を説明します。

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

[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue>
    : Dictionary<TKey, TValue>, IXmlSerializable
{
    #region IXmlSerializable Members
    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    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
}

まず、このコードには1つの落とし穴があります。これがある別のソースから辞書を読んだとしましょう:

<dictionary>
  <item>
    <key>
      <string>key1</string>
    </key>
    <value>
      <string>value1</string>
    </value>
  </item>
  <item>
    <key>
      <string>key1</string>
    </key>
    <value>
      <string>value2</string>
    </value>
  </item>
</dictionary>

辞書には1つのキーしか持てないため、これは分離解除時に例外をスローします。


直列化された辞書でXElementを使用する必要がある理由は、辞書がDictionary<String,String>として定義されておらず、辞書がDictionary<TKey,TValue>であるためです。

問題を確認するには、自分に問いかけます。XMLとしてそれ自体を記述する要素を使用するものにシリアル化するTValueがあるとします(辞書の辞書Dictionary<int,Dictionary<int,string>>(パターン、それはルックアップテーブルです))、あなたの属性のみのバージョンはどのように完全に属性内の辞書を表しますか?

29

ディクショナリはデフォルトではC#ではSerializableではありません。その理由はわかりませんが、デザインの選択であったようです。

今のところ、 Json.NET を使用してJSONに変換し、そこから辞書に変換することをお勧めします(逆も同様)。 XMLが本当に必要でない限り、JSONを完全に使用することをお勧めします。

6

構造体KeyValuePairSerializableがあります:

[Serializable]
public struct KeyValuePairSerializable<K, V>
{
    public KeyValuePairSerializable(KeyValuePair<K, V> pair)
    {
        Key = pair.Key;
        Value = pair.Value;
    }

    [XmlAttribute]
    public K Key { get; set; }

    [XmlText]
    public V Value { get; set; }

    public override string ToString()
    {
        return "[" + StringHelper.ToString(Key, "") + ", " + StringHelper.ToString(Value, "") + "]";
    }
}

次に、DictionaryプロパティのXMLシリアル化は次のとおりです。

[XmlIgnore]
public Dictionary<string, string> Parameters { get; set; }

[XmlArray("Parameters")]
[XmlArrayItem("Pair")]
[DebuggerBrowsable(DebuggerBrowsableState.Never)] // not necessary
public KeyValuePairSerializable<string, string>[] ParametersXml
{
    get 
    { 
        return Parameters?.Select(p => new KeyValuePairSerializable<string, string>(p)).ToArray(); 
    }
    set
    {
        Parameters = value?.ToDictionary(i => i.Key, i => i.Value);
    }
}

プロパティだけがリストではなく配列でなければなりません。

3
xmedeko

L.B.の回答に基づいています。

使用法:

var serializer = new DictionarySerializer<string, string>();
serializer.Serialize("dictionary.xml", _dictionary);
_dictionary = _titleDictSerializer.Deserialize("dictionary.xml");

ジェネリッククラス:

public class DictionarySerializer<TKey, TValue>
{
    [XmlType(TypeName = "Item")]
    public class Item
    {
        [XmlAttribute("key")]
        public TKey Key;
        [XmlAttribute("value")]
        public TValue Value;
    }

    private XmlSerializer _serializer = new XmlSerializer(typeof(Item[]), new XmlRootAttribute("Dictionary"));

    public Dictionary<TKey, TValue> Deserialize(string filename)
    {
        using (FileStream stream = new FileStream(filename, FileMode.Open))
        using (XmlReader reader = XmlReader.Create(stream))
        {
            return ((Item[])_serializer.Deserialize(reader)).ToDictionary(p => p.Key, p => p.Value);
        }
    }

    public void Serialize(string filename, Dictionary<TKey, TValue> dictionary)
    {
        using (var writer = new StreamWriter(filename))
        {
            _serializer.Serialize(writer, dictionary.Select(p => new Item() { Key = p.Key, Value = p.Value }).ToArray());
        }
    }
}
3
Jbjstam

ExtendedXmlSerializer を使用できます。クラスがある場合:

public class TestClass
{
    public Dictionary<int, string> Dictionary { get; set; }
}

このクラスのインスタンスを作成します。

var obj = new TestClass
{
    Dictionary = new Dictionary<int, string>
    {
        {1, "First"},
        {2, "Second"},
        {3, "Other"},
    }
};

ExtendedXmlSerializerを使用してこのオブジェクトをシリアル化できます。

var serializer = new ConfigurationContainer()
    .UseOptimizedNamespaces() //If you want to have all namespaces in root element
    .Create();

var xml = serializer.Serialize(
    new XmlWriterSettings { Indent = true }, //If you want to formated xml
    obj);

出力xmlは次のようになります。

<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns:sys="https://extendedxmlserializer.github.io/system" xmlns:exs="https://extendedxmlserializer.github.io/v2" xmlns="clr-namespace:ExtendedXmlSerializer.Samples;Assembly=ExtendedXmlSerializer.Samples">
  <Dictionary>
    <sys:Item>
      <Key>1</Key>
      <Value>First</Value>
    </sys:Item>
    <sys:Item>
      <Key>2</Key>
      <Value>Second</Value>
    </sys:Item>
    <sys:Item>
      <Key>3</Key>
      <Value>Other</Value>
    </sys:Item>
  </Dictionary>
</TestClass>

ExtendedXmlSerializerを nuget からインストールするか、次のコマンドを実行できます。

Install-Package ExtendedXmlSerializer
2
Wojtpl2

クラスBの配列を含むクラスAを記述します。クラスBにはidプロパティとvalueプロパティが必要です。 xmlをクラスAにデシリアライズします。Aの配列を目的の辞書に変換します。

辞書をシリアル化するには、クラスAのインスタンスに変換し、シリアル化します...

2
erikH

KeyedCollectionは辞書のように機能し、シリアル化可能です。

最初に、キーと値を含むクラスを作成します。

/// <summary>
/// simple class
/// </summary>
/// <remarks></remarks>
[Serializable()]
public class cCulture
{
    /// <summary>
    /// culture
    /// </summary>
    public string culture;

    /// <summary>
    /// Word list
    /// </summary>
    public List<string> list;

    /// <summary>
    /// status
    /// </summary>
    public string status;
}

次に、KeyedCollection型のクラスを作成し、クラスのプロパティをキーとして定義します。

/// <summary>
/// keyed collection.
/// </summary>
/// <remarks></remarks>
[Serializable()]
public class cCultures : System.Collections.ObjectModel.KeyedCollection<string, cCulture>
{
    protected override string GetKeyForItem(cCulture item)
    {
        return item.culture;
    }
}

この種のデータをシリアル化するのに便利です。

2
Smartoweb

異なるモジュール間のWCF通信にシリアル化可能なクラスを使用します。以下は、DataContractとしても機能するシリアル化可能なクラスの例です。私のアプローチは、LINQの機能を使用して、辞書をすぐに使用可能なKeyValuePair <>のシリアル化可能なList <>に変換することです。

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

    namespace MyFirm.Common.Data
    {
        [DataContract]
        [Serializable]
        public class SerializableClassX
        {
            // since the Dictionary<> class is not serializable,
            // we convert it to the List<KeyValuePair<>>
            [XmlIgnore]
            public Dictionary<string, int> DictionaryX 
            {
                get
                {
                    return  SerializableList == null ? 
                            null :
                            SerializableList.ToDictionary(item => item.Key, item => item.Value);
                }

                set
                {
                    SerializableList =  value == null ?
                                        null :
                                        value.ToList();
                }
            }

            [DataMember]
            [XmlArray("SerializableList")]
            [XmlArrayItem("Pair")]
            public List<KeyValuePair<string, int>> SerializableList { get; set; }
        }
    }

使い方は簡単です-データオブジェクトの辞書フィールド-DictionaryXに辞書を割り当てます。シリアル化は、割り当てられた辞書をKeyValuePair <>のシリアル化可能なList <>に変換することにより、SerializableClassX内でサポートされます。

    // create my data object
    SerializableClassX SerializableObj = new SerializableClassX(param);

    // this will call the DictionaryX.set and convert the '
    // new Dictionary into SerializableList
    SerializableObj.DictionaryX = new Dictionary<string, int>
    {
        {"Key1", 1},
        {"Key2", 2},
    };
1
Michael G

Sharpeserializer(オープンソース)には簡単な方法があります:

http://www.sharpserializer.com/

辞書を直接シリアライズ/デシリアライズできます。

オブジェクトを属性でマークする必要はありません。また、Serializeメソッドでオブジェクトタイプを指定する必要もありません( here を参照)。

Nuget経由でインストールするには:Install-package sharpserializer

それは非常に簡単です:

Hello World(公式ウェブサイトより):

// create fake obj
var obj = createFakeObject();

// create instance of sharpSerializer
// with standard constructor it serializes to xml
var serializer = new SharpSerializer();

// serialize
serializer.Serialize(obj, "test.xml");

// deserialize
var obj2 = serializer.Deserialize("test.xml");
0
Malick