XML スキーマでの名前空間の操作

 

大胆なオバサンジョ
Microsoft Corporation

2002 年 8 月 20 日

概要: Dare Obasanjo では、W3C XML スキーマのさまざまな側面と、それらが名前空間の影響を受ける方法について説明します。 対象となるトピックには、targetNamespace、elementFormDefault、attributeFormDefault 属性の適切な使用方法のほか、スキーマ内の要素のインクルード、インポート、再定義が含まれます。 (13ページ印刷)

いくつかの電源ツールのための私の王国

今週末、 最後の記事 で注文した本棚がついに到着しました。 工具を買いに行く代わりに、私はドライバーとハンマーとして使用する古い靴と一緒にそれを置こうとしました。 いくつかの水疱と数時間後、私の本棚は組み立てられ、少し不安定でした。

私は単に「家具を置く」ことで水疱を得たという事実を笑って助けられなかった私の重要な他の人と電話を降りた後、私は私の威厳を取り戻すために私のXMLベースの書籍カタログを続けることにしました。

作成したアプリケーションの有効性をチェックするだけでなく、.NET XML シリアル化のクールな機能を使用して、必要に応じて XML を C# オブジェクトに変換できるように、XML インスタンスのスキーマを作成することにしました。

しかし、まず、インスタンスドキュメントとスキーマの両方の検証を実行するための便利なコマンドラインツールが必要でした。 このプロセスを簡略化するために作成したツールを次に示します。

using System; 
using System.Xml; 
using System.Xml.Schema;

public class XsdValidate{
  
  static XmlSchemaCollection sc = new XmlSchemaCollection();
  static string xsdFile = null; 
  static string xmlFile = null; 
  static string nsUri = null; 

  static string usage = @"Usage: xsdvalidate.exe [-xml <xml-file>] 
   [-xsd <schema-file>] [-ns <namespace-uri>]

Sample:  xsdvalidate.exe -xml t.xml
Validate the XML file by loading it into XmlValidatingReader with
   ValidationType set to auto.  

Sample:  xsdvalidate.exe -xml t.xml -xsd t.xsd -ns ns1
This will validate the t.xml with the schema t.xsd with target namespace 'ns1'

Sample:  xsdvalidate.exe xsd t.xsd -ns ns1
This will validate the schema t.xsd with target namespace 'ns1'";

  public static void ValidationCallback(object sender, ValidationEventArgs args) {

    if(args.Severity == XmlSeverityType.Warning)
      Console.Write("WARNING: ");
    else if(args.Severity == XmlSeverityType.Error)
      Console.Write("ERROR: ");
    
    Console.WriteLine(args.Message); // Print the error to the screen.
  }

  public static void Main(string[] args){

    if((args.Length == 0) || (args.Length %2 != 0)){
      Console.WriteLine(usage);
      return; 
    }
    
     for(int i = 0; i < args.Length; i++) {
       switch(args[i]){

       case "-xsd":     xsdFile = args[++i];     break; 
       case "-xml":     xmlFile = args[++i];     break;     
       case "-ns":    nsUri  = args[++i];     break; 
    
       default:     Console.WriteLine("ERROR: Unexpected argument " + args[i]);    return; 

       }//switch
     }//for

     if(xsdFile != null){       
       sc.ValidationEventHandler += new ValidationEventHandler(ValidationCallback);
       sc.Add( nsUri, xsdFile);
       Console.WriteLine("Schema Validation Completed");
     } 
     
     if(xmlFile != null){
       XmlValidatingReader vr = new XmlValidatingReader(new XmlTextReader(xmlFile));
       vr.Schemas.Add(sc); 
       vr.ValidationType = ValidationType.Schema;
       vr.ValidationEventHandler += new ValidationEventHandler(ValidationCallback);
       
       while(vr.Read());
       Console.WriteLine("Instance Validation Completed");
     }
  }//Main
}//XsdValidate

ターゲット名前空間、スキーマの場所: 違いは何ですか?

私が最初に行わなければならなかったのは、ターゲット名前空間を持つスキーマを作成するかどうかでした。 スキーマのターゲット名前空間は、そのスキーマで検証できる要素と属性の名前空間を指定します。 前の記事のインスタンス ドキュメントでは 名前空間が使用されていたため、実際には、ターゲット名前空間 urn:xmlns:25hoursaday-com:my-bookshelfとして使用するか、名前空間なしでインスタンス ドキュメントを作成するかを選択しました。

