XML を JSON に変換するのは機械的に聞こえますが、JSON に直接対応するものがない XML の部分にすぐぶつかります:属性、テキストと要素が混在するコンテンツ、1 回または複数回現れる要素、名前空間です。唯一の「正しい」マッピングはなく、慣例があるだけです。本ガイドでは標準的な慣例、避けて通れない決断、そして JavaScript、Python、ブラウザで XML を JSON に変換する方法を解説します。
マッピングの概観
XML to JSON コンバータの大半(xmltodict、fast-xml-parser、本サイトのツールも)は同じ形を取ります:各要素はオブジェクトになり、属性は特別なプレフィックス付きキーになり、テキストは値か予約キーに置かれます。
<!-- XML -->
<note id="1" priority="high">
<to>Ada</to>
<from>Bob</from>
<body>Hello & welcome</body>
</note>// JSON
{
"note": {
"@id": "1",
"@priority": "high",
"to": "Ada",
"from": "Bob",
"body": "Hello & welcome"
}
}属性 → @ プレフィックスキー
JSON オブジェクトには属性の概念がないので、ほぼ普遍的な慣例として属性名に @ のプレフィックスを付けます。これで子要素と区別でき、マッピングが可逆になります。
<book id="b1" lang="en"/>
→ { "book": { "@id": "b1", "@lang": "en" } } 別のプレフィックス($、_)や、ネストした "@attributes" オブジェクトを使うツールもあります。1 つを選び一貫して適用してください —— 下流コードは属性がどこにあるか知る必要があります。
テキストノードと混在コンテンツ
要素がテキストのみを含むときは文字列値に縮約されます。しかし要素が属性とテキストを持つ場合、テキストの置き場が必要です —— 慣例的にはキー #text です。
<price currency="USD">9.99</price>
→ { "price": { "@currency": "USD", "#text": "9.99" } }
<title>Effective TypeScript</title>
→ { "title": "Effective TypeScript" }真の混在コンテンツ(HTML 様のマークアップのようにテキストと子要素が交互に出る)が最も難しいケースです —— ほとんどのデータ指向コンバータは緩いテキストを連結するか落とします。XML がドキュメント志向(データ志向ではなく)なら、ここで情報損失が起きます。
単一 vs 配列の問題
これは他のどれよりもコードを壊しがちな落とし穴です。1 回現れる要素はオブジェクトになり、2 回現れる同じ要素は配列になります。つまり JSON の形はスキーマではなくデータに依存します:
<tags><tag>a</tag></tags>
→ { "tags": { "tag": "a" } } // オブジェクト
<tags><tag>a</tag><tag>b</tag></tags>
→ { "tags": { "tag": ["a", "b"] } } // 配列 tags.tag が常に配列であることを期待する側は単項目のケースでクラッシュします。解決策は 2 つ:パーサに既知の繰り返し要素を常に配列として扱わせるか、パース後に正規化(const arr = [].concat(node.tag ?? []))するかです。
名前空間
XML 名前空間は xmlns 宣言で束縛されたプレフィックス(soap:Envelope)を使います。JSON に名前空間概念はないので、コンバータは通常以下のいずれかを取ります:
- プレフィックスをキーに保持 ——
"soap:Envelope"。シンプルで可逆ですが、キーにコロンを含むためブラケットアクセスが必要です(obj["soap:Envelope"])。 - プレフィックスを落とす ——
"Envelope"。キーは綺麗になりますが、名前空間を失い、同じローカル名を使う 2 つの名前空間で衝突する恐れがあります。 xmlnsを属性として保持 —— 宣言が"@xmlns:soap"キーになり、束縛がラウンドトリップで生き残ります。
多くのデータ業務では、プレフィックスをキーに保持するのが最も安全な既定です —— 情報を失いません。
実体と CDATA
正しいコンバータは 5 つの定義済み実体(<、>、&、"、')と数値参照(©)を対応する文字にデコードし、<![CDATA[...]]> ブロックをリテラルテキストとして扱います。
名付き慣例:BadgerFish、GData、Parker
「属性は @、テキストは #text」だけが世にある方式ではありません。他システムの XML→JSON 出力を読むときに出会う 3 つの名付き慣例:
- BadgerFish —— 属性は
@プレフィックスキー、テキストは$、名前空間宣言は@xmlns。冗長だが無損。 - GData —— Google の変種:属性は
$プレフィックス、テキストは$t、繰り返し要素は常に配列。無損で形が予測可能。 - Parker —— 属性を完全に捨てる;最もシンプルで損失の多いマッピング。両端を制御していて要素値のみ気にする場合に有用。
すでに XML から JSON を生成するシステムと統合する際は、どの慣例を使っているか把握してからパースコードを書いてください。
JSONPath で結果をクエリ
XML を変換したら、JSONPath で値を指し示せます。XPath 流儀との 2 つの違い:
- 属性キーはマッピングの
@プレフィックスを伴うので、XPath の@idは JSONPath で$..['@id']になります。 - 上記の単一 vs 配列のため、
book/titleのような動く XPath は両形態を扱うために JSONPath では$..book[*].titleと書く必要があるかもしれません。
コードで XML を JSON に変換
// JavaScript(ブラウザ) —— DOMParser + 小さなウォーカー、またはライブラリ:
import { XMLParser } from 'fast-xml-parser';
const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '@' });
const obj = parser.parse(xmlString);
# Python —— xmltodict は属性を "@name"、テキストを "#text" にマッピング
import xmltodict, json
doc = xmltodict.parse(xml_string)
print(json.dumps(doc, indent=2))XML をオンラインで JSON に変換
素早く変換するには、XML を JSON ⇄ XML コンバータ に貼り、To JSON をクリック。上記の慣例(属性は @、混在コンテンツは #text、繰り返し要素は配列)を適用し、すべてブラウザで動作します。内部のフィードや API ペイロードがマシンを離れません。
よくある質問
XML 属性は JSON でどう表現されますか?
慣例として、@ をプレフィックスしたキー(例:@id)になり、子要素と区別され、マッピングを逆にできます。
なぜ同じ要素がオブジェクトになったり配列になったりするのですか?
形がデータに従うためです:1 回の出現はオブジェクトに、複数回の出現は配列にマッピングされます。パーサを既知の繰り返し要素を常に配列扱いするように設定するか、パース後に [].concat(value) で正規化してください。
属性を持つ要素内のテキストはどうなりますか?
オブジェクトには既に属性があるので、予約キー #text 配下に格納されます。テキストのみの要素はプレーンな文字列に縮約されます。
XML 名前空間はどう扱いますか?
JSON に名前空間はありません。最も安全なアプローチはプレフィックスをキーに保持("soap:Envelope")し、xmlns 宣言を @xmlns:* 属性として保つことで、何も失われません。
関連ツールとガイド
- JSON ⇄ XML コンバータ —— ブラウザで両方向に変換
- JSON から XML へ:ルート要素、配列、属性マッピング —— 逆方向
- CSV と XML を JSON に変換する方法 —— CSV を含む
- JSON とは? —— 対象フォーマットの型と規則