10626


お世話になります。

Dictionaryを内包したクラスをシリアル化する方法を教えてください。  
ネットでも検索しましたが、ちょゅっと複雑で理解できませんでした。

お手数ではありますが、サンプルを踏まえて教えていただければ助かります。

下記の様なクラスです。

[code]

    public class Page
    {
        public string name = null;
        public Dictionary<string ,HashSet<string> dic = new Dictionary<string ,HashSet<string>>();
    
    }
    
[/code]

本当に申し訳ありませんが、これだけのクラスです。  
Dictionaryにstringが一つついただけですが、このクラスの  
シリアル化、逆シリアル化をお願いいたします。


CC BY-SA 3.0
2015-05-25T06:28:00.153
10579
2015-05-26T02:41:24.617
2015-05-25T09:46:11.460
5044
9374
question
c#
C#　Dictionaryクラスを内包したクラスをシリアル化する方法


<http://dobon.net/vb/dotnet/file/xmlserializerhashtable.html>  
の「シリアル化できるDictionaryを作成する」はどうですか。

WriteXml()内でthisからKeyとValueを取得してシリアル化(XML化)  
ReadXml()内でXMLからKeyとValueを取得してthisに追加  
どういうXMLにするかは自分で決める必要があります。


CC BY-SA 3.0
2015-05-25T06:51:43.043
10583
2015-05-25T06:51:43.043
3446
10579
answer


ターゲットを .NET Framework 3.0
以降に限定できるなら、DataContractSerializerを利用する方法が比較的手軽で使いやすいと思われます。

上記の方法を利用する場合は、プロジェクトの参照設定に「System.Runtime.Serialization.dll」を追加して、シリアライズするクラスに[DataContract]属性を、フィールドに[DataMember]属性をそれぞれ記述する必要があります。

以下、xmlをシリアル化して直接編集して再度逆シリアル化するコンソールアプリケーションのサンプルです。

※編集前のPageクラスに合わせてコーディングしてしまいました。  
おそらくDictionary<string ,HashSet<string>>でも動くと思いますが、手元に開発環境がないので確認できません。ご留意願います。

[code]

    using System;
    using System.Collections.Generic;
    using System.Runtime.Serialization;
    using System.Text;
    using System.Xml;
    
    class Program
    {
        static void Main(string[] args)
        {
            var page = new Page();
            page.Dic.Add("string", "foo");
            page.Dic.Add("int", 1);
            page.Dic.Add("double", 3.14d);
    
            //シリアル化(整形無し)
            var serializer = new DataContractSerializer(typeof(Page));
            using (var writer = XmlWriter.Create("raw.xml"))
            {
                serializer.WriteObject(writer, page);
            }
            //シリアル化(整形あり)
            var settings = new XmlWriterSettings
            {
                Encoding = Encoding.UTF8,  //エンコーディングも指定可
                Indent = true,
            };
            using (var writer = XmlWriter.Create("arranged.xml", settings))
            {
                serializer.WriteObject(writer, page);
            }
            //XmlDocumentによる読み込みと書き換え(シリアル化とは無関係なコード)
            //<Name i:nil="true" /> を <Name>ほげ</Name>に置換 
            var doc = new XmlDocument();
            doc.Load("arranged.xml");
            var child = doc["Page"]["Name"];
            child.Attributes.RemoveAll();
            child.InnerText = "ほげ";
            doc.Save("changed.xml");
            //逆シリアル化
            using (var reader = XmlReader.Create("changed.xml"))
            {
                page = (Page)serializer.ReadObject(reader);
            }
            //結果表示
            Console.WriteLine(string.Format("Name: {0}", page.Name));
            foreach (var pair in page.Dic)
            {
                Console.WriteLine(string.Format("{0}: {1}", pair.Key, pair.Value));
            }
            Console.ReadLine();
        }
    }
    
    //参照設定に「System.Runtime.Serialization.dll」を追加すること
    [DataContract]
    public class Page
    {
        [DataMember]
        public string Name;
        [DataMember]
        public Dictionary<string, object> Dic;
    
        public Page()
        {
            //(ローカル変数ではなく)フィールドやプロパティはnullで初期化されるので、以下の記述は不要
            //Name = null;
            Dic = new Dictionary<string, object>();
        }
    }
    
[/code]

なおDataContractSerializerから出力されるxmlは、以下の例のようにあまり手入力に優しくありません。  
.NETのバージョンやxmlのフォーマットに問題がある場合は、sokandkさんの紹介するリンクを参考に実装するか、またはXmlDocumentを自前で作成するなどの方法を検討してください。

[code]

    <?xml version="1.0" encoding="utf-8"?>
    <Page xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1">
      <Dic xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
        <d2p1:KeyValueOfstringanyType>
          <d2p1:Key>string</d2p1:Key>
          <d2p1:Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:string">foo</d2p1:Value>
        </d2p1:KeyValueOfstringanyType>
        <d2p1:KeyValueOfstringanyType>
          <d2p1:Key>int</d2p1:Key>
          <d2p1:Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:int">1</d2p1:Value>
        </d2p1:KeyValueOfstringanyType>
        <d2p1:KeyValueOfstringanyType>
          <d2p1:Key>double</d2p1:Key>
          <d2p1:Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:double">3.14</d2p1:Value>
        </d2p1:KeyValueOfstringanyType>
      </Dic>
      <Name>ほげ</Name>
    </Page>
    
[/code]