私は効果的に新しいマークアップボキャブラリを作成し、名前空間がマークアップボキャブラリをあいまいにするメカニズムを提供することを考えると、私はターゲット名前空間と一緒に行くことにしました。 したがって、スキーマ内のグローバル (または最上位レベル) の要素と属性の宣言は、名前空間の urn:xmlns:25hoursaday-com:my-bookshelf 要素と属性のみを参照します。 スキーマのグローバル型定義にも同じことが当てはまります。 スキーマの最初の行を次に示します。

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="urn:xmlns:25hoursaday-com:my-bookshelf"
    xmlns:bk="urn:xmlns:25hoursaday-com:my-bookshelf">

2 つ目の関連する決定は、XML インスタンス ドキュメントでスキーマの場所を使用することです。 名前空間の schemaLocation 属性と noNamespaceSchemaLocation 名前空間 http://www.w3.org/2001/XMLSchema-instance は、ドキュメントの検証に使用できる 1 つ以上のスキーマへのハードコーディングされた参照を提供するために、インスタンス ドキュメントで使用されます。 参照されるスキーマは、表示される要素のスコープだけでなく、ドキュメント全体に適用されます。 ただし、名前空間名がスキーマのターゲット名前空間と同じである属性または要素が最初に出現した後にスキーマの場所を指定するとエラーになります。

属性には schemaLocation 、スキーマの場所へのターゲット名前空間と URI 参照の 1 つ以上のペアが値として含まれます。 ドキュメントの検証に使用するスキーマのターゲット名前空間と場所を参照するために 属性を使用 schemaLocation するインスタンス ドキュメントのスニペットを次に示します。

<bk:books xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:bk="urn:xmlns:25hoursaday-com:my-bookshelf" 
xsi:schemaLocation="urn:xmlns:25hoursaday-com:my-bookshelf file:///C:/books.xsd" >

noNamespaceSchemaLocation 値は、ターゲット名前空間のないスキーマへの単一の URI 参照です。

メモschemaLocation属性と 属性はnoNamespaceSchemaLocationどちらも検証プロセッサのヒントにすぎません。他の手段を使用してドキュメントのスキーマを指定する場合は無視できます。

インスタンス ドキュメントで または 属性をnoNamespaceSchemaLocation使用schemaLocationしないことを決めました。これは、インターネットに接続できる場合とない可能性がある異なるマシン上のドキュメントを利用することを期待しているため、スキーマへのハードコーディングされた参照は、多くの場合、不適切です。

最初に成功しない場合

XML ブック カタログの形式を再考する際に、ルート要素から 属性を on-loan 削除することにしましたが、残りの形式は変更しないようにすることにしました。 したがって、私の最後の記事から以下の(少し変更された)インスタンスドキュメントを考えると:

<?xml version="1.0" encoding="UTF-8" ?> 
<bk:books xmlns:bk="urn:xmlns:25hoursaday-com:my-bookshelf">
 <bk:book publisher="IDG books" on-loan="Sanjay" >
  <bk:title>XML Bible</bk:title> 
  <bk:author>Elliotte Rusty Harold</bk:author>
 </bk:book>
 <bk:book publisher="QUE">
  <bk:title>XML By Example</bk:title> 
  <bk:author>Benoit Marchal</bk:author>
 </bk:book>
</bk:books>

私は次のスキーマを作成して、それを検証し、そのilkの他のスキーマを検証しました:

<?xml version="1.0" encoding="UTF-8" ?> 
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="urn:xmlns:25hoursaday-com:my-bookshelf"
    xmlns:bk="urn:xmlns:25hoursaday-com:my-bookshelf">

 <xs:element name="books"> 
  <xs:complexType>
   <xs:sequence> 
    <xs:element name="book" type="bk:bookType" maxOccurs="unbounded" />
   </xs:sequence> 
  </xs:complexType>
 </xs:element>

 <xs:complexType name="bookType">
  <xs:sequence>
   <xs:element name="title" type="xs:string" />
   <xs:element name="author" type="xs:string" />
  </xs:sequence>
  <xs:attribute name="publisher" type="xs:string" />
  <xs:attribute name="on-loan" type="xs:string" use="optional" />
 </xs:complexType>

</xs:schema>

