« XMLスキーマ定義に従ってXMLファイルを読み込む(1) | トップページ

2012年10月28日 (日)

XMLスキーマ定義に従ってXMLファイルを読み込む(2)

タイトル通り、前回の「XMLスキーマ定義に従ってXMLファイルを読み込む(1)」の続きです。

前回Visual Stuio上のスキーマエディタで多少の制約を入れましたが、この制約条件に相当するプロパティを以下の表にまとめてみました。

プロパティ名 制約内容
AllowDBNull 文字型の場合、DefaultValueがデフォルト値なら空要素を許容するか指定できる
DefaultValue 文字型以外の場合、デフォルト値なら空要素を許容しない様になる
DataType データの型の定義
MaxLength 文字型の場合で最大の文字列長(-1は制限なし)

XMLスキーマが本来持っている豊富な制約条件が用意されている事から比べると、なんともショボい内容ですね。
本当はもっと厳しい制約条件を入れたくなる事は多そうです。例えば、前回テストプログラムに添付したXMLファイルだと、Width,Height 要素は通常正の整数値しか許されるべきではありません。DataTypeプロパティを System.UInt16 と指定する事で、これに近い制約条件に出来ますが、0は許容されてしまいます。

では、せっかくスキーマ定義があるのだから、Visual Studioのスキーマエディタによるビジュアルな修正を諦めて、手修正でもっと厳しい制約条件を入れられないか?と思う人もいるでしょう。もちろん xsd ファイルはテキストファイルなので、テキストエディタ等を使用すれば修正自体は簡単に行えます。ところがスキーマ定義上は許される内容でも、前回紹介したコードを使用する場合、その加えた内容の制約チェックがされません。

また、同様に以前の記事「プロジェクトにXMLスキーマを取り込む場合の注意点」で示した注意事項に違反する例4.の xml ファイルから生成した xsd ファイルを、下記の様に例4の構造に合致するようにスキーマを定義し直したとします。この場合も前回紹介したコードでは、やはりその修正が無効状態となります(スキーマ定義に合わない事が原因で例外が発生します)。

修正前xsdファイル
修正前xsdファイル
修正後xsdファイル
修正後xsdファイル

どうも、xsdコマンドで生成されたスキーマに合致し、VisualStudioのスキーマエディタで変更可能な部分だけが、制約チェックに取り入れられてる様なのです。

では、本来の豊富な制約条件をとりこんだスキーマによるチェックは不可能か?というと、「XMLスキーマ定義に従ってXMLファイルを読み込む(1)」でも最初に紹介したXmlDocumentクラスを使った方のコードではチェック可能です。しかしこれは XmlDocument クラスを使用したからではなく、XmlSchema クラスオブジェクトを取得するのに DataSet クラスを利用していることが原因の様です。

いいかえれば、スキーマ取得に DataSet クラスオブジェクトを利用しなければいいのです。とはいえ、せっかくプロジェクト内にスキーマを取り込んでいるのに、これを使用せずに別途外部ファイルのスキーマ定義を読み込むのは好ましい事ではありませんね。

いろいろ考えた結果、プロジェクトとして取り込んだ xsd ファイルを、リソースとしても格納する事で解決することが出来ました。そのコードは以下の通りです。

/// <summary>
/// 指定したデータセットが持つXMLスキーマに従ってXMLファイルを読み込む。
/// スキーマに従っていない場合は例外が発生する。
/// </summary>
/// <param name="path">XMLファイルのパス</param>
/// <param name="dtSet">データセット</param>
/// <param name="resourceName">リソース名</param>
public static void ReadXmlFile( string path, DataSet dtSet, string resourceName )
{
    if ( !File.Exists( path ) )
    {
        throw new FileNotFoundException( "ファイルが存在しません", path );
    }
    // XML読込み&妥当性検証
    XmlReaderSettings settings = new XmlReaderSettings();
    settings.ValidationType = ValidationType.Schema;
    settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
    settings.Schemas.Add( GetXmlSchema( resourceName ) );
    XmlReader reader = XmlReader.Create( path, settings );
    try
    {
        dtSet.ReadXml( reader );
    }
    finally
    {
        reader.Close();
    }
}

/// <summary>
/// XmlSchemaクラスオブジェクトの取得(リソースxsdファイルから取得)
/// </summary>
/// <param name="resourceName">リソース名</param>
/// <returns>XmlSchemaクラスオブジェクト</returns>
private static XmlSchema GetXmlSchema( string resourceName )
{
    XmlSchema schema = new XmlSchema();
    Assembly thisAssembly = Assembly.GetExecutingAssembly();
    using ( Stream resourceStream = thisAssembly.GetManifestResourceStream( resourceName ) )
    {
        schema = XmlSchema.Read( resourceStream, null );
    }
    return schema;
}

また、ソリューションエクスプローラで取り込んだ xsd ファイルを選択し、そのプロパティのビルドアクションを「埋め込まれたリソース」にして、リソースとして取り込める様に設定しておく必要があります。プロパティの設定

なおリソース名は、既定の名前空間を含めて指定する必要があります。既定の名前空間はプロジェクトプロパティにその指定があります(この場合だと、リソース名は、"SchemaTest.xsdtest4.xsd"となります)。

プロジェクトプロパティ

以上のようにする事で、本来のスキーマ定義に従った形での読み込みが可能となります。
しかし、この方法ではXML読み込みは問題ないのですが、書き込みの方は有効な解決策が見つかってないです。DataSet 経由でXML書き込みを行わず、XmlTextWriter クラスなり、XmlDocument クラスを使用して、一つ一つ書き込んでいく方法しかないと思われます。
それ故、例4.の様な xml ファイルは推奨できない訳です。xml 設計時には十分注意しましょう。

なお、今回も前回のテストプログラムを改造して、この様な xml ファイルを読み込むテストプログラムを作成しています。いつも通り、本記事最後の部分からダウンロード出来ます。

最後になりましたが、前回・今回ともテストプログラムでは使用してないものの、XmlReaderSettings クラスには ValidationEventHandler というイベントハンドラが存在します。これを利用すれば、例外を発生させずにイベント処理で、バリデーション処理を行う事が可能です。

では今回の記事でテストしたソース一式をここに置いておきます。
「SchemaTest2.zip」をダウンロード

« XMLスキーマ定義に従ってXMLファイルを読み込む(1) | トップページ

C# Tips」カテゴリの記事

コメント

コメントを書く

コメントは記事投稿者が公開するまで表示されません。

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/1412251/47534875

この記事へのトラックバック一覧です: XMLスキーマ定義に従ってXMLファイルを読み込む(2):

« XMLスキーマ定義に従ってXMLファイルを読み込む(1) | トップページ