CC BY-SA 3.0
2015-05-25T11:52:39.480
10601
2015-05-25T11:59:21.397
2015-05-25T11:59:21.397
9820
9820
10579
answer


単純な、同一環境への永続化や、同一アーキテクチャの別環境への転送と言うことであれば、BinaryFormatterを使うのが一番簡単かと思います。

欠点としては、バイナリへのシリアライズを行うので、シリアライズ結果の単純な目視確認が難しいという点と、例えば受信先の環境が異なる場合は、対処が難しい点です。

以下のサンプルはファイルストリームにシリアライズして、そこからデシリアライズするものです。

[code]

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;
    
    namespace ConsoleApplication8
    {
        //この属性を付与する
        [Serializable]
        public class Page
        {
            public Dictionary<string, HashSet<string>> Dict = new Dictionary<string, HashSet<string>>();
            public string Name;
        }
    
    
        internal class Program
        {
            private static void Main()
            {
                //とりあえず作成。
                Page page = new Page {Name = "name"};
                page.Dict.Add("foo", new HashSet<string> {"hello", "world"});
                page.Dict.Add("bar", new HashSet<string> {"piyo"});
    
                //フォーマッタの初期化
                BinaryFormatter formatter = new BinaryFormatter();
    
                //シリアライズ
                using (FileStream fileStr = new FileStream("Page.bin", FileMode.Create))
                {
                    formatter.Serialize(fileStr, page);
                }
    
                Page deserializedObject;
                //デシリアライズ
                using (FileStream fileStr = new FileStream("Page.bin", FileMode.Open))
                {
                    deserializedObject = formatter.Deserialize(fileStr) as Page;
                }
    
    
                //蛇足的に。デシリアライズ後のPageオブジェクトの表示をしてみる。
                if (deserializedObject == null) Console.WriteLine("Fail!");
                else
                {
                    Console.WriteLine("Name:" + deserializedObject.Name);
                    Console.WriteLine();
    
                    foreach (KeyValuePair<string, HashSet<string>> pair in deserializedObject.Dict)
                    {
                        Console.WriteLine("Key:" + pair.Key);
                        foreach (string element in pair.Value)
                        {
                            Console.WriteLine("\tSetValue:" + element);
                        }
                        Console.WriteLine();
                    }
                }
    
                Console.WriteLine();
                Console.WriteLine("Press enter to exit...");
                Console.ReadLine();
            }
        }
    }
    
[/code]

もし、もっと汎用的なシリアライゼーションが必要であるなら、

  * Json.NET <https://www.nuget.org/packages/newtonsoft.json/>
  * MessagePack for CLI <https://www.nuget.org/packages/MsgPack.Cli>

などの外部シリアライザの運用を考慮してみても良いかも知れないです。  
尚、詳説は省きますが上記二つの外部シリアライザを使った場合のシリアライズと、デシリアライズの方法を簡単に書いておきます。（Nugetにて、適宜上記ライブラリを取得し参照しておいてください）

[code]

    using System;
    using System.Collections.Generic;
    using System.IO;
    using MsgPack;
    using Newtonsoft.Json;
    
    namespace ConsoleApplication8
    {
        public class Page
        {
            public Dictionary<string, HashSet<string>> Dict = new Dictionary<string, HashSet<string>>();
            public string Name;
        }
    
    
        internal class Program
        {
            private static void Main()
            {
                //とりあえず作成。
                Page page = new Page {Name = "Json.NET"};
                page.Dict.Add("foo", new HashSet<string> {"hello", "world"});
                page.Dict.Add("bar", new HashSet<string> {"piyo"});
    
                //Json.NETを使う場合。
                JsonSerializer jsonSerializer = new JsonSerializer();
    
    
                //シリアライズ
                using (StreamWriter wtr = new StreamWriter("page.json"))
                {
                    jsonSerializer.Serialize(wtr, page);
                }
    
                Page deserialized;
    
                //デシリアライズ
                using (JsonTextReader rdr = new JsonTextReader(new StreamReader("page.json")))
                {
                    deserialized = jsonSerializer.Deserialize<Page>(rdr);
                }
    
                //表示
                Console.WriteLine("Json sample");
                Dump(deserialized);
    
                Console.WriteLine();
                Console.WriteLine();
    
    
                //MessagePack for CLIを使う場合。
                page.Name = "MessagePack for CLI";
                page.Dict.Add("MsgPack", new HashSet<string> {"hoge", "piyo"});
    
                //シリアライズ
                using (FileStream str = new FileStream("page.msg", FileMode.Create))
                {
                    ObjectPacker packer = new ObjectPacker();
                    packer.Pack(str, page);
                }
    
                //デシリアライズ
                using (FileStream str = new FileStream("page.msg", FileMode.Open))
                {
                    ObjectPacker packer = new ObjectPacker();
                    deserialized = packer.Unpack<Page>(str);
                }
    
                //表示
                Console.WriteLine("MsgPack sample");
                Dump(deserialized);
            }
    
            public static void Dump(Page page)
            {
                Console.WriteLine("Name:" + page.Name);
                Console.WriteLine();
    
                foreach (var pair in page.Dict)
                {
                    Console.WriteLine("Key:" + pair.Key);
                    foreach (var elem in pair.Value)
                    {
                        Console.WriteLine("\tSetContent:" + elem);
                    }
                }
    
                Console.WriteLine();
            }
        }
    }
    
[/code]


CC BY-SA 3.0
2015-05-26T02:18:50.760
10626
2015-05-26T02:41:24.617
2015-05-26T02:41:24.617
7287
7287
10579
answer