メインコンテンツへ移動

XMLをJSONに変換

XMLからJSONへの変換は、文書をブラウザ内で変換します。2つの形式の間に正規の対応関係は存在しないため、あらゆる判断は黙示のデフォルトではなくオプションとして表れます。中でも重要なのがforce-arrayリストです。XMLは1項目のリストと単一の値を区別できず、どんな変換ツールにも推測はできないからです。

端末内で処理
XML
0 B1 行行 1、列 1
JSON読み取り専用
0 B1 行

XMLをJSONに変換はどう動きますか?

XMLとJSONは同じ形を記述しないため、両者の間に唯一の正しい変換は存在しません。それでも唯一の変換を提示する変換ツールは、4つの判断を隠しています。そのどれもが、あなたのデータを黙って壊し得るものです。

推測できない単一要素問題

XMLには配列がありません。<line/><line/>は2つの要素、<line/>は1つの要素です。そのため変換ツールは、繰り返し要素を配列にし、単独の要素はスカラーのままにします。つまり同じエンドポイントが、明細1行の注文では{"line": {…}}を返し、明細2行の注文では{"line": [{…}, {…}]}を返すことになります。後者を前提に書かれたコードは、前者を受け取った途端に動かなくなります。本番環境で、顧客が1商品だけ注文した日に。

これは推論では解けません。必要な情報、つまりこの要素はコレクションなのかという情報は、インスタンスではなくスキーマの中にあります。誠実な解決策がforce-arrayリストです。コレクションである要素の名前を並べておけば、その要素は常に配列になります。文書にたまたまいくつ含まれていようと、変わりません。

属性には行き先が必要

<line sku="X"><sku>Y</sku></line>には、同名の属性と子要素があります。両者を統合すれば、どちらかが失われます。プレフィックス(デフォルトは@)は両方を保持します。その代償は、JavaScriptで少し打ちにくいキーになることです。空に設定すれば統合されますが、衝突を受け入れることになります。

XMLに型はない

<qty>3</qty>は文字列"3"です。数値への強制変換は推測であり、その推測こそが、部品番号007を7に、郵便番号01970を1970に変えてしまうものです。そのため値のパースはデフォルトでオフになっています。オンにした場合でも、先頭が0の値は文字列のまま残ります。JSONの文法はそもそも先頭の0を禁じているので、失うものは何もありません。

混在コンテンツは生き残らない

<p>before<b>bold</b>after</p>には、子要素の前と後ろの両方にテキストがあります。JSONのオブジェクトには順序がないため、2つのテキストは1つのキーの下に連結され、<b>に対する前後の位置関係は失われます。これを直す手段はありません。文書型のXMLはデータ型ではないのであり、JSONへの変換は損失を伴う変換ではなく、間違った操作なのです。

不正な入力は推測せず、拒否する

対応が壊れたタグや閉じられていない要素は、タグと行を名指しするエラーになります。<a><b></a>を受け付けてそれらしいJSONを出力する変換ツールは、失敗するツールよりも質が悪いのです。切り詰められたXMLを貼り付けた人は、それが切り詰められていたことに、最後まで気づかないままですから。

XML

<order id="1">
  <line sku="A">Widget</line>
</order>

JSON

{
  "order": {
    "@id": "1",
    "line": {
      "@sku": "A",
      "#text": "Widget"
    }
  }
}
// with "line" forced: "line": [ … ]

XMLをJSONに変換が対応するオプションとエッジケースは?

