気象庁XMLについて

今回沿岸予報のアプリを作るにあたって、初めてAPIやXMLに触れましたが、気象庁のXMLは通常のAPIのJSONとは比べ物にならないほど難しかったので愚痴として残しておきますw

苦労した点

通常のAPIと違いとりあえずXMLの中身を把握しなければなりません、どこに自分の欲しいデータがあるか、構造がどうなっているか、これだけでもかなり時間を食いました。
そしてフィードも短期と長期に分かれていて、警報も同じ種類のフィードに入ってくる為
もしかして最新の天気予報も短期フィードで拾えない可能性があるのか?と、しばらく監視する必要もありました。
このことは気象庁のサイトに書かれていなかったです(見つけれなかっただけかもしれませんが)。

海上警報に置いては見ていない警報が有るためそれをどうするか出てきたらまた検討しなければなりません。出来ればもう二度と見たくないのですが・・・
(風系、濃霧、着氷、波浪、+α?・・・なぜ全部のパターンを公式HPで載せておかないのか・・・)
着氷は手に入れたので一番下に貼っておきます、XMLがエラーでアップロードできなかったので直貼りですが。他も入手したら随時載せます。

そしてXMLに使用している地名も一貫性がなくすべて調べる必要がありました、コードナンバーの手もありましたが対応表を作るのが面倒だったので文字列をハードコートしました、まぁこれが間違いだったかもしれませんがw
しかもデータの種類によって地名から地域名に変わっていたりとかなり面倒でした。

例:3日間の天気予報   上川地方、留萌地方
  3〜4データ分の気温 旭川、留萌

XMLの構造も、なんでこれ分かれてるんだ???と突っ込みたくなるような構造してます、後から追加してああなったのかはわかりませんが構造も一貫性がありません。

今回PHPのsimplexml_load_file()でパースしたのですが、安易にXPathで飛んでforeachで回しても取れない時があり、2日くらいハマりました。
GPTに愚痴ったら一瞬で解決しましたがw

↓その時の原因と解決を載せておきます。↓

気象庁XMLのパース時の注意点

これ、**気象庁XML特有の「同名タグが階層的に複数存在する構造」**が原因です。
順を追ってわかりやすく説明しますね。

🔹 まず、気象庁のXML構造の特徴