驚くべきことに、上記のスキーマは検証ツールで正常に検証されましたが、XML インスタンス ドキュメントを検証しようとすると、複数のエラー メッセージが表示されました。 具体的には、次のコマンドを実行したとき:

xsdvalidate -xml books.xml -xsd books.xsd -ns urn:xmlns:25hoursaday-com:my-bookshelf

私は次の出力を得ました(行番号はトリミングされています):

Schema Validation Completed
ERROR: Element 'urn:xmlns:25hoursaday-com:my-bookshelf:books' has invalid child
element 'urn:xmlns:25hoursaday-com:my-bookshelf:book'. Expected 'book'
ERROR: The 'urn:xmlns:25hoursaday-com:my-bookshelf:book' element is not declared. 
WARNING: Could not find schema information for the attribute 'publisher'. 
WARNING: Could not find schema information for the attribute 'on-loan'. 
ERROR: The 'urn:xmlns:25hoursaday-com:my-bookshelf:title' element is not declared. 
ERROR: The 'urn:xmlns:25hoursaday-com:my-bookshelf:author' element is not declared. 
ERROR: The 'urn:xmlns:25hoursaday-com:my-bookshelf:book' element is not declared. 
WARNING: Could not find schema information for the attribute 'publisher'. 
ERROR: The 'urn:xmlns:25hoursaday-com:my-bookshelf:title' element is not declared. 
ERROR: The 'urn:xmlns:25hoursaday-com:my-bookshelf:author' element is not declared. 
Instance Validation Completed

最初のエラーメッセージは、私に何が間違っていたかについての手がかりを与えました。 スキーマの最初の行を次のように簡単に変更しました。

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="urn:xmlns:25hoursaday-com:my-bookshelf"
    xmlns:bk="urn:xmlns:25hoursaday-com:my-bookshelf"
 elementFormDefault="qualified" >

私が再ランすると、ツールは次の出力を得ました:

   Schema Validation Completed
Instance Validation Completed

エラー メッセージの外観は、スキーマにローカル要素の宣言が含まれており、要素の属性xs:schemaelementFormDefault既定値が "非修飾" であることが原因でした。 これらの概念については、次のセクションで詳しく説明します。

グローバルに考え、ローカルで行動する

要素の子として表示される要素と属性の xs:schema 宣言は、グローバル宣言と見なされます。 その他のすべての要素宣言と属性宣言は、ローカル宣言と見なされます。 ローカル要素または属性宣言は、 属性を介してグローバル宣言を ref 参照できます。これにより、ローカル宣言がグローバル宣言と実質的に同じになります。 グローバル宣言の名前は、ローカル宣言とは別のシンボル空間に配置されます。 また、ローカル宣言の名前のスコープは、外側の型定義のスコープです。 したがって、スキーマには、同じ名前を持ち、名前の競合が発生しない要素または属性の宣言を含む 2 つ以上の型定義を含めることができます。 これは、1 つ以上のローカル要素または属性と同じ名前を共有するグローバル要素または属性の場合も同様です。

ローカル要素宣言とグローバル要素への参照の両方で、出現制約を使用して カーディナリティ を表すことができます。 出現制約は、 属性と maxOccurs 属性をminOccurs使用して指定します。

ローカル要素とグローバル要素を使用するサンプル スキーマと、グローバル要素宣言への参照を次に示します。

<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
 targetNamespace="http://www.example.com"
 xmlns="http://www.example.com">

 <!-- global element declaration --> 
 <xs:element name="language" type="xs:string" />

 <!-- complex type with local element declaration -->  <xs:complexType name="sequenceOfLanguages" >  
  <xs:sequence>
   <xs:element name="language" type="xs:NMTOKEN" maxOccurs="unbounded" />
  </xs:sequence>
 </xs:complexType>

 <!-- complex type with reference to global element declaration -->
  <xs:complexType name="sequenceOfLanguages2" >  
  <xs:sequence>
   <xs:element ref="language" maxOccurs="10" />
  </xs:sequence>
 </xs:complexType>
</xs:schema>

既定では、グローバル要素にはスキーマのターゲット名前空間と同じ名前空間名が付けられますが、ローカル要素には名前空間名はありません。 つまり、上記のスキーマの場合、グローバルlanguage要素宣言は、名前空間名として を持つhttp://www.example.comインスタンス ドキュメント内の要素を検証languageできます。 ただし、型の 要素の language ローカル宣言では sequenceOfLanguages 、名前空間名のないインスタンス ドキュメント内の要素のみを検証 language できます。

要素の子 xs:schema として発生する型定義は、グローバル型定義と見なされます。 グローバル型定義には名前が必要です。 グローバル型定義は、属性と要素宣言の属性、または派生型の属性をbase使用してtype参照できます。 型定義は、要素または属性宣言の一部としてローカルに作成することもできます。その場合、名前を持つ必要はなく、 匿名型と見なされます。

匿名型定義とグローバル型定義、およびグローバル型定義への参照を使用するサンプル スキーマを次に示します。

<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
 targetNamespace="http://www.example.org" 
 xmlns:tns="http://www.example.org">

 <!-- element declaration that references a global complex type --> 
 <xs:element name="languages" type="tns:sequenceOfLanguages" />

 <!-- global complex type definition -->
 <xs:complexType name="sequenceOfLanguages" >  
  <xs:sequence>
   <xs:element name="language" type="xs:NMTOKEN" maxOccurs="unbounded" />
  </xs:sequence>
 </xs:complexType>

 <!-- attribute declaration with anonymous simple type  -->
 <xs:attribute name="positiveDecimal">
  <xs:simpleType>  
  <xs:restriction base="xs:decimal">
   <xs:minExclusive value="0"  />
  </xs:restriction>
 </xs:simpleType>
 </xs:attribute>
</xs:schema>

型定義、要素宣言、および属性宣言は、名前に対して同じシンボル空間を共有しません。 そのため、型定義、グローバル宣言、およびローカル宣言が 1 つの名前を共有するスキーマを持つことができます。 この方法は非常にわかりにくいので、避ける必要があります。

資格はありますか?

最後のセクションでは、既定ではグローバル宣言が名前空間名を持つ要素または属性を検証し、ローカル宣言では名前空間名のない要素または属性を検証することを説明しました。 名前空間名を持つ要素または属性を記述するために使用される用語は、 名前空間修飾です

ローカル宣言が名前空間の修飾された要素と属性を検証するかどうかに関して、既定の動作をオーバーライドできます。 要素には xs:schemaelementFormDefault 属性と attributeFormDefault 属性があり、スキーマ内のローカル宣言で名前空間修飾要素と属性をそれぞれ検証するかどうかを指定します。 いずれかの属性の有効な値は 、修飾 され、 非修飾です。 両方の属性の既定値は 非修飾です

ローカル要素の form 属性と属性宣言を使用して、 要素の 属性と attributeFormDefault 属性のelementFormDefault値をxs:schemaオーバーライドできます。 これにより、インスタンス ドキュメント内の要素と属性の検証がローカル宣言に関連して動作する方法をきめ細かく制御できます。

次の例では、および form 属性を使用してローカル宣言を elementFormDefaultattributeFormDefault制御する方法を示します。

Schema 有効なインスタンス ドキュメント
<?xml version="1.0" encoding="UTF-8" ?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

targetNamespace="http://www.example.org"

xmlns:tns="http://www.example.org">

<!-- elementFormDefault and attributeFormDefault

have value unqualified in this schema -->

<xs:element name="root" type="tns:rootType" />

<xs:complexType name="rootType" >

<xs:sequence>

<xs:element name="child1" type="xs:string" maxOccurs="2" />

<xs:element name="child2" type="xs:string" form="qualified" />

</xs:sequence>

<xs:attribute name="attr" type="xs:string" />

</xs:complexType>

</xs:schema>

<?xml version="1.0" encoding="UTF-8" ?>

<ex:root xmlns:ex=http://www.example.org attr="unqualified">

<child1>I am not namespace qualified</child1>

<child1>Neither am I</child1>

<ex:child2>I am namespace qualified</ex:child2>

</ex:root>

<?xml version="1.0" encoding="UTF-8" ?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

targetNamespace="http://www.example.org"

xmlns:tns="http://www.example.org"

elementFormDefault="qualified"

attributeFormDefault="qualified">

<xs:element name="root" type="tns:rootType" />

<xs:complexType name="rootType" >

<xs:sequence>

<xs:element name="child1" type="xs:string" maxOccurs="2" />

<xs:element name="child2" type="xs:string" />

</xs:sequence>

<xs:attribute name="attr1" type="xs:string" />

<xs:attribute name="attr2" type="xs:string" form="unqualified"/>

</xs:complexType>

</xs:schema>

<?xml version="1.0" encoding="UTF-8" ?>

<ex:root xmlns:ex="http://www.example.org"

ex:attr1="qualified" attr2="unqualified">

<ex:child1>I am namespace qualified</ex:child1>

<ex:child1>So am I</ex:child1>

<ex:child2>Me too</ex:child2>

</ex:root>

全体がその部分の合計より大きい

スキーマは、検証中に 1 つの論理スキーマにアセンブルされる複数のスキーマから構成できます。 W3C XML スキーマには、外部スキーマからターゲット スキーマ ドキュメントにグローバル宣言と型定義をアセンブルするために使用できる 3 つの要素が用意されています。 3 つの要素は、xs:include xs:import、および xs:redefineです。

xs:includeは、ターゲット名前空間を持たないスキーマ、または外側のスキーマと同じターゲット名前空間を持つスキーマから定義を取り込む場合に使用されます。 xs:import は xs:include に似ていますが、インポートされたスキーマには外側のスキーマとは異なるターゲット名前空間が必要であるという点が異なります。 インポートされたスキーマに名前空間名がない場合、外側のスキーマにはターゲット名前空間が必要です。

次の例は、インポートされた宣言が外側のスキーマで参照される方法と、名前空間の修飾がローカル宣言にどのように影響するかを示しています。

<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
 targetNamespace="http://www.example.org" 
 xmlns:tns="http://www.example.org"
xmlns:imp="http://www.import.org">
<xs:import namespace="http://www.import.org" schemaLocation="file:///c:/import.xsd"
  <xs:element name="root" type="imp:rootType" />
</xs:schema>

Imported Schema: import.xsd
<xs:schema xmlns:xs=http://www.w3.org/2001/XMLSchema
targetNamespace="http://www.import.org" elementFormDefault="qualified"> 

<xs:complexType name="rootType" >  
  <xs:sequence>
   <xs:element name="child1" type="xs:string" maxOccurs="2" />
   <xs:element name="child2" type="xs:string"/>
  </xs:sequence>
</xs:complexType>

</xs:schema>

Instance Document: [when elementFormDefault="qualified" in import.xsd]
<?xml version="1.0" encoding="UTF-8" ?>
<ex:root xmlns:ex=http://www.example.org xmlns:imp="http://www.import.xsd">
 <imp:child1>I am from imported schema </imp:child1>
 <imp:child1>So Am I </imp:child1>
 <imp:child2>Me too </imp:child2>
</ex:root>

Instance Document: [when elementFormDefault="unqualified" in import.xsd]
<?xml version="1.0" encoding="UTF-8" ?>
<ex:root xmlns:ex="http://www.example.org">
 <child1>Don't know where I come from </child1>
 <child1>neither do I </child1>
 <child2>Me too </child2>
</ex:root>

xs:redefine は、基本的に 2 つのタスクを実行することで、型の再定義に使用されます。 1 つ目は、別のスキーマ ドキュメントから宣言と定義を取り込み、現在のターゲット名前空間の一部として使用できるようにすることで、 として xs:include 機能することです。 含まれる宣言と型は、同じターゲット名前空間を持つスキーマからであるか、名前空間を持たない必要があります。 第 2 に、型派生と同様の方法で型を再定義し、古い定義を置き換える新しい定義を使用できます。

xs:includexs:import、xs:redefine の例と詳細については、W3C XML スキーマ 入門を参照してください。

カルマ カメレオン

ターゲット名前空間のないスキーマは、多くの場合 、カメレオン スキーマと呼ばれます。 カメレオン スキーマは、ターゲット名前空間に関係なく任意のスキーマに含めることができます。これにより、カメレオン スキーマの型定義と宣言によって、外側のスキーマのターゲット名前空間が取得されます。

カメレオン スキーマを使用する例を次に示します。

もっと読む

Dare Obasanjo は Microsoft の WebData チームのメンバーであり、特に、.NET Framework、Microsoft XML Core Services (MSXML)、Microsoft Data Access Components (MDAC) の System.Xml 名前空間と System.Data 名前空間内のコンポーネントを開発しています。

この記事に関する質問やコメントは、GotDotNet の Extreme XML メッセージ ボード に自由に投稿できます。