XMLは死んでいない(残念ながら)— 成人としてJSONに変換する
あなたはXMLから逃れたと thought ましたが、それは間違いでした。古くからのAPIや企業システムからのXMLを処理し、構造的な特徴を理解し、頭を損なわずにJSONに変換する方法について説明します。
あなたはRESTを学び、JSONを採用し、XML時代がFlashやIE6のように過去のものだと考えた。しかし、新しいクライアントが銀行統合のAPI認証情報を渡してくれたとき、そこに400行のSOAPエンドロールが現れた。
XMLへの再会。それはどこにもいなかった。
なぜXMLが今もあらゆる場所に存在しているのか
多くの現代アプリ開発者にとって、XMLは古びたもののように感じられるが、現実世界では特に企業ソフトウェア、銀行、医療、政府システムにおいてXMLは絶対的に重要なインフラである。ここでは、あなたが実際に遭遇する場面を紹介する。
- SOAPウェブサービス – 金融機関、保険プラットフォーム、大規模ERPシステムの標準である。あなたのフィンテック統合はおそらくこれを通じて行われる。
- 銀行API – ISO 20022、SWIFTメッセージ、多くの核心銀行APIはXMLで通信する。RESTオプションはない。
- 政府データ – HMRC、IRS、そしてほとんどの政府ポータルはXMLを受信または返却する。これはすぐに変化しない。
- エンタープライズミドルウェア – SAP、Oracle、および古くからのESBシステムはXMLをネイティブに扱う。何かエンタープライズレベルの統合を行う場合、XMLに遭遇する可能性が高い。
- RSSおよびAtomフィード – まだXMLである。多くのコンテンツパイプライン、ニュースアグリゲーター、モニタリングツールはこれらを依存している。
不快な事実:XMLはどこにも消えず、その上に構築されたシステムが消えているからである。銀行の核心インフラを置き換えるのはスプリントチケットではない。だから、あなたは適応する。
XML vs JSON:実際にあなたを悩ませる構造的差異
変換を始める前に、XMLからJSONへの変換は単なるフォーマットの交換ではないという理解が重要である。この2つのフォーマットはデータを異なる方法でモデル化しており、その差異が実際の問題を生む。
並列比較:同じデータを両フォーマットで
シンプルな顧客注文を取ってみよう。以下がXMLでの例だ。
<order id="ORD-1042" currency="GBP">
<customer>
<name>Alice Martin</name>
<email>alice@example.com</email>
</customer>
<items>
<item sku="PRD-001">
<description>Wireless Keyboard</description>
<quantity>1</quantity>
<price>49.99</price>
</item>
<item sku="PRD-007">
<description>USB-C Hub</description>
<quantity>2</quantity>
<price>29.99</price>
</item>
</items>
</order>
そしてJSONでの同等のデータは以下の通り。
{
"order": {
"@id": "ORD-1042",
"@currency": "GBP",
"customer": {
"name": "Alice Martin",
"email": "alice@example.com"
},
"items": {
"item": [
{
"@sku": "PRD-001",
"description": "Wireless Keyboard",
"quantity": "1",
"price": "49.99"
},
{
"@sku": "PRD-007",
"description": "USB-C Hub",
"quantity": "2",
"price": "29.99"
}
]
}
}
}
すでに構造的な摩擦が見えてくる。それらを一つずつ見てみよう。
属性 vs キー
XML要素は属性(id="ORD-1042")を子要素やテキストコンテンツとともに持ち得る。JSONには属性という概念がない——すべてがキー-バリューペアである。最も一般的な慣例は、属性を @ で前付けることである。変換時にこれにより "@id": "ORD-1042"が得られる。一部のパーサーは $ または完全にフラット化する。この慣例は、消費コードがどのプレフィックスを期待しているかを理解する上で重要である。
配列 vs 再現要素
これは開発者たちに常に問題を引き起こす。JSONでは配列は明示的に表現される: [...]。XMLでは、このような区別はない——再現する兄弟要素は暗黙的にリストとして扱われる。パーサーが1つの <item> 要素を見るとオブジェクトを返すが、2つの <item> 要素を見ると配列を返す。APIが1つの結果を返した場合、あなたのコードが破壊される。
解決策は、既知のリストフィールドに対して配列を強制すること、またはタイプ情報を保持するライブラリを使用することである。一時的なデータ変換を行う場合、フィールドが単一オブジェクトに見えるが、実際のプロダクションパッケージではリストになる可能性があることを確認する。
テキストノードと混合コンテンツ
XML要素はテキストコンテンツと子要素を同時に保持できる(混合コンテンツ)。JSONはこれをきれいに表現できない。パーサーはそれぞれ異なる方法で処理する——一部は #text キーを使用し、他のものは _、あるいは混合コンテンツを無視する。XMLの混合コンテンツを変換する場合、出力の確認を行う。
名前空間
SOAPレスポンスには多くのXML名前空間が含まれている: <ns2:getOrderResponse xmlns:ns2="http://...">。パーサーによっては、これらは削除され、キー名(ns2:getOrderResponse)に結合されたり、URIにマッピングされる。ほとんどの場合、削除したいが、2つの名前空間が同じ要素名を持つ場合、その区別が失われる。出力がきれいであると仮定する前に、実際に何が含まれているかを理解しておく。
高速パス:パーサーを書かずオンラインでXMLを変換
APIレスポンスをデバッグしたり、不慣れなXMLスキーマを探索したり、一時的な変換を行う場合、パーサーを書くのは過剰である。 XMLからJSONへの変換ツール — XMLを貼り付けて、すぐにクリーンなJSONを得て、構造を確認し、コードを書く前に見直す。
これは属性( @ プレフィックス慣例)、ネストされた要素、再現要素を配列として扱い、テキストコンテンツを保持する。これは実際のAPIパッケージの大部分をカバーしている。SOAPレスポンスがエンドロールを剥がされたデータとしてどのように見えるかを迅速に理解するために便利である。
プログラムによる変換:プロダクションで使うべきもの
XML APIと統合するプロダクションコードでは、パーサーを手動で書くのではなく、適切なライブラリを使うべきである。以下は各言語での標準選択肢である。
JavaScript / Node.js
import { XMLParser } from 'fast-xml-parser';
const parser = new XMLParser({
ignoreAttributes: false,
attributeNamePrefix: '@',
isArray: (name) => ['item', 'product', 'order'].includes(name),
});
const result = parser.parse(xmlString);
fast-xml-parser Node.jsでの最良の選択肢である。 isArray を用いて、既知のリスト要素に対して配列として扱う——これは最終的にプロダクションで発生する「単一アイテム/配列不一致」のバグを防ぐ。
パイソン
import xmltodict
with open('response.xml') as f:
data = xmltodict.parse(f.read())
import json
print(json.dumps(data, indent=2))
xmltodict はPythonでの標準選択肢である。属性は @ 、テキストノードは #text の慣例を使用する。注意点として、これは OrderedDictを返すため、 json.dumps.
PHP
$xml = simplexml_load_string($xmlString);
$json = json_encode($xml);
$data = json_decode($json, true);
と良好にシリアル化できる。 simplexml_load_string + json_encode PHPの DOMDocument は迅速なパスだが、属性の扱いが不一致であり、エッジケースでデータを失う可能性がある。SOAPレスポンスのプロダクション用途では、 LIBXML_NOBLANKS または専用ライブラリを使用することを検討する。
よく見落とされる問題点
- 数値は文字列として出力される。 XMLには数値型がない——すべてがテキストである。価格フィールドは
"49.99"、ではなく49.99となる。変換後に明示的にキャストする。 - ブール値は文字列として残る。
<active>true</active>になります"active": "true"。条件分岐で使用する前に確認する。 - 空要素は
nullまたは空オブジェクトになる。<middleName/>はnull,""、 または{}に変換される可能性がある。エッジケースをテストする。 - CDATAセクション は保存されたり保存されなかったりする。APIがHTMLコンテンツをエスケープするためにCDATAを使用している場合、パーサーがコンテンツを無視しないか確認する。
- 順序は保証されない。 XML要素の順序はいくつかのスキーマで重要であるが、JSONオブジェクトのキー順序は保証されない。消費システムで順序が重要である場合、それを明示的に処理する。
SOAPに特化した処理
SOAPはXMLの上に別の層を加える——すべてのレスポンスは <Envelope> で包まれており、 <Body>があり、名前空間宣言や <Header> ブロックで飾られている。変換前に、通常はボディコンテンツだけを抽出する。
Pythonで zeep (SOAPクライアント)を使用すると、Pythonオブジェクトが直接得られ、XMLをパースする必要がない。Node.jsでは soap と strong-soap を同じようにする。もし fetch または axiosでSOAPエンドポイントに直接アクセスする場合、エンドロールを手動で剥がした後、XMLからJSONに変換する必要がある。
SOAPレスポンスの迅速な確認には XMLからJSONへの変換ツール が有用——完全なエンドロールを貼り付けて、構造を確認し、実際に必要なデータのパスを特定できる。
現実を受け入れて進む
あなたのグリーンフィールドプロジェクトが2003年に作られたSOAPAPIと統合する必要があるという事実に気づくと、ある種の悲しみを感じる。それを感じて、そして進む。XMLは解決済みの問題であり、パーサーは成熟しており、変換ツールが存在し、問題点はよく記述されている。あなたは最初の開発者ではない。
あなたの言語に適したライブラリを使用し、タイプ変換を明示的に処理し、既知のリストフィールドに対して配列を強制し、APIドキュメントに示されている理想化された例ではなく、実際のパッケージでテストする。ドキュメントは1つのアイテムを示すが、プロダクションは50個を送る。
探索作業——不慣れなXMLスキーマを理解したり、変換をコードを書く前に検証したりする場合—— XMLからJSONへの変換ツール をブックマークしておく。毎回パッケージをスクリプトで起動するよりも速い。
