Azureはじめました

Windows Azureで業務システムを組んでみる日記

xmlスキーマからバインド用のクラスを作ってデシリアライズ

Azure関係ねぇw

けど、メッセージング関連でWSDLSOAPを扱う場合があるので先に調査しておく。

xsd.exe ツールを使ってスキーマからクラスを生成

スキーマを適当なフォルダに展開してVisual Studioコマンドプロンプト*1

cd <xsdのフォルダ>
xsd /l:cs /c <xsdファイル>

の形で実行すればxsdのバインド用のクラスが生成される。

これで万事めでたしめでたし。


かとおもいきや、xsd:import 属性が読み込まれてない('A`)

xsd.exeでxsd:importが無視される


Using import or include with xsd.exe - MGBlog

I have recently been playing around with XML schemas and creating class models from them using xsd.exe (see MSDN).

I ran into a stumbling block, however, when I tried to use it to generate classes from a schema that used the statement to import another schema. Although it is not very obvious from the documentation, it does support this, however it does not support the schemaLocation hint.

In order to use schemas that import or include other schemas you need to specify both the base schema and the imported schema on the command line.

For example if you have the following two schemas:

Schema1

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema targetNamespace="http://example.com/XMLSchema1.xsd"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:examp1="http://example.com/XMLSchema1.xsd"
    xmlns:examp2="http://example.com/XMLSchema2.xsd">

    <xs:import namespace="http://example.com/XMLSchema2.xsd" schemaLocation="XMLSchema2.xsd" />

   <xs:element name="example">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="test" type="examp2:myType" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

Schema2

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema targetNamespace="http://example.com/XMLSchema2.xsd"
    xmlns:examp2="http://example.com/XMLSchema2.xsd"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:complexType name="myType">
        <xs:sequence>
            <xs:element name="test" type="xs:string" />
        </xs:sequence>[f:id:twisted0517:20130514193958p:plain]
    </xs:complexType>
</xs:schema>

You would need to use the following command line:

xsd.exe schema1.xsd schema2.xsd /c

I accept no responsibility for the consequences of using the information in this post.

FAQだったのか。
要はxsd.exeに元となるスキーマとimportのschemaLocationで使用されてるスキーマを列挙すればいいと。

Import 要素バインディングのサポート

schemaLocation 属性には、インポートする .xsd ファイルの場所を指定します。ただし、schemaLocation 属性が 要素に現れるとき、Xsd.exe ではこの属性を無視します。その代わり、Xsd.exe ではインポートされるファイルは、追加のコマンド ライン引数として指定されます。
生成されるソース ファイルには、引数として渡された .xsd ファイルのうち、最後のファイルの名前が付けられます。Xsd.exe を呼び出すためのコマンド ラインの例を次に示します。
xsd importedAttribute.xsd attributeRef.xsd /classes /language:CS
生成されるソース ファイルは、attributeRef.cs という名前になります。

MSDNにも出てた。

バインドクラスを使ってXmlを読み込み

xmlのルートクラスが ROOTだったら上で作ったcsの中にROOTクラスがあるはずなので、

using (XmlReader xmlreader = new XmlTextReader(xmlFileName)) {
	XmlSerializer serializer = new XmlSerializer(typeof(ROOT));
	ROOT xmlroot = (ROOT)serializer.Deserialize(xmlreader);

	Console.WriteLine(xmlroot.anyElement);
}

こんな感じに。

anyクラスはどうしたもんか

スキーマにanyノードが含まれていた場合、その要素のエレメントは当然バインディングには乗ってこない。
f:id:twisted0517:20130514193958p:plain*2

XmlSerializer serializer = new XmlSerializer(typeof(ROOT),new Type[]{typeof(BRANCH)});
ROOT xmlroot = (ROOT)serializer.Deserialize(xmlreader);
BRANCH branch = (BRANCH)serializer.Deserialize(xmlroot.any.OuterXml);
Console.WriteLine(branch.anyElement);

でなんとかなるかとおもいきや、xmlroot.any.OuterXmlはAnyノードのXMLだけで、xmlnsの定義が含まれていないためパースエラーになってまう。

(´・ω・`) まう

(以降調査中)

*1:スタート>VisualStudio20xx>Visual Studio Tools>Visual Studioコマンドプロンプト

*2:Any要素がSystem.Xml.XmlElementとしてバインドされる