2012年09月08日
川俣晶の縁側ソフトウェア技術雑記 total 7391 count

Windows ストア アプリでのXMLシリアライズ

Written By: 川俣 晶連絡先

 意外と妙なところでハードルが高かったのでメモ。

 Windows ストア アプリにおいて、XML形式でのシリアライズとデシリアライズ。

using System.Xml;

using System.Runtime.Serialization;

……

[DataContract]

class X

{

    [DataMember]

    public string s1;

}

private void Button_Click_1(object sender, RoutedEventArgs e)

{

    var x = new X();

    x.s1 = "TestData";

    var ds = new DataContractSerializer(typeof(X));

    using (TextWriter sw = new StringWriter())

    {

        using (var xw = XmlWriter.Create(sw))

        {

            ds.WriteObject(xw, x);

        }

        TextBlock1.Text = sw.ToString();

    }

    using (TextReader sw = new StringReader(TextBlock1.Text))

    {

        using (var xw = XmlReader.Create(sw))

        {

            X o = (X)ds.ReadObject(xw, false);

            TextBlock2.Text = o.s1;

        }

    }

}

 意外と引っかかるのはWriteObject, ReadObjectメソッドがXmlReader, XmlWriterオブジェクトを要求すること。このクラスは抽象クラスだからnewできないので、通常はXmlTextReaderやXmlTextWriter(等)をnewするわけですが、Windows ストア アプリではnewできません。しかし、F1を押すと.NET Framework 4.5のリファレンスに飛んで、XmlTextReader/Writerを受け付けると読めてしまいます。結局、このジレンマを解消するには.NET Framework 2.0以降存在する昔ながらのCreateメソッドの利用というわけです。

 しかし、これが吐き出すXMLはプロジェクト依存です。プロジェクト名クラス名が埋め込まれてしまっています。アプリ間連携に使いにくい中身です。

<?xml version="1.0" encoding="utf-16"?><MainPage.X xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/SerializeInWindowsStoreApp001"><s1>TestData</s1></MainPage.X>

依存性のある情報は取ってしまえ §

 というわけで、プロジェクト名とクラス名を取ってみました。

[DataContract(Namespace = "")]

class Y

{

    [DataMember]

    public string s1;

}

private void Button_Click_2(object sender, RoutedEventArgs e)

{

    var y = new Y();

    y.s1 = "TestData";

    var ds = new DataContractSerializer(typeof(Y), "root", "");

    using (TextWriter sw = new StringWriter())

    {

        using (var xw = XmlWriter.Create(sw))

        {

            ds.WriteObject(xw, y);

        }

        TextBlock1.Text = sw.ToString();

    }

    using (TextReader sw = new StringReader(TextBlock1.Text))

    {

        using (var xw = XmlReader.Create(sw))

        {

            Y o = (Y)ds.ReadObject(xw, false);

            TextBlock2.Text = o.s1;

        }

    }

}

 これで実行結果は以下のようになります。メンバーの名前は見えてもそれはいいかな。(これも、属性で変更できる)

<?xml version="1.0" encoding="utf-16"?><root xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><s1>TestData</s1></root>

補足 §

 ちなみに、Createメソッドが返すのは以下の2つの非公開クラスのオブジェクトらしい。

  • System.Xml.XmlWellformedWriter
  • System.Xml.XmlTextReaderImpl

 XmlTextReaderやXmlTextWriterがなくても、これがあれば十分ということらしい。これらが見えなくても、抽象クラスのXmlReader/Writerが見えるのでそれ経由でアクセスできるから良いということかな?

 ちなみに、上の2クラスはWindows ストア アプリで初めて導入されたものではなく、昔からあったらしい。(ドキュメントに載っていないだけで)