技術リファレンスとオプション
パラメータ型デフォルト動作とエッジケース
配列を強制要素名(なし)ここに挙げた名前は、1回しか出現しなくても常に配列になります。最重要オプションです。要素1個のリスト問題への唯一の対策であり、文書から推測することはできません。
属性プレフィックス文字列@属性を同名の子要素と区別して保持します。空にすると両者を統合しますが、衝突時にはどちらか一方が勝つことに留意してください。
テキストキー文字列#text属性や子要素も持つ要素のテキストの行き先です。テキストしか持たない要素は、素の文字列になります。
値の型変換真偽値オフ"3"を3に、"true"をtrueに、"null"をnullに変換します。デフォルトはオフです。XMLには型がなく、推測では部品番号が整数になってしまうからです。
先頭のゼロ文字列として保持常に値の解析が有効でも、007は"007"のままです。JSONの文法は先頭のゼロを禁じているため、ゼロを保ったまま変換できる表現が存在しません。
名前空間を除去真偽値オフプレフィックスを外してsoap:EnvelopeをEnvelopeにし、xmlns宣言を取り除きます。SOAPには便利ですが、2つの名前空間が同じローカル名を使うと情報が失われます。
コメントを保持真偽値オフコメントを#commentメンバーの下に保存します。要素間での位置は保持されません。JSONオブジェクトには順序がないからです。
空要素null常に<a/>と<a></a>はXMLでは同じものです。どちらも空文字列ではなくnullになります。空文字列では値があることを示唆してしまうからです。
CDATAリテラルテキスト—CDATA内の内容はそのまま取り込まれます。中の<は、小なり記号ではなく、アンパサンドのあとにltが続く文字の並びです。CDATAとはそういうものです。
実体参照デコード定義済みの5種類& < > " 'と数値参照を扱います。宣言されていない実体は削除ではなくそのまま残します。内容を黙って消すほうが悪い失敗だからです。

よくある質問

1件ならオブジェクトなのに、2件だと配列になるのはなぜですか?

XMLには配列がないからです。XMLの変換における最大の落とし穴は、これ1つに尽きます。変換ツールは<line>を1つ見ても、スキーマがもっと許しているかどうかを知る術がありません。要素名をforce-array欄に入れれば、常に配列になります。あなたのコードが前提とすべき形は、こちらです。これは出荷前に行う価値があります。1件だけの注文が本番環境を壊してからでは遅いのです。

キーが@で始まるのはなぜですか?

それらは属性です。XMLには2種類のメンバーがあり、JSONには1種類しかないため、属性にはプレフィックスが要ります。なければ子要素と衝突します。sku属性とsku子要素を両方持つ要素は、特別なことではありません。プレフィックスを空にして統合することもできますし、アンダースコアのような扱いやすい文字に変えることもできます。

数値が文字列になるのはなぜですか?

XMLでは元々文字列だからです。文書に型情報はありません。<qty>3</qty>を数値3に変換するのは推測であって、数量ならたまたま当たりますが、部品番号、郵便番号、電話番号では間違います。ゼロ詰めの識別子がデータにないなら、値のパースをオンにしてください。その場合でも、先頭が0の値は文字列のまま保たれます。

子要素の前後にあったテキストはどうなりますか?

連結されてtextキーの下に置かれ、位置の情報は失われます。これは混在コンテンツであり、JSONには本当に表現できません。オブジェクトには順序がないため、あるテキストが子の前で、別のテキストが後だったと記録する場所がないのです。XMLが文書型のもの(XHTML、DocBook、散文の中にマークアップが入るもの全般)なら、JSONへの変換は損失を伴う変換ではなく、間違った操作です。

SOAPレスポンスも扱えますか?

扱えます。その際はstrip namespacesをオンにしてください。soap:EnvelopeがEnvelopeになり、つけない限りすべての要素の属性として現れるxmlns宣言が取り除かれます。ただし、除去には代償もあります。文書が本当に同じローカル名の2つの名前空間を使っている場合、情報は失われます。SOAPではまれですが、混在した語彙のXMLではよくあることです。

XMLはアップロードされますか?

いいえ。トークナイザーも変換器も、すでに開いているタブの中で動作します。Networkパネルで確かめてください。重要なのは、現在通信で飛び交うXMLの大部分はSOAP、SAMLアサーション、金融メッセージであり、つまり開発者が扱う中でも最も機密性の高い素材だという点です。