XML などのプロパティの中の「name=”value”」みたいなやつの値をCSV とかに抽出したい。
Contents
例えばこんなやつの「name=」の値を取り出して一覧にしたい。
例えばこんなやつ
<Configuration xmlns:xlink="http://www.w3.org/1999/xlink">
<Property name="ie.ldap.serverHostName" overridable="true"
targetFile="codebase/WEB-INF/ieStructProperties.txt"
value="test.bbtec.net"/>
<Property name="installedProduct.location.Apache" overridable="true"
targetFile="codebase/WEB-INF/ieWebServerLocation.txt"
value="C:\ptc\HTTPServer"/>
<Property name="ie.ldap.managerPw" overridable="true"
targetFile="codebase/WEB-INF/ieStructProperties.txt"
value="encrypted.ie.ldap.managerPw"/>
<Property name="wt.webapp.name" overridable="true" targetFile="codebase/wt.properties"
value="Windchill"/>
<Property name="wt.rmi.server.hostname" overridable="true"
targetFile="codebase/wt.properties"
value="test.bbtec.net"/>
</Configuration>
Bing のサンプルコード
Bing 氏によると、Get-Content でXML をプロパティ単位で取得できるとのこと。
# XMLファイルのパス
$xmlFilePath = "path\to\your\file.xml"
# CSVファイルのパス
$csvFilePath = "path\to\your\output.csv"
# XMLファイルを読み込む
[xml]$xml = Get-Content -Path $xmlFilePath
# 必要なプロパティを抽出する
$data = foreach ($item in $xml.Root.Element) {
[PSCustomObject]@{
Property1 = $item.Property1
Property2 = $item.Property2
# 他のプロパティも追加できます
}
}
# データをCSVファイルにエクスポートする
$data | Export-Csv -Path $csvFilePath -NoTypeInformation
$xml.Root.Element が下記のXML を前提とた場合の値になっています。
<Root>
<Element>
<Property1>Value1</Property1>
<Property2>Value2</Property2>
</Element>
<Element>
<Property1>Value3</Property1>
<Property2>Value4</Property2>
</Element>
</Root>
実行結果がこんな感じになります。
Property1 Property2
--------- ---------
Value1 Value2
Value3 Value4
試してみた結果
上記の例ではプロパティがタグで挟んでありましたが、name=**** でもプロパティとして取得されていたので、「例えばこんなやつ」の場合、下記のように書けます。
# XMLファイルを読み込む
[xml]$xml = Get-Content -Path $xmlFilePath;
# 各Element要素をループしてプロパティを表示
foreach ($item in $xml.Configuration.Property) {
$data += @(
[PSCustomObject]@{
Name=$item.name;
Override=$item.overridable;
Value=$item.value;
}
)
}
# 書き出し
$data | ForEach-Object { $_ } | Export-Csv -Path $csvFilePath -NoTypeInformation;
最初、正規表現を使って抜き出ししようと思ってましたが、理解するとこちらの方がはるかに楽でした。
正規表現で頑張ってみた!
参考までに、無駄な思考錯誤をいかに置いておきます。
最初「$xml.Root.Element」が理解できていなくて、Bing 氏の提案をあきらめて、
正規表現で頑張ってました。
正規表現でダブルクォーテーションで囲まれた値は「”([^”]*)”」
Bing 氏による解説
正規表現の構造
":
ダブルクォーテーションを表します。
これはリテラル文字として、文字列の開始と終了を示します。
([^"]*):
これはキャプチャグループです。キャプチャグループは、括弧 () で囲まれた部分で、マッチした内容を後で参照するために使用されます。
[^"]:
これは否定文字クラスです。[] 内の ^ は否定を意味し、" 以外の任意の文字を表します。
つまり、ダブルクォーテーション以外の任意の文字にマッチします。
*:
これは量指定子で、直前の要素(この場合は [^"])が0回以上繰り返されることを意味します。
つまり、ダブルクォーテーション以外の任意の文字が0回以上続く部分にマッチします。
全体の意味
"([^"]*)":
最初の " はダブルクォーテーションの開始を示します。
([^"]*) はダブルクォーテーション以外の任意の文字が0回以上続く部分をキャプチャします。
最後の " はダブルクォーテーションの終了を示します。
例
例えば、文字列 This is a "test" string with "multiple" quoted "words". に対してこの正規表現を適用すると、以下の部分がマッチします:
"test"
"multiple"
"words"
そして、キャプチャグループ ([^"]*) によって、以下の文字列が抽出されます:
test
multiple
words
あれこれした結果こんな感じになりました。
コメントアウトしてあるような感じで書くと対象の値が複数の時、全部出力することもできます。
今回は「name」属性だけに絞り込む感じなので、2段階でマッチを評価しています。
配列をCSV に変換する部分は、文字列のlength をCSV に出力してしまう現象を回避した結果こんな感じになりました。
[PSCustomObject] の定義が配列の定義に入れ子になっている感じです。
$xmlFilePath = "site.xconf";
$csvFilePath = "output.csv";
$pattern = 'name="([^"]*)"';
$pattern2 = '"([^"]*)"';
$data = @();
Get-Content -Path $xmlFilePath | ForEach-Object{
if(($_ -match "name=" )){
$matches = [regex]::Matches($_, $pattern);
$matches = [regex]::Matches($matches.Value, $pattern2);
$data += @( [PSCustomObject]@{ name = $matches.Value.Replace('"','') } );
#foreach ($match in $matches) {
# $match.Groups.Value
#}
}
}
$data | ForEach-Object { $_ } | Export-Csv -Path $csvFilePath -NoTypeInformation;
(Visited 15 times, 1 visits today)