- ベストアンサー
C#でプロパティを使わずXMLにデータを保存したい
- C#を使っているアプリでデータを保存したい場合、プロパティを使わずにカプセル化する方法を考えています。具体的には、List<Book>を使って1件ずつデータを保存し、読み込む際も1件ずつ読み込んで設定する方法が一般的です。しかし、XmlSerializerを使ったまとめて保存する方法もあります。
- C#でデータを保存する際、プロパティを使わずにカプセル化する方法を探しています。1件ずつデータを保存し、読み込む際も1件ずつ読み込んで設定する方法が標準的な考え方です。もしくは、XmlSerializerを使ってまとめて保存する方法もあります。
- C#のアプリでデータを保存する方法について考えています。プロパティを使わずにカプセル化し、1件ずつデータを保存していく方法が一般的です。また、XmlSerializerを使ってまとめて保存する方法もあります。どちらの方法を使うかは、状況によって異なるでしょう。
- みんなの回答 (2)
- 専門家の回答
質問者が選んだベストアンサー
- ベストアンサー
# 書いている間に別の方から似たようなことをすでに回答されていますが、、そのまま投稿しちゃいます。 プロパティもカプセル化手段の一つですので「プロパティをできるだけ使わずにカプセル化して」というのはちょっとよく分かりません。 読み取り専用プロパティを設けたい場合には、以下のような自動実装プロパティを読み書き別のアクセス権にすると楽です。 public string ISBN { get; private set; } リスト状のデータをXML保存するための標準的な考え方は「用途に応じて適切な手法を用いる」です。 たとえば、出力形式は決まっておらずアプリの都合で保存/復元が出来れば良いのであればシリアライザを用いてまとめて保存しても良いでしょうし、出力するXMLのスキーマを特定の構造にしたいとか、きめ細かいエラー処理をしたいという場合には自前の保存処理を作れば良いでしょう。 シリアライザを利用する場合、XmlSerializerでもよいですけれど、個人的には DataContractSerializer をおすすめします。 privateメンバの保存/復元も出来ますし、メンバがリスト状でも大丈夫です。 こんな風にデータクラスに属性のマーキングをして、 // System.Runtime.Serialization への参照設定をしておく。 [DataContract(Namespace = "")] public class Book { public Book(string isbn, string name) { this.ISBN = isbn; this.Name = name; } [DataMember] public string ISBN { get; private set; } [DataMember] public string Name { get; private set; } } こんな風にリストごと保存/復元をすることが出来ます。(エラー処理等は除く) private void xmlSerialize(string filePath, List<Book> bookShelf) { var confDir = Path.GetDirectoryName(filePath); Directory.CreateDirectory(confDir); var xmlSettings = new XmlWriterSettings(); xmlSettings.Indent = true; using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read)) using (var xmlWriter = XmlTextWriter.Create(fileStream, xmlSettings)) { var dataContractor = new DataContractSerializer(bookShelf.GetType()); dataContractor.WriteObject(xmlWriter, bookShelf); } } private List<Book> xmlDeserialize(string filePath) { using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { var dataContractor = new DataContractSerializer(typeof(List<Book>)); return (List<Book>)dataContractor.ReadObject(fileStream); } } 注意点としては、デシリアライズ時にはコンストラクタが呼び出されないため、データクラスに以下のようなメソッドを加えてシリアライズ前後に処理を行う必要がある場合も、あるでしょう。 [OnDeserializing] private void onDeserializing(StreamingContext context) { // シリアライズ直前の処理 } [OnDeserialized] private void onDeserialized(StreamingContext context) { // シリアライズ後の処理 } 自前で保存を行うならば、XDocumentを利用したLINQ to XMLによる処理をおすすめします。 以下の例では Select で射影して一括で処理していますが、エラー時に場所や値を詳細に出したい場合などは foreach で回したり要素をもっと細かく見る処理にしても良いかと思います。 private void saveLinqToXml(string filePath, List<Book> bookShelf) { var confDir = Path.GetDirectoryName(filePath); Directory.CreateDirectory(confDir); var shelfXml = new XDocument(); shelfXml.Add( new XElement("BookShelf", bookShelf.Select(book => new XElement("Book", new XElement("ISBN", book.ISBN), new XElement("Name", book.Name) ) ) ) ); shelfXml.Save(filePath); } private List<Book> loadLinqToXml(string filePath) { var shelfXml = XDocument.Load(filePath); return shelfXml.Root .Elements("Book") .Select(bookNode => new Book((string)bookNode.Element("ISBN"), (string)bookNode.Element("Name"))) .ToList(); }
その他の回答 (1)
- Yune-Kichi
- ベストアンサー率74% (465/626)
XmlSerializerやDataContractSerializerといったシリアライザを使わないのであれば, System.Xml.Linq.XDocumentやSystem.Xml.XmlDocumentを使って一つずつ要素化していった後保存し, デシリアライズも要素を読み取りながら一つずつ逆変換を行うのが基本です。 XmlSerializerも内部ではそのようにして書き出しています。 使っているのはXDocument/XmlDocumentではなく,XmlWriter/XmlReaderのようですが。 また,シリアライザ自体は汎用品なので,リフレクションを使ってプロパティを取得しています。 ただ,実装がまずいと, ・循環参照が処理できない ・同一のオブジェクトがシリアライズ・デシリアライズを経て別のオブジェクトになる といったことが起きます。 なので,シリアライザを自分で書くことはあまりお薦めできません。 # 最悪,データのシリアライズ・デシリアライズ専用のクラスを作ってしまい,XmlSerializerを使うのもよいかと。
お礼
なるほど!やはりループで回すのが基本なのですね。 シリアライズ専用クラスは動きが理解しやすそうなので一度試してみます! ありがとうございました!
お礼
プロパティもカプセル化手段の一つなのですね! 勉強不足でした。プロパティなどできるだけ外部に公開せずに設計するのが基本と思い込んでいました。 データを与えるときだけは後から変更されないようにコンストラクタで、取得はプロパティで試してみます。 普通に考えたら何のためのプロパティだって話ですもんね。 サンプルコードまで載せて頂いてありがとうございます。 DataContractSerializer 早速勉強してみます。 ありがとうございました。