2種類のパース方法
simplexml_load_file()
- 特徴: シンプルな記述で、階層構造の要素や値をすぐ取得できる。
- 向いているケース:
- 階層が深くても、要素名が分かっていて直接アクセスしたい場合。
- XPathも使えるが、複雑な操作や属性の細かい制御はやや苦手。
DOMDocument
- 特徴: XMLのノードを細かく操作できる。属性や名前空間、ノードの追加・削除なども柔軟。
- 向いているケース:
- 名前空間や属性の扱いが多い場合。
- ノードの追加・削除・編集など、XML構造自体を変更したい場合。
- 複雑なXPathや詳細な検索が必要な場合。
結論
値の取得や簡単な検索なら simplexml_load_file() が手軽でおすすめ
XMLの編集や複雑な検索・名前空間の厳密な扱いが必要なら DOMDocument が向いています
階層が深いだけなら、まずは simplexml_load_file
XMLをSimpleXMLElementでパース後
要素までの呼び方
目的の項目までの指定方法は、一般的に「パス(path)」や「階層(hierarchy)」と呼ばれる
XMLの場合:「ノードパス」「XMLパス」「階層」などと呼ばれます。XPath(エックスパス)という正式なパス指定方法もあります。
JSONの場合:「パス」「プロパティパス」「キーの階層」などと呼ばれます。JSONPath(ジェイソンパス)というパス指定方法もあります。
2種類のパース、中身
1 2 3 4 5 6 |
//ファイルを一度文字列として読む $xml = file_get_contents('20250830193825_0_VPFD51_030000.xml'); $obj = simplexml_load_string($xml); //直接パース $obj = simplexml_load_file('20250830193825_0_VPFD51_030000.xml'); |
どちらの方法でも**最終的に $obj
は同じ型(SimpleXMLElementオブジェクト)**になります。
XPath
目的の要素を簡単に検索するためのメソッドとして XPath を使うことができます。
たとえば、$obj->xpath(‘パス’) で目的の要素を配列で取得できます。
xpath() メソッドは SimpleXMLElement オブジェクトで利用できます。
1 2 3 4 5 6 7 8 9 |
//例:Type要素をすべて取得する場合 <?php // ...existing code... $types = $obj->xpath('//Type'); foreach ($types as $type) { echo $type . "\n"; } // ...existing code... |
属性付き要素の指定
refID=”1″ のような属性を持つ要素をXPathで指定する場合、以下のように書きます。
1 2 3 4 5 6 7 |
<?php // ...existing code... $waveHeightParts = $obj->xpath('//WaveHeightForecastPart[@refID="1"]'); foreach ($waveHeightParts as $part) { echo $part->asXML() . "\n"; } // ...existing code... |
すべての WaveHeightForecastPart 要素のうち、refID 属性が “1” のものを取得します。
このように、[@属性名=”値”] で属性値を指定できます。
名前空間
XMLに名前空間(xmlns属性)がある場合、SimpleXMLElementのxpathはそのままでは要素を見つけられません。
<Report xmlns=”http://xml.kishou.go.jp/jmaxml1/” xmlns:jmx=”http://xml.kishou.go.jp/jmaxml1/” xmlns:jmx_add=”http://xml.kishou.go.jp/jmaxml1/addition1/”>
この xmlns や xmlns:○○ で始まる属性が「名前空間(namespace)」を定義している部分です。
xmlns=”…” はデフォルト名前空間
xmlns:jmx=”…” や xmlns:jmx_add=”…” はプレフィックス付き名前空間
この名前空間があると、XPathで要素を検索する際に特別な指定が必要になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//対応例(名前空間ありの場合 <?php $namespaces = $obj->getNamespaces(true); if (!empty($namespaces)) { foreach ($namespaces as $prefix => $ns) { $obj->registerXPathNamespace($prefix, $ns); //$nsは名前を格納する変数 } } $names = $obj->xpath('//ns:Name'); // nsは$nsのこと foreach ($names as $name) { echo $name . "\n"; } //直接名前を指定するとき XML内の xmlns="..." の値が、registerXPathNamespace の第2引数になります。 $obj->registerXPathNamespace('ns', 'http://xml.kishou.go.jp/jmaxml1/body/meteorology1/'); // 実際のXMLの名前空間URIに合わせて変更(目的のパスの直上にあるxmlns=" ") $names = $obj->xpath('//ns:Name'); // nsは実際のプレフィックスに合わせて変更 *[local-name()="Name"] foreach ($names as $name) { echo $name . "\n"; } |
要素値がわかっている場合のパスの取得例
この関数は、最初に与えた要素(例:$obj->Body
)から再帰的に探索します。
完全なXPath(ルートからのパス)を取得したい場合は、$obj から再帰的にたどる必要があります。
属性値や複数同名要素がある場合は、さらに工夫が必要です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?php function findElementPath($element, $targetValue, $currentPath = '', $ns = null) { // 要素名を取得(名前空間対応) $name = $element->getName(); if ($ns) { $name = $ns . ':' . $name; } $path = $currentPath . '/' . $name; // 値が一致したらパスを返す if (trim((string)$element) === $targetValue) { return $path; } // 子要素を再帰的に探索 foreach ($element->children() as $child) { $result = findElementPath($child, $targetValue, $path, $ns); if ($result) return $result; } return null; } // 例:名前空間を登録してから使う $obj->registerXPathNamespace('ns', 'http://xml.kishou.go.jp/jmaxml1/body/meteorology1/'); $body = $obj->Body; // 「明日」のパスを探す $path = findElementPath($body, '明日', '', 'ns'); echo $path . "\n"; // 例: /ns:MeteorologicalInfos/ns:TimeSeriesInfo/ns:TimeDefines/ns:TimeDefine/ns:Name |
要素値がわかっている場合のパスの取得例(複数条件)
例:【12時から18時まで】の【内陸】の【降水確率】を取得したい場合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?php $xml = simplexml_load_file('20250830193825_0_VPFD51_030000.xml'); // 必要な名前空間を登録 $xml->registerXPathNamespace('m', 'http://xml.kishou.go.jp/jmaxml1/body/meteorology1/'); // 「12時から18時まで」のtimeIdを取得 $timeDefines = $xml->xpath('//m:TimeDefine[m:Name="12時から18時まで"]'); //↑XPathで一致した要素がすべて配列の要素として格納されます。一致する要素がなければ、空の配列([])になります。 $targetTimeId = null; if ($timeDefines && isset($timeDefines[0])) { $targetTimeId = (string)$timeDefines[0]['timeId']; } // 「内陸」の降水確率を取得 if ($targetTimeId) { $items = $xml->xpath('//m:Item[m:Area/m:Name="内陸"]'); foreach ($items as $item) { foreach ($item->Kind as $kind) { if ((string)$kind->Property->Type === '降水確率') { foreach ($kind->Property->ProbabilityOfPrecipitationPart->children('http://xml.kishou.go.jp/jmaxml1/elementBasis1/') as $pop) { if ((string)$pop['refID'] === $targetTimeId) { echo (string)$pop . "\n"; // 例: 20 } } } } } } //<Body xmlns="http://xml.kishou.go.jp/jmaxml1/body/meteorology1/" xmlns:jmx_eb="http://xml.kishou.go.jp/jmaxml1/elementBasis1/"> //<jmx_eb:ProbabilityOfPrecipitation condition="雨" description="20パーセント" refID="1" type="6時間降水確率" unit="%">20</jmx_eb:ProbabilityOfPrecipitation> //のように jmx_eb: がある場合はそこが名前空間になっている。 //そのため $kind->Property->ProbabilityOfPrecipitationPart->children('http://xml.kishou.go.jp/jmaxml1/elementBasis1/') //のように指定しないと ProbabilityOfPrecipitationPart の子要素が取得できない。 |
コメント