C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[4]: DataContractSerializerでの書き込み


(過去ログ 110 を表示中)

[トピック内 5 記事 (1 - 5 表示)]  << 0 >>

■65146 / inTopicNo.1)  DataContractSerializerでの書き込み
  
□投稿者/ arnord (1回)-(2013/02/07(Thu) 16:57:00)

分類:[C#] 

2013/02/07(Thu) 17:11:17 編集(投稿者)
初心者です。
VisualStudio C# 2010 Expressを使っています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.IO;

namespace DataContractSerializerTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string fileName = Path.Combine(Environment.CurrentDirectory, "filename.config");
            MySettings setting = new MySettings();
            //書き込むオブジェクトに適当に値を設定
            for (int i = 0; i < 10; i++)
            {
                setting.BoolDictionary.Add(i.ToString(), i % 2 == 0);
                setting.StringDictionary.Add(i.ToString(), i.ToString() + "番目");
            }

            DataContractSerializer serializer1 = new DataContractSerializer(typeof(MySettings));
            //ファイルに書き込む
            using (var fs1 = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write))
            {
                //シリアル化し、XMLファイルに保存する
                serializer1.WriteObject(fs1, setting);
            }

            DataContractSerializer serializer2 = new DataContractSerializer(typeof(MySettings));
            //ファイルを開く
            using (var fs2 = new FileStream(fileName, FileMode.Open, FileAccess.Read))
            {
                //XMLファイルから読み込み、逆シリアル化する
                var deseriarize = serializer2.ReadObject(fs2);
                //とりあえずキャスト
                var readsetting = (MySettings)deseriarize;
                //表示を行う
                foreach (var pair in readsetting.BoolDictionary)
                {
                    Console.WriteLine(pair.Key.ToString() + " " + pair.Value.ToString());
                }
                foreach (var pair in readsetting.StringDictionary)
                {
                    Console.WriteLine(pair.Key.ToString() + " " + pair.Value.ToString());
                }
            }
        }
        /// <summary>
        /// 設定を保存する型
        /// </summary>
        [Serializable()]
        [DataContract()]
        public class MySettings
        {
            public MySettings()
            {
                this.StringDictionary = new Dictionary<string, string>();
                this.BoolDictionary = new Dictionary<string, bool>();
            }
            [DataMember()]
            public Dictionary<string, string> StringDictionary { get; set; }
            [DataMember()]
            public Dictionary<string, bool> BoolDictionary { get; set; }

        }
    }
}

上記のようにConfigファイルに書き込みを実行したのち、たとえば

setting.StringDictionary.Add(i.ToString(), i.ToString() + "番目");

の行をコメントアウトして再度実行すると読み込みの時にエラーが起こります。
これは当然と言えばまぁ当然なんですが、そのあとConfigファイルをVisualStudioなど開いてみると
明らかにXMLが崩れていて、それが原因で開くことに失敗しているようです。
書き込みを行う前に

File.Delete(fileName);

を追加することにより解決はしますがこれ以外に何か解決法はないでしょうか?
せめて保存する型の定義に変更があってもXMLが崩れないようにする方法はないのでしょうか?

引用返信 編集キー/
■65150 / inTopicNo.2)  Re[1]: DataContractSerializerでの書き込み
□投稿者/ とっちゃん (45回)-(2013/02/07(Thu) 18:18:56)
とっちゃん さんの Web サイト
No65146 (arnord さん) に返信

FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write))

ここで、ファイルがあれば、オープン、なければ作成としてファイルを用意しています。
FileMode.Create にして、保存するときに今までのデータは破棄するように指示すれば解決すると思いますがどうでしょう?

その代わり、追記ではないため、既にファイルがある場合古いデータは消え去ります。

引用返信 編集キー/
■65169 / inTopicNo.3)  Re[2]: DataContractSerializerでの書き込み
□投稿者/ arnord (2回)-(2013/02/08(Fri) 19:51:08)
No65150 (とっちゃん さん) に返信

回答ありがとうございます。

あ、確かにその通りですね。解決します。
しかし、削除しているのとは根本的には変わらないですね。

すでにあるファイルに追記ができる形が望ましいのです・・・
引用返信 編集キー/
■65184 / inTopicNo.4)  Re[3]: DataContractSerializerでの書き込み
□投稿者/ とっちゃん (46回)-(2013/02/12(Tue) 10:30:43)
とっちゃん さんの Web サイト
No65169 (arnord さん) に返信
> あ、確かにその通りですね。解決します。
> しかし、削除しているのとは根本的には変わらないですね。
>
> すでにあるファイルに追記ができる形が望ましいのです・・・

シリアライザなどのシーケンシャルなデータアクセス形式を使ってXMLファイルに追記する形で保存したい場合は
既に存在しているファイル内の情報を読み取り、それをプログラム的に合成してから改めてXMLファイルとして保存する必要があります。

シリアライザを使わず、XMLDOMObjectとして読み書きするのであれば、任意箇所に挿入することはできますが

おそらく、今回のような場合は、
1.保存済みのファイルがあれば、読み取る
2.1で読んだ(あるいは新規に用意した)オブジェクトにデータを追加する
3.保存する
という流れをとる必要があると思います。
#自前でシリアライズをやる場合は必ず必要な処理と思ってよい


あと、
string fileName = Path.Combine(Environment.CurrentDirectory, "filename.config");
ですが、

起動時の CurrentDirectory は、必ず同じとは限りません。
また、仮に CurrentDirectory をプログラムのあるフォルダにしたとしても
インストール条件などによっては、書き込みできません。
開発環境のプログラムの出力先フォルダなら問題ありません。
ですが、Program Filesフォルダおよびそのサブフォルダは
インストールや更新以外での読み書きは原則として行ってはいけません。

なんとなく...ですが、
プロジェクトのプロパティにある設定(Propetires.Settings.Default)の利用も検討してもよい気がします。
ま、何となくそういう気がしただけなのでわかりませんけど。

引用返信 編集キー/
■65278 / inTopicNo.5)  Re[4]: DataContractSerializerでの書き込み
□投稿者/ arnord (3回)-(2013/02/15(Fri) 22:43:53)
No65184 (とっちゃん さん) に返信
> おそらく、今回のような場合は、
> 1.保存済みのファイルがあれば、読み取る
> 2.1で読んだ(あるいは新規に用意した)オブジェクトにデータを追加する
> 3.保存する
> という流れをとる必要があると思います。
> #自前でシリアライズをやる場合は必ず必要な処理と思ってよい

な、なるほど・・・
思ったより面倒なんですね。

> あと、
> string fileName = Path.Combine(Environment.CurrentDirectory, "filename.config");
> ですが、
>
> 起動時の CurrentDirectory は、必ず同じとは限りません。
> また、仮に CurrentDirectory をプログラムのあるフォルダにしたとしても
> インストール条件などによっては、書き込みできません。
> 開発環境のプログラムの出力先フォルダなら問題ありません。
> ですが、Program Filesフォルダおよびそのサブフォルダは
> インストールや更新以外での読み書きは原則として行ってはいけません。


そこまでは考えてませんでした。
なんかINIみたいな感じでテキトーに使えるかなーとか思ってました。


> なんとなく...ですが、
> プロジェクトのプロパティにある設定(Propetires.Settings.Default)の利用も検討してもよい気がします。
> ま、何となくそういう気がしただけなのでわかりませんけど。

調べてみます。
ありがとうございました。

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -