XMLはあちこちにあります。角括弧の使用が面倒ですが、XML形式はまだ広く使用されています。設定ファイル、RSSフィード、Officeファイル(.docxの「x」)などがその一部です。XMLファイルを解析するためにPowerShellを使用することは、PowerShellの旅の中で必須のステップです。
このチュートリアルでは、PowerShellがXMLファイルを解析し、検証する方法を紹介します。これにより、XMLデータの整合性を検証し、スクリプトのゲートで誤ったデータを停止するためのツールが提供されます!
前提条件
提示された資料に従うには、次のものが必要です:
- PowerShellバージョン3.0以上。例はWindows PowerShell v5.1で作成されました。
- Notepad++、Visual Studio Code、またはXMLを理解する他のテキストエディタ。
Select-Xml
を使用してPowerShell XML要素を解析する
まず、PowerShellを使用してXMLを解析する最も人気で簡単な方法の1つであるSelect-Xml
について説明します。 Select-Xml
コマンドレットを使用すると、XMLファイルまたは文字列とXPathと呼ばれる「フィルター」を指定して特定の情報を取得できます。
XPathは、要素名の連鎖です。XMLドキュメント内のノードを識別し、ナビゲートするための「パスのような」構文を使用します。
たとえば、コンピュータの一覧が含まれたXMLファイルがあり、このXMLファイルをPowerShellを使用して解析したいとします。各コンピュータには、名前、IPアドレス、レポートに含めるためのInclude
要素など、さまざまな要素があります。
要素は、開始タグと終了タグを持ち、その間にテキストがある場合があります。例えば、
<Name>SRV-01</Name>
PowerShellを使用して、このXMLファイルを解析し、コンピュータの名前を取得したいとします。そのためには、Select-Xml
コマンドを使用することができます。
上記のファイルでは、コンピュータの名前はName要素の内部テキスト(InnerXML)に表示されます。
InnerXMLは、要素のタグの間のテキストです。
コンピュータの名前を見つけるためには、まず適切なXPath(/Computers/Computer/Name
)を指定します。このXPath構文は、Computer
要素の下にあるName
ノードのみを返します。次に、各Name
要素のInnerXMLのみを取得するために、ForEach-Object
ループで各要素のNode.InnerXML
プロパティにアクセスします。
Select-Xml
を使用してXML属性を解析するPowerShellの使用方法
この問題については、別のアプローチでコンピュータ名を見つけましょう。今回は、XMLの要素ではなく、XMLの属性でコンピュータの記述子が表されます。
属性は、
name="SRV-01"
のようなキー/値の部分です。属性は常に開始タグ内に現れ、タグ名の直後に配置されます。
以下は、属性でコンピュータの記述子が表されたXMLファイルです。要素ではなく属性として各記述子を見ることができます。
今回は各記述子が属性であるため、XPathを少し調整してコンピュータ要素のみを見つけます。そして、再びForEach-Object
コマンドレットを使用してname属性の値を見つけます。
そして実際に、同じ結果が出力されます:SRV-01、SRV-02、SRV-03 :

要素または属性を読み取る場合、Select-Xml
の構文は煩雑です。XPathパラメータを使用し、結果をループに渡し、最後にNodeプロパティの下のデータを探す必要があります。
幸いにも、PowerShellにはXMLファイルを読み取り、XMLオブジェクトに変換するより便利で直感的な方法があります。
関連記事: PowerShellデータ型アクセラレータを使用してコーディングを高速化する方法
XML文字列をオブジェクトにキャストする
PowerShellを使用してXMLを解析する別の方法は、そのXMLをオブジェクトに変換することです。これを行う最も簡単な方法は、[xml]
の型アクセラレータを使用することです。
変数名を[xml]
で接頭辞付けすることで、PowerShellは元のプレーンテキストのXMLをオブジェクトに変換し、それを操作できるようにします。
XMLオブジェクトの要素の読み取り
今、$xmlElm
と$xmlAttr
の両方の変数は、XMLオブジェクトであり、ドット記法を使用してプロパティに参照することができます。おそらく、各コンピュータ要素のIPアドレスを見つける必要があります。XMLファイルがオブジェクトであるため、単にIP要素を参照することでこれを行うことができます。
PowerShellバージョン3.0以降では、XMLオブジェクトは、要素の内部テキストを読むために使用される構文と同じ構文で属性値を取得します。したがって、IPアドレスの値は、要素ファイルとまったく同じ構文で属性ファイルから読み取られます。
XML属性の読み取り
まったく同じドット記法を使用することで、XML属性も読み取ることができます。XMLの構造には違いがあるにもかかわらずです。
以下の結果は、両方のファイルから同じデータを取得していることを示しています:

さらに、オブジェクトがメモリにロードされた場合、PowerShell ISEを使用している場合は、IntelliSenseを使用してタブ補完することができます。以下に示すように、例を挙げます。

XMLデータの反復処理
XMLファイルを直接XMLオブジェクトに読み込んだ後([xml]
の型アクセラレータを利用する)、PowerShellオブジェクトの全機能を利用できます。
例えば、include=”true” 属性を持つすべてのコンピュータをXMLファイルからループ処理して、接続状態をチェックする必要があるとします。以下のコードは、その方法を示しています。
このスクリプトは、以下のようなものです。
- ファイルを読み込んでXMLオブジェクトにキャストする。
- 該当するコンピュータを繰り返し処理して、接続状態を取得する。
- 最後に、結果の出力オブジェクトをパイプラインに送信する。
上記のスクリプトの結果は以下の通りです:

XMLスキーマ
前のセクションで、異なる方法でデータセットを表す2つの異なるXMLファイルを見ました。これらの方法はXMLスキーマと呼ばれます。XMLスキーマは、特定の XMLドキュメントの許可される構成要素を定義します:
- 特定のドキュメントに表示できる要素と属性の名前。
- 子要素の数と順序。
- 要素と属性のデータ型。
スキーマは、基本的にXMLの構造を定義します。
XMLデータの検証
XMLファイルは正しい構文を持っているかもしれません(Notepad++のようなエディタではそれがない場合に警告が表示されます)、しかし、そのデータがプロジェクトの要件に一致しないかもしれません。これがスキーマが登場する場所です。XMLデータに頼る場合、定義されたスキーマに従ってデータが有効であることを確認する必要があります。
ランタイムでデータエラーが発見されるのは望ましくありません。スクリプトの途中で500行も深くなっていた場合、すでにファイルシステムやレジストリ上でいくつかの不可逆的な操作を実行しているかもしれません。
では、データが正しいかどうかを事前に確認する方法はありますか?まず、いくつかの可能なエラータイプを見てみましょう。
XMLデータの可能なエラー
一般的に、XMLファイルで見つかるエラーは、メタデータのエラーとデータ自体のエラーのいずれかのカテゴリに属します。
XMLメタデータのエラー
以下のファイルMorePeople.xmlは、構文的には完全に有効です。下記に示すように、ファイルには単一のPeople要素(ルート要素)があり、その内部には3つのPerson要素が含まれています。この構造は完全に受け入れられます。しかし、1つの例外があります。見えますか?
見えなかった場合は心配しないでください。問題は最初の内部要素にあります:
Countryであるべき場所で、誤ってCountyになっています。
XMLデータのエラー
Countryの問題をMorePeople.xmlで修正した後、別の問題が潜んでいます。
メタデータ、つまり要素と属性は問題ありません。では何が問題なのでしょうか?今回の問題は、再び最初のPersonの行で、valuesの1つにあります。誰かがyesをtrueの代替として十分なものだと判断したようですが、以下のようなコードでは、最初の要素を取得するためにtrueではなくyesを探しているため、エラーが発生します:
XMLスキーマの作成
これまでに発生しうるエラーの種類を把握したので、スキーマファイルがどのように役立つのかを示します。最初のステップは、サンプルデータファイルを作成することです。サンプルは最小の例であり、単一の内部要素のみを含んでいる場合があります。上記の例では、次のようなサンプルファイルPeople.xmlを作成しましょう:
次に、以下のようなPowerShell関数を作成し、それをサンプルデータと共に使用して.xsdスキーマを作成します。
この関数をISEまたはお気に入りのPowerShellエディタにコピーし、メモリに読み込んでスキーマを作成します。関数が読み込まれたら、スキーマを作成するためのコードは、次の1行です:
結果は、新しく作成されたスキーマへのパスが表示されます:

スキーマファイルを使用してデータを検証する
上記の結果で指定された場所を確認してください。 .xsdファイルがそこにある場合、バリデーションが機能している正しい方法です。確認ステップでは、以下の関数を使用してください:
関数をメモリに読み込み、2つのエラー例からMorePeople.xmlをバリデーションに使用してください。バリデーションをトリガーするには、以下のコマンドを使用してください:
実際の結果は、MorePeople.xmlの内容に依存します。
2つの例を見てみましょう。 MorePeople.xmlがエラーがない場合、上記の関数はTrue
を返します。

MorePeople.xmlファイルに間違ったデータ(CountryキーがCountyと誤って綴られている)が含まれている場合、関数はいくつかの失敗の詳細を返し、False
を返します。

見てわかるように、詳細な出力で指定されたエラーは非常に情報が豊富です。問題が発生したファイルを指し示し、それが発生した正確なコンポーネントを指し示します。
バリデーションスキーマの微調整
スキーマファイルを見て、さらに改善する方法を見てみましょう。
New-XmlSchema
によって作成されるデフォルトのスキーマは以下の通りです:
上記のデフォルトのスキーマは良いですが、完璧ではありません。実際には、Country属性のタイポをキャッチしています。ただし、スキーマをそのままにしておくと、他の期待値が満たされない場合には、Test-XmlBySchema
のバリデーションでこれらの問題がエラーとして報告されません。これを解決しましょう。
下の表は、検証エラーとは見なされず、Test-XmlBySchema
で見逃されるいくつかのケースを示しています。各行の右側の列には、スキーマに必要な保護を追加するための手動の変更方法が示されています。
A modified version of the schema file, with all protections added, is shown right after the table.
デフォルトのスキーマに設定を追加する-例
Required Behavior | Relevant setting on the schema file |
At least one Person element | Set the minOccurs value of the Person element to 1 |
Make Name, Country and IsAdmin attributes mandatory | Add use=”required” to each of these attributes declaration |
Allow only true/false values for IsAdmin | Set type=”xs:boolean” for the IsAdmin declaration |
Allow only Country names between 3 and 40 characters | Use the xs:restriction (see detailed explanation after the schema text) |
例でIsAdmin属性にブール制約がある場合、その値は小文字 trueまたはfalse
xs:restrictionを使用した文字列の長さの検証
文字列の長さの検証は少し複雑です。上記の変更スキーマの一部として表示されているため、もう少し詳しく説明します。
Country属性の元のスキーマ項目(use=”required”を手動で追加した後)は、以下のようになります:
長さの保護を追加するには、<xs:simpleType>
要素を追加し、その中に<xs:restriction base="xs:string">
を追加します。この制約には、xs:minLength
とxs:minLength
で宣言された必要な制限が含まれています。
これらの変更により、最終的なxs:attribute
の宣言は、1行から8行の巨大なノードになりました:
上記の説明を聞いても頭がくらくらしない場合、検証を実際に見る権利を得ました。それを行うために、意図的に値Canadaを2文字の音節に短縮してみましょう:Ca
短い国名が設定され、MorePeople.xmlが保存されたら、以下の検証コマンドを実行する準備ができます。
そして、結果は実際に複雑なスキーマがその役割を果たしたことを示しています:

XMLスキーマの検証は複雑さを増し、特に正規表現と組み合わせると思いつくパターンをほぼすべて検証することができます。