Skip to main content

This is documentation for Caché & Ensemble. See the InterSystems IRIS version of this content.Opens in a new tab

For information on migrating to InterSystems IRISOpens in a new tab, see Why Migrate to InterSystems IRIS?

Caché オブジェクトへの XML のインポート

この章では、%XML.ReaderOpens in a new tab クラスを使用して、Caché オブジェクトに XML ドキュメントをインポートする方法について説明します。以下のトピックについて説明します。

Note:

使用する任意の XML ドキュメントの XML 宣言にはそのドキュメントの文字エンコードを明記する必要があり、明記しておけば、ドキュメントは宣言どおりにエンコードされるようになります。文字エンコードが宣言されていない場合は、このドキュメント内の前述の “入出力の文字エンコード” で説明されている既定値が使用されます。これらの既定値が正しくない場合は、XML 宣言を修正して、実際に使用されている文字セットを指定するようにします。

任意の XML ドキュメントを読み取って DOM (ドキュメント・オブジェクト・モデル) を返すには、%XML.ReaderOpens in a new tab を使用することもできます。このドキュメントで後述する “XML ドキュメントの DOM 表現” の章を参照してください。

XML リーダの作成の概要

Caché には、XML ドキュメントを読み取り、そのドキュメントの要素に対応する 1 つまたは複数の XML 対応 Caché オブジェクトを作成するツールがあります。基本的な要件は以下のとおりです。

  • そのオブジェクトのクラス定義は、%XML.AdaptorOpens in a new tab を拡張する必要があります。いくつかの例外はありますが、そのオブジェクトが参照するクラスも、%XML.AdaptorOpens in a new tab の拡張とします。"オブジェクトの XML への投影" を参照してください。

    Tip:

    対応する XML スキーマを使用できる場合は、それを使用してクラス (およびサポートするクラス) を生成できます。このドキュメントで後述する “XML スキーマからのクラスの生成” の章を参照してください。

  • XML ドキュメントをインポートするには、%XML.ReaderOpens in a new tab のインスタンスを作成してから、そのインスタンスのメソッドを呼び出します。これらのメソッドでは、XML ソース・ドキュメントを指定し、XML 要素を XML 対応クラスに関連付け、ソースからオブジェクトに要素を読み込みます。

%XML.ReaderOpens in a new tab クラスは、%XML.AdaptorOpens in a new tab によって提供されるメソッドと連携して、以下の処理を行います。

  • Caché SAX インタフェースを使用して、内向きの XML ドキュメントを解析および検証します。この検証では、DTD 検証または XML スキーマ検証のいずれかを実行できます。

  • XML 対応のオブジェクトが XML ドキュメントに含まれる要素と相互に関連するかどうかを判定し、そのドキュメントを読み取ったときにこれらのオブジェクトのメモリ内インスタンスを生成します。

%XML.ReaderOpens in a new tab クラスによって生成されたオブジェクト・インスタンスは、データベースには格納されません。これは、メモリ内のオブジェクトです。データベースにオブジェクトを格納する場合は、%Save() メソッドを呼び出すか (永続オブジェクト)、関連するプロパティ値を永続オブジェクトにコピーし、それを保存します。また、アプリケーションでは、新規のデータを挿入するタイミングと、既存のデータを更新するタイミングを決定する必要があります。%XML.ReaderOpens in a new tab では、これらを区別できません。

次のターミナル・セッションは簡単な例を示しています。この例では、XML ファイルを新しいオブジェクトに読み込んで、そのオブジェクトを調べてから保存します。

GXML>Set reader = ##class(%XML.Reader).%New()
 
GXML>Set file="c:\sample-input.xml"
 
GXML>Set status = reader.OpenFile(file)
 
GXML>Write status
1
GXML>Do reader.Correlate("Person","GXML.Person")
 
GXML>Do reader.Next(.object,.status)
 
GXML>Write status
1
GXML>Write object.Name
Worthington,Jeff R.
GXML>Write object.Doctors.Count()
2
GXML>Do object.%Save()

この例では、次のサンプル XML ファイルを使用しています。

<Person GroupID="90455">
  <Name>Worthington,Jeff R.</Name>
  <DOB>1976-11-03</DOB>
  <Address>
    <City>Elm City</City>
    <Zip>27820</Zip>
  </Address>
  <Doctors>
    <Doctor>
      <Name>Best,Nora A.</Name>
    </Doctor>
    <Doctor>
      <Name>Weaver,Dennis T.</Name>
    </Doctor>
  </Doctors>
</Person>

インポート・メソッドの作成

メソッドの全体的な構造

メソッドは、以下の操作の一部またはすべてをこの順序で実行する必要があります。

  1. %XML.ReaderOpens in a new tab クラスのインスタンスを作成します。

  2. 必要に応じて、このインスタンスの Format プロパティを指定して、インポートするファイルの形式を指定します。“リーダ・プロパティ” を参照してください。

    既定では、XML ファイルはリテラル形式と想定されます。ファイルが SOAP でエンコードされた形式の場合は、そのように明記して、ファイルを正しく読み取れるようにする必要があります。

  3. 必要に応じて、このインスタンスのその他のプロパティを設定します。“リーダ・プロパティ” を参照してください。

  4. ソースを開きます。そのためには、%XML.ReaderOpens in a new tab の次のメソッドのいずれかを使用します。

    • OpenFile() — ファイルを開きます。

    • OpenStream() — ストリームを開きます。

    • OpenString() — 文字列を開きます。

    • OpenURL() — URL を開きます。

      URL で HTTPS を使用する場合、この章で後述の “HTTPS URL のドキュメントへのアクセス” を参照してください。

    いずれの場合も、オプションでメソッドに 2 番目の引数を指定して、Format プロパティの値をオーバーライドすることができます。

  5. このファイルの 1 つ以上の XML 要素の名前と、対応する構造を持つ Caché XML 対応クラスとを相互に関連付けます。共有するには、以下の 2 つの方法があります。

    • Correlate() メソッドを使用します。このメソッドには、以下のシグニチャがあります。

      method Correlate(element As %String, 
                       class As %String, 
                       namespace As %String)
      

      ここで、element は XML 要素名、class は Caché クラス名 (パッケージ付き)、namespace はオプションのネームスペース URI を示します。

      namespace 引数を使用する場合、指定されたネームスペースの中の指定された要素名のみが適合します。namespace 引数を "" と指定すると、Next() メソッドで指定された既定のネームスペースが適合します。namespace 引数を使用しない場合、要素名のみを使用して適合を判断します。

      Tip:

      この章の例では関連付けを 1 つしか示していませんが、Correlate() メソッドを繰り返し呼び出して複数の要素を関連付けることができます。

    • CorrelateRoot() メソッドを使用します。このメソッドには、以下のシグニチャがあります。

      method CorrelateRoot(class As %String)
      

      ここで、class は Caché クラス名 (パッケージ付き) です。このメソッドでは、XML ドキュメントのルート要素を指定したクラスに関連付けることを指定します。

  6. 次のように、クラス・インスタンスをインスタンス化します。

    • Correlate() を使用した場合、ファイルの関連付けられた要素を、一度に 1 つずつループします。ループ内で、Next() メソッドを使用します。このメソッドには、以下のシグニチャがあります。

      method Next(ByRef oref As %ObjectHandle, 
                  ByRef sc As %Status, 
                  namespace As %String = "") as %Integer
      

      ここで、oref はメソッドで作成されたオブジェクト、sc はステータス、namespace はファイルの既定のネームスペースを示します。

    • CorrelateRoot() を使用した場合、Next() メソッドを一度呼び出します。これにより、関連付けられたクラスがインスタンス化されます。

    ファイルの最後に達すると、Next() メソッドは 0 を返します。その後 Next() を再度呼び出すと、ファイルの先頭から再度オブジェクトをループします (指定した相互関係は、そのまま有効です)。

エラーのチェック

前述のセクションで説明したメソッドのほとんどではステータスが返されます。メソッドを実行するたびに返されるステータスを確認し、必要に応じて実行を中止します。

基本的なインポートの例

次のような test.xml と呼ばれる XML ファイルがあるとします。

<Root>
   <Person>
      <Name>Elvis Presley</Name>
   </Person>
   <Person>
      <Name>Johnny Carson</Name>
   </Person>
</Root>

まず、Person のオブジェクト表現である、XML 対応のクラス MyApp.Person を定義します。

Class MyApp.Person Extends (%Persistent,%XML.Adaptor)
{
Parameter XMLNAME = "Person";

Property Name As %String;
}

このファイルを MyAppPerson クラスのインスタンスにインポートするには、次のメソッドを記述します。

ClassMethod Import()
{
    // Create an instance of %XML.Reader
    Set reader = ##class(%XML.Reader).%New()

    // Begin processing of the file
    Set status = reader.OpenFile("c:\test.xml")
    If $$$ISERR(status) {do $System.Status.DisplayError(status)}

    // Associate a class name with the XML element name
    Do reader.Correlate("Person","MyApp.Person")
    
    // Read objects from xml file
    While (reader.Next(.object,.status)) {
        Write object.Name,!
    }
    
    // If error found during processing, show it
    If $$$ISERR(status) {do $System.Status.DisplayError(status)}

}

このメソッドは、以下のタスクを実行します。

  • Caché SAX インタフェースを使用して、入力ファイルを解析します。DTD またはスキーマが指定されている場合、DTD またはスキーマに対するドキュメントの認証も行います。

  • Correlate() メソッドを使用してクラス MyApp.MyPerson を XML 要素 <Person> に関連付けます。<Person> の各子要素は、MyPerson のプロパティになります。

  • 入力ファイルから、<Person> 要素がすべてなくなるまで、1 つずつ読み取ります。

  • 最後に、エラーによりループが終了すると、そのエラーが現在の出力デバイスに表示されます。

上記の説明のとおり、この例ではオブジェクトがデータベースに格納されません。MyPerson は永続オブジェクトなので、以下の行を While ループに追加することで、オブジェクトをデータベースに格納できます。

    Set savestatus = object.%Save()
    If $$$ISERR(savestatus) {do $System.Status.DisplayError(savestatus)}

HTTPS URL のドキュメントへのアクセス

OpenURL() メソッドでは、SSL/TLS を必要とする URL にドキュメントがある場合、以下の手順を実行します。

  1. 管理ポータルを使用して、必要な接続の詳細を格納する SSL/TLS 構成を作成します。詳細は、"Caché セキュリティ管理ガイド" の “Caché での SSL/TLS の使用法” の章を参照してください。

    これは、1 回限りの手順です。

  2. %XML.ReaderOpens in a new tab を使用する場合は、リーダ・インスタンスの SSLConfiguration プロパティを設定します。値については、前の手順で作成した SSL/TLS 構成の名前を指定します。

または、%XML.ReaderOpens in a new tab を使用する場合は、以下の手順も実行します。

  1. %Net.HttpRequestOpens in a new tab のインスタンスを作成します。

  2. そのインスタンスの SSLConfiguration プロパティを、管理ポータルで作成した SSL/TLS 構成の名前に設定します。

  3. %Net.HttpRequestOpens in a new tab のそのインスタンスを、OpenURL() の 3 つ目の引数として使用します。

以下はその例です。

 set reader=##class(%XML.Reader).%New() 
 set httprequest=##class(%Net.HttpRequest).%New() 
 set httprequest.SSLConfiguration="mysslconfigname" 
 set status=reader.OpenURL("https://myurl",,httprequest) 

必要な要素および属性のチェック

既定では、Next() メソッドは、Required とマークされたプロパティに対応する要素と属性の有無をチェックしません。該当する要素と属性の有無をリーダがチェックするようにするには、Next() を呼び出す前に、リーダの CheckRequired プロパティを 1 に設定します。互換性の理由から、このプロパティの既定値は 0 です。

CheckRequired を 1 に設定して Next() を呼び出すときに、インポートされた XML の必須の要素または属性が失われている場合、Next() メソッドは、sc 引数をエラー・コードに設定します。以下はその例です。

SAMPLES>set next= reader.Next(.object,.status)
 
SAMPLES>w next
0
SAMPLES>d $system.Status.DisplayError(status)
 
ERROR #6318: Property required in XML document: ReqProp

予期しない要素および属性の処理

ソース XML ドキュメントには予期しない要素および属性が含まれている場合があるため、%XML.AdaptorOpens in a new tab クラスには、そのようなドキュメントをインポートする際の動作を指定するパラメータが用意されています。詳細は、"オブジェクトの XML への投影" の “特殊なトピック” の章を参照してください。

空の要素および属性のインポート方法の制御

オブジェクトを XML 対応にする場合、NULL 値および空文字列を XML に投影する方法を指定します ("オブジェクトの XML への投影" を参照)。

その指定方法の 1 つは、XML 対応クラスで XMLIGNORENULL"RUNTIME" (大文字/小文字の区別なし) に設定することです。この場合、%XML.ReaderOpens in a new tab を使用して XML ファイルを読み取り、Caché オブジェクトを作成すると、Caché は次のようにリーダの IgnoreNull プロパティの値を使用して、空の要素または属性の処理方法を決定します。

  • リーダの IgnoreNull プロパティが 0 (既定) の場合、要素または属性が空のとき、対応するプロパティには $char(0) が設定されます。

  • リーダの IgnoreNull プロパティが 1 の場合、要素または属性が空のとき、対応するプロパティは設定されず、"" となります。

リーダの IgnoreNull プロパティが有効になるためには、XML 対応クラスで XMLIGNORENULL"RUNTIME" に設定されている必要があります。XMLIGNORENULL に指定可能な他の値は、0 (既定値)、1、および "INPUTONLY" です。"オブジェクトの XML への投影" を参照してください。

例 : IgnoreNull が 0 (既定)

まず、以下のクラスについて考えてみます。

Class EmptyStrings.Import Extends (%Persistent, %XML.Adaptor)
{

Parameter XMLNAME="Test";

///Reader will set IgnoreNull property
Parameter XMLIGNORENULL = "RUNTIME";

Property PropertyA As %String;

Property PropertyB As %String;

Property PropertyC As %String;

Property PropertyD As %String(XMLPROJECTION = "ATTRIBUTE");

Property PropertyE As %String(XMLPROJECTION = "ATTRIBUTE");
}

以下の XML ファイルについても考えてみます。

<?xml version="1.0" encoding="UTF-8"?>
<Test PropertyD="">
  <PropertyA></PropertyA>
  <PropertyB xsi:nil="true" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</Test>

%XML.ReaderOpens in a new tab のインスタンスを作成した場合、それを使用してこのファイルを前のクラスにインポートすると、次のような結果が得られます。

  • PropertyAPropertyD$char(0) になります。

  • その他のすべてのプロパティは "" になります。

例えば、各プロパティを検証しその値を調べて出力を記述するルーチンを記述する場合、次のようになります。

PropertyA is $char(0)
PropertyB is null
PropertyC is null
PropertyD is $char(0)
PropertyE is null

例 : IgnoreNull が 1

前の例のバリエーションとして、リーダの IgnoreNull プロパティを 1 に設定します。この場合、すべてのプロパティは "" になります。

例えば、各プロパティを検証しその値を調べて出力を記述するルーチンを記述する場合、次のようになります。

PropertyA is null
PropertyB is null
PropertyC is null
PropertyD is null
PropertyE is null

入力ドキュメントの前の部分のスキップ

XML ドキュメントは、一連のノードで構成されています。ルート・ノードには 0 という番号が割り当てられ、ルート・ノード内の 1 つ目の要素には 1 という番号が割り当てられ、それ以降の要素にも順番どおりに番号が割り当てられています。読み取りの開始位置となるノードを指定できます。この指定は大きなドキュメントの場合に特に便利です。このためには、リーダの Node プロパティを設定します。この値には整数を指定します。

以下にサンプル・メソッドを示します。

ClassMethod DemoSkippingAhead(nodenumber as %Integer=0)
{
    Set cls="GXML.Person"
    Set filename="c:\GXML.Person.xml"
    Set element="Person"

    // Create an instance of %XML.Reader
    Set reader = ##class(%XML.Reader).%New()

    // Begin processing of the file
    Set status = reader.OpenFile(filename)
    If $$$ISERR(status) {Do $System.Status.DisplayError(status)}

    // Associate a class name with the XML element name
    Do reader.Correlate(element,cls)
    
    //skip ahead in the file
    Set reader.Node=nodenumber

    // Read objects from xml file
    While (reader.Next(.object,.status)) {
        Write "Node number "_reader.Node_" contains "_object.Name,!
    }
}

このメソッドでは、仮の入力ファイル、クラス名、および要素名を使用しています。既定では、このメソッドはファイルの先頭から開始します。以下はその例です。

GXML>d ##class(Readers.ReadFile).DemoSkippingAhead()
Node number 3 contains Emerson,Chad I.
Node number 30 contains O'Rielly,Patricia L.
Node number 63 contains Hanson,Brendan T.
Node number 120 contains Orwell,Tara H.
Node number 195 contains Gold,Elvis O.
Node number 234 contains Klein,Brenda U.
Node number 252 contains Yezek,Kristen Q.
Node number 327 contains Quine,Angelo B.
Node number 378 contains Vivaldi,Milhouse J.
Node number 453 contains Yezek,Vincent D.
Node number 471 contains Xander,Juanita D.
Node number 522 contains Winters,Kyra R.
Node number 555 contains Woo,Michelle J.
Node number 636 contains Ihringer,Yan A.
Node number 654 contains West,Hannah N.
Node number 729 contains Xiang,Bob G.
Node number 762 contains Ximines,Howard H.
Node number 789 contains Quixote,Jocelyn P.
Node number 864 contains Hills,Charles E.
Node number 897 contains Evans,Milhouse R.

代わりに、次のように前方にスキップすることもできます。

GXML>d ##class(Readers.ReadFile).DemoSkippingAhead(700)
Node number 729 contains Xiang,Bob G.
Node number 762 contains Ximines,Howard H.
Node number 789 contains Quixote,Jocelyn P.
Node number 864 contains Hills,Charles E.
Node number 897 contains Evans,Milhouse R.

その他の便利なメソッド

場合によっては、%XML.ReaderOpens in a new tab の以下の追加メソッドを使用する必要がある場合があります。

  • Rewind() メソッドは、XML ソース・ドキュメントの最初から読み取りを再開する必要がある場合に使用します。このメソッドは、すべての相互関係をクリアします。

  • Close() メソッドは、インポート・ハンドラを明示的に閉じる、または削除する必要がある場合に使用します。インポート・ハンドラは自動的に削除されます。このメソッドは、下位互換性のために含まれています。

リーダ・プロパティ

%XML.ReaderOpens in a new tab の以下のプロパティを設定して、メソッドの全体的な動作を制御できます。

  • ドキュメントを解析するときに %XML.ReaderOpens in a new tab のインスタンスがプロセス・プライベート・グローバルを使用するかどうかを指定するには、UsePPGHandler プロパティを使用します。このプロパティが True の場合、インスタンスはプロセス・プライベート・グローバルを使用します。このプロパティが False の場合、インスタンスはメモリを使用します。このプロパティが設定されていない場合 (または空文字列と等しい場合)、インスタンスは既定 (通常はメモリ) を使用します。

    Format プロパティは、XML ドキュメントの全体の形式の指定に使用します。以下の値のいずれかを指定します。

    • 既定値であり、この章のほとんどの例で使用されている "literal"

    • SOAP 1.1 規格で説明されている方法でエンコードされている "encoded"

    • SOAP 1.2 規格で説明されている方法でエンコードされている "encoded12"

    OpenFile()OpenStream()OpenString()OpenURL() の各メソッド内ではこの Format プロパティをオーバーライドできることに注意してください。

    Correlate()Next() を使用していない場合、このプロパティには何の効果もなくなります。

  • Summary プロパティは、リーダに XML 対応オブジェクトの要約フィールドのみのインポートを強制するために使用します。"オブジェクトの XML への投影" で説明したように、オブジェクトの概要は XMLSUMMARY クラス・パラメータによって指定され、コンマ区切りのプロパティのリストとして指定されます。

  • IgnoreSAXWarnings プロパティを使用して、リーダがSAX パーサにより発行される警告を報告すべきかを指定します。

%XML.ReaderOpens in a new tab は、参照中のドキュメントを検証するのに使用できるプロパティも提供します。

  • Document プロパティには、読み取り対象の解析されたドキュメント全体を表現する %XML.DocumentOpens in a new tab のインスタンスが含まれています。%XML.DocumentOpens in a new tab の詳細は、クラス・リファレンスを参照してください。

  • Node プロパティは、XML ドキュメントの現在のノードを表す文字列です。0 はドキュメント、つまり、ルート要素の親を表すことに注意してください。

EntityResolverSAXFlags、および SAXSchemaSpec の各プロパティの詳細は、“SAX パーサの使用法のカスタマイズ” の章を参照してください。SAXMask プロパティが無視されることに注意してください。

相互に関連付けられたオブジェクトをリーダが処理する方法の再定義

%XML.ReaderOpens in a new tab のインスタンスが XML 対応クラスと相互に関連付けられた XML 要素を検出すると、リーダはそのクラスの XMLNew() メソッドを呼び出し、次にこのメソッドは既定では %New() を呼び出します。つまり、リーダが相互に関連付けられた要素を検出すると、リーダは相互に関連付けられたクラスの新しいオブジェクトを作成します。この新しいオブジェクトには、XML ドキュメントから読み取ったデータが格納されます。

この動作をカスタマイズするには、XML 対応クラス (または場合によっては独自のカスタム XML アダプタ) 内の XMLNew() を再定義します。例えば、このメソッドは代わりにそのクラスの既存のインスタンスを開くことができます。次にこの既存のインスタンスは、XML ドキュメントから読み取ったデータを受け取ります。

以下の例では、XML ドキュメントから読み取った新しいデータを使用して既存のインスタンスを更新するように XMLNew() を変更する方法を示しています。

どちらの例でも、簡易化のために、XML ドキュメント内のノードには、そのクラスのエクステント内の ID と比較できる ID が含まれていると想定しています。当然ながら、他の方法で XML ドキュメントを既存のオブジェクトと比較することもできます。

%XML.Reader が XMLNew() を呼び出すとき

参考までに、%XML.ReaderOpens in a new tabXMLNew() メソッドを以下の 2 つの場合に自動的に呼び出します。

  • (外部ドキュメントの) XML 要素を XML 対応クラスに関連付けた後で %XML.ReaderOpens in a new tabNext() メソッドを呼び出すと、%XML.ReaderOpens in a new tabXMLNew() を呼び出します。Next() メソッドはドキュメントから次の要素を取得し、XMLNew() を呼び出して適切なオブジェクトのインスタンスを作成した後で、要素をオブジェクトにインポートします。

  • 同様に、%XML.ReaderOpens in a new tab は関連付けられた XML 要素の任意のオブジェクト値プロパティの XMLNew() を呼び出します。

例 1 : XML 対応クラス内の XMLNew() の変更

まず、以下の XML ファイルについて考えてみましょう。

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <Person>
    <CacheID>4</CacheID>
    <Name>Quine,Maria K.</Name>
    <DOB>1964-11-14</DOB>
    <Address>
      <City>Hialeah</City>
      <Zip>94999</Zip>
    </Address>
    <Doctors>
      <Doctor>
        <Name>Vanzetti,Debra B.</Name>
      </Doctor>
    </Doctors>
  </Person>
...

このファイルは、次の Caché クラスにマッピングします (一部のみ表示)。

Class GXML.PersonWithXMLNew Extends (%Persistent,%Populate,%XML.Adaptor)
{

Parameter XMLNAME="Person";

/// make sure this is the same as the XMLNAME of the property 
/// in this class that is of type %XML.Id
Parameter CACHEID As %String = "CacheID";

Property IdForExport 
As %XML.Id(XMLNAME="CacheID",XMLPROJECTION="ELEMENT") [Private,Transient];

Property Name As %Name;

Property DOB As %Date(FORMAT = 5, MAXVAL = "+$h");

Property Address As GXML.Address;

Property Doctors As list Of GXML.Doctor;

}

このクラスでは、IdForExport プロパティの目的は、このクラスのオブジェクトがエクスポートされるときに Caché 内部 ID を要素 (CacheID) に投影することです (この例では、これによりインポート用の適切なファイルを簡単に生成できます。お使いのクラスにはこのようなプロパティが含まれている必要はありません)。

CACHEID パラメータを使用して、このクラスのオブジェクトをエクスポートする際に Caché ID 用に使用する要素を指定します。これが含まれている理由は、このクラスに追加したカスタマイズされた XMLNew() メソッドの利便性のためだけです。このメソッドの定義は以下のとおりです。

ClassMethod XMLNew(doc As %XML.Document, node As %Integer, 
contOref As %RegisteredObject = "") As GXML.PersonWithXMLNew
{
    Set id=""
    Set tmpnode=doc.GetNode(node)
    Do tmpnode.MoveToFirstChild()
    Do {
        //compare data node to the string given by the CACHEID parameter
        //which indicates the XMLNAME of the ids for this object
        If tmpnode.NodeData=..#CACHEID {
            //get the text from this node; this corresponds to an id in Cache
            Do tmpnode.GetText(.id)}
        } While tmpnode.MoveToNextSibling()
    
    //if there is no id in the given node, create a new object
    If id="" {
        Write !, "Creating a new object..."
        Quit ..%New()}
    
    //open the given object
    Set result=..%OpenId(id)
    
    //if the id doesn't correspond to an existing object, create a new object
    If result=$$$NULLOREF {
        Write !, "Creating a new object..." 
        Quit ..%New()}

    Write !, "Updating an existing object..."

    Quit result
}

%XML.ReaderOpens in a new tab は、XML ドキュメントを読み取ってノードを GXML.PersonWithXMLNew と相互に関連付ける際に、このメソッドを呼び出します。このメソッドは、このクラス内の CACHEID パラメータの値 (CacheID) を参照します。次にこのメソッドは、CacheID という要素を持つドキュメント内のノードを調べて、その値を取得します。

この ID がこのクラスの既存オブジェクトに対応している場合は、このメソッドはそのインスタンスを開きます。そうでない場合は、このメソッドはこのクラスの新しいインスタンスを開きます。どちらの場合でも、開かれたインスタンスは XML ドキュメントで指定されたプロパティを受け取ります。

最後に、次のユーティリティ・クラスには、XML ファイルを開いて %XML.ReaderOpens in a new tab を呼び出すメソッドが含まれています。

Class GXML.DemoXMLNew Extends %RegisteredObject
{

ClassMethod ReadFile(filename As %String = "c:\temp\sample.xml")
{
    Set reader=##class(%XML.Reader).%New()
    Set sc=reader.OpenFile(filename)
    If $$$ISERR(sc) {Do $system.OBJ.DisplayError(sc) Quit  }
    
    Do reader.Correlate("Person","GXML.PersonWithXMLNew")
    
    //loop through elements in file 
    While reader.Next(.person,.sc) {
        Write !,person.Name,!
        Set sc=person.%Save()
        If $$$ISERR(sc) {Do $system.OBJ.DisplayError(sc) Quit  }
    }

    Quit
}

}

上記のメソッドを実行すると、ファイル内の <Person> 要素ごとに以下のいずれかの操作が実行されます。

  • 既存のオブジェクトが開かれて、ファイル内の詳細情報によって更新されてから保存されます。

  • ファイル内の詳細情報が格納された新しいオブジェクトが作成されます。

以下はその例です。

GXML>d ##class(GXML.DemoXMLNew).ReadFile()
 
Updating an existing object...
Zampitello,Howard I.
 
Creating a new object...
Smyth,Linda D.
 
Creating a new object...
Vanzetti,Howard I.
 

例 2 : カスタム XML アダプタ内の XMLNew() の変更

2 つ目の例では、1 つ目の例と同じ操作を実行するカスタム XML アダプタを作成します。このアダプタ・クラスは以下のとおりです。

Class GXML.AdaptorWithXMLNew Extends %XML.Adaptor
{

/// make sure this is the same as the XMLNAME of the property in this class
/// that is of type %XML.Id
Parameter CACHEID As %String = "CacheID";

Property IdForExport 
As %XML.Id(XMLNAME="CacheID",XMLPROJECTION="ELEMENT") [Private,Transient];

ClassMethod XMLNew(document As %XML.Document, node As %Integer, 
containerOref As %RegisteredObject = "") As %RegisteredObject 
[CodeMode=objectgenerator,GenerateAfter=%XMLGenerate,ServerOnly=1]
{
 If %compiledclass.Name'="GXML.AdaptorWithXMLNew" {
  Do %code.WriteLine(" Set id=""""")
  Do %code.WriteLine(" Set tmpnode=document.GetNode(node)")
  Do %code.WriteLine(" Do tmpnode.MoveToFirstChild()")
  Do %code.WriteLine(" Do {")
  Do %code.WriteLine("     If tmpnode.NodeData=..#CACHEID ")
  Do %code.WriteLine("       {Do tmpnode.GetText(.id)}")
  Do %code.WriteLine(" } While tmpnode.MoveToNextSibling() ")
  Do %code.WriteLine(" If id="""" {")
  Do %code.WriteLine("  Write !,""Creating new object...""")
  Do %code.WriteLine("  Quit ##class("_%class.Name_").%New()}")
  Do %code.WriteLine(" set result=##class("_%class.Name_").%OpenId(id)")
  Do %code.WriteLine(" If result=$$$NULLOREF {")
  Do %code.WriteLine("     Write !,""Creating new object...""")
  Do %code.WriteLine("     Quit ##class("_%class.Name_").%New() }")
  Do %code.WriteLine(" Write !,""Updating existing object ...""")
  Do %code.WriteLine(" Quit result")

  }

 QUIT $$$OK
}

}

IdForExport プロパティと CACHEID パラメータによって、サブクラスのオブジェクトがエクスポートされるときに Caché 内部 ID を要素に投影する方法の規約が決定されます。その趣旨は、サブクラス内の IdForExport を再定義する場合は、それに応じて CACHEID を再定義するというものです。

このクラスでは、XMLNew() メソッドはメソッド・ジェネレータです。このクラス (または任意のサブクラス) がコンパイルされると、Caché はここで示しているコードをこのメソッドの本体に書き込みます。"Caché オブジェクトの使用法" の “メソッド・ジェネレータ” の章を参照してください。

以下のクラスは、カスタム・アダプタを拡張します。

Class GXML.PersonWithXMLNew2 
Extends (%Persistent, %Populate, GXML.AdaptorWithXMLNew)
{

Parameter XMLNAME = "Person";

Property Name As %Name;

Property DOB As %Date(FORMAT = 5, MAXVAL = "+$h");

Property Address As GXML.Address;

Property Doctors As list Of GXML.Doctor;

}

前出のサンプル ReadFile メソッドを実行すると、ファイル内の <Person> 要素ごとに、このメソッドは新しいレコードを作成して保存するか、既存のレコードを開いて更新します。

その他の例

ここでは別の例をいくつか挙げます。

柔軟なリーダ・クラス

以下の例は、ファイル名、ディレクトリ、クラス、および要素を入力引数として受け入れる、より柔軟なリーダ・メソッドを示します。このメソッドは、読み取る各オブジェクトを保存します。

Class Readers.BasicReader Extends %RegisteredObject
{

ClassMethod Read(mydir, myfile, class, element)
{
   set reader=##class(%XML.Reader).%New()
   if $extract(mydir,$length(mydir))'="/" {set mydir=mydir_"/"}
   set file=mydir_myfile
   set status=reader.OpenFile(file)
   if $$$ISERR(status) {do $System.Status.DisplayError(status)}

   do reader.Correlate(element,class)

   while reader.Next(.object,.status)
   {
      if $$$ISERR(status) {do $System.Status.DisplayError(status)}
      set status=object.%Save()
      if $$$ISERR(status) {do $System.Status.DisplayError(status)}
      }
}

}

Person オブジェクトで読み取る場合、対応する Address オブジェクトでも自動的に読み取りが行われることに注意してください (Person オブジェクトを保存すると、対応する Address オブジェクトも自動的に保存されます)。これはかなり粗野な方法なので、データの一括ロードにのみ適しています。この方法では、既存のデータとの比較や既存のデータの更新は行われません。

このメソッドを使用するには、読み取る XML ドキュメントとプロジェクションが一致する XML 対応クラスが必要です。MyApp.PersonWithAddress および MyApp.Address という名前の XML 対応クラスがあるとします。また、以下の XML ドキュメントがあるとします。

<?xml version="1.0" encoding="UTF-8"?>
<Root>
   <Person>
      <Name>Able, Andrew</Name>
      <DOB>1977-10-06</DOB>
      <Address>
         <Street>6218 Clinton Drive</Street>
         <City>Reston</City>
         <State>TN</State>
         <Zip>87639</Zip>
      </Address>
   </Person>
</Root>

このファイルのオブジェクトを読み取り、それをディスクに保存するには、以下のような操作を行います。

 set dir="C:\XMLread-these"
 set file="PersonData.txt"
 set cls="MyApp.PersonWithAddress"
 set element="Person"
 do ##class(Readers.BasicReader).Read(dir,file,cls,element)

文字列の読み取り

以下のメソッドは、XML 文字列、クラス、および要素を入力引数として受け入れます。このメソッドは、読み取る各オブジェクトを保存します。

Class Readers.BasicReader Extends %RegisteredObject
{

ClassMethod ReadString(string, class, element)
{
   set reader=##class(%XML.Reader).%New()
   set status=reader.OpenString(string)
   if $$$ISERR(status) {do $System.Status.DisplayError(status)}

   do reader.Correlate(element,class)

   while reader.Next(.object,.status)
   {
      if $$$ISERR(status) {do $System.Status.DisplayError(status)}
      set status=object.%Save()
      if $$$ISERR(status) {do $System.Status.DisplayError(status)}
      }
}

}

このメソッドを使用するには、以下のような操作を行います。

 set cls="MyApp.Person"
 set element="Person"
 do ##class(Readers.BasicReader).ReadString(string,cls,element)
FeedbackOpens in a new tab