気象庁の防災情報や天気予報XML(たとえば https://www.data.jma.go.jp/developer/xml/feed/ 内の各ファイル)には、
以下のように同名タグ(Property や Type)が複数階層にネストしていることが多いです。

一見「同じPropertyタグ配下にTypeValueがある」だけですが、
実際のXML構造ではこうしたタグが複数の階層や異なる親要素に散らばっていることが多いです。

🔹 問題の原因:XPathが「文脈を失う」から

PHPで simplexml_load_file() を使っている場合、
$xml->Property->Type のような指定は「最初に見つかったProperty要素」だけにアクセスします。
なので、例えばこのような構造では:

$xml->Property->Type は最初の <Property> にしか到達できず、
最低気温 の方はスキップされてしまいます。

🔹 対処法①:xpath() でフルパス指定する

SimpleXMLElement の xpath() を使って、
「すべてのProperty」を対象に走査するのが安全です。

👉 //Property という XPath は「階層に関係なくすべての Property を取得」します。
これで「最高気温」「最低気温」など、同階層でなくても拾えます。

🔹 対処法②:foreachで1階層ずつ辿る(構造固定型)

もし気象庁のXMLがある程度決まった構造である場合(例えばMeteorologicalInfos > Item > Propertyなど)、
次のように明示的に階層を辿る方が安定します:

🔹 対処法③:Type名でフィルタするXPath

特定の要素だけ取りたい場合は、XPathで条件指定も可能です:

🔹 まとめ

状況対処方法
同名タグが複数階層に存在xpath('//Property') で全取得
特定のTypeだけ欲しいxpath('//Property[Type="最低気温"]/Value')
構造が固定階層を明示してforeach

💡 問題の正体(要約)

PHP の simplexml_load_file() は、
同じ名前のタグが同階層で複数ある場合に、最初のひとつしか直接アクセスできないことがあります。

しかも、気象庁XMLは複数の namespace(xmlns)を使っていて、
タグ名の判定が "Property" ではなく "ns:Property" のように扱われています。

この2つの要因が合わさると:

の $property が最初のPropertyで止まる(つまり最低気温だけ)現象が起こります。

✅ 原因の詳細

あなたのXMLの該当部分を抜粋するとこうですね:

という デフォルト名前空間 が宣言されています。
これにより <Property> タグは内部的には
{http://xml.kishou.go.jp/jmaxml1/body/meteorology1/}Property
という「名前空間付きのタグ名」として扱われます。

SimpleXML ではこれを明示的に登録しないと xpath() や foreach が正しく動作しません。

✅ 解決策:名前空間を登録してXPathで抽出する

最も確実な方法は、XPath + 名前空間登録を使うことです。

💡 これで正しく全部取れる理由

  • registerXPathNamespace() を使うと、
     met:Property や eb:Temperature が本来の namespace 付き要素として認識される。
  • xpath('.//met:Property') を使うことで、同階層に複数ある <Property> すべてを抽出できる。
  • foreach を回すと 最低気温 も 最高気温 も 最高気温予測範囲 も取得できる。

🔎 補足:もし foreach だけで書きたい場合

名前空間を削除してパースする方法もあります(ただし一部属性が欠けることもある)。

👉 これでも最低限動きますが、気象庁XMLは jmx_eb のような別 namespace タグもあるので、
正式には XPath+registerXPathNamespace の方法が安全です。

症状原因対処
最高気温が取得できないnamespace により Property が1つしか認識されないregisterXPathNamespace+XPath で全取得
foreach で Property をループしても1件だけdefault namespace の影響名前空間削除 or XPath 使用
同階層に複数の Property があるSimpleXML の制約.//met:Property で取得

コードを見せたら具体的なコードもくれた回答

🧩 問題の本質

となっており、
$kind->Property に複数ある <Property> の最初の1つしか入らないのが原因です。

PHP の SimpleXML は、
同じタグ名が複数ある場合に $element->Property でアクセスすると、最初の1件だけを返す仕様になっています。
SimpleXMLElement が IteratorAggregate を実装してるけど「最初のノード」扱いになる)

✅ 解決法①:xpath('.//m:Property') でループする

Kind の中にある Property は複数存在します。
それを明示的にループさせるようにします👇

🟢 これで「最低気温」「最高気温」「最高気温予測範囲(上端)」「最高気温予測範囲(下端)」など、全部出力されます。
Property が複数でもすべてループに入るようになります。

✅ 解決法②:$kind->Propertyを配列として扱う

もう1つの書き方は:

これでもOKです。
SimpleXML の仕様として「同名タグは foreach にすると配列的に全部取れる」挙動があるため、
$kind->Property->Type ではなく foreach ($kind->Property as $property) にするのがコツです。

🔍 なぜ「区域予報」は動くのに「地点予報」は止まるのか

気象庁の XML は、

  • 「区域予報」側では <Kind> ごとに Property が1つしか無い。
  • 「地点予報」側では <Kind> の中に Property が複数ある。

つまり:

種類Kind配下のProperty数$kind->Property->Type の結果
区域予報1OK
地点予報複数最初の1件(最低気温)しか出ない

という構造上の違いなんです。

✅ 最終形(動作保証済みコード)

🧠 まとめ(超重要ポイント)

現象原因対策
地点予報で1件しか出ない<Kind> 内に複数 <Property> がある$kind->Property を foreach で回す
XPathで1件しか出ないPropertyを直接指定している.//m:Property を明示的にループ
区域予報では動く各KindにPropertyが1件のみ仕様上問題なし

つまり構造がバラバラで一箇所で取れたからといって他の場所でも同じ様に取れるとは限らないということでした。
名前空間については GitHub Copilot で解決はしていました、ですが取れなかったです。
ちなみにこのforeach問題は GitHub Copilot では解決できませんでした。(GitHub CopilotはVScodeに組み込まれていてフォルダ、ファイル、一部のIDEの状態まで読み取れます、WordPress環境下ではPHPテスト出来ないのか?と聞いたらテスト環境をファイルごとまるっと作ってくれました、すごすぎです。)

ついでに

海上警報の警報タイプからXPathで目的のデータをforeachで回して取ろうとする時は2回xpath(‘parent::*’)を使って親を遡ってからやらないと取れません。理由は特に調べていません。

レア海上警報XML

着氷

コメント

タイトルとURLをコピーしました