このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

XMLからJSONへの変換をどうすべかねぇ

XMLとJSONのあいだで相互変換をしたいわけですが、とりあえず XML → JSON 方向を考えます。

内容:

[追記]続きは、http://d.hatena.ne.jp/m-hiyama/20100410/1270859793[/追記]

実例と基本方針

一般的なケースの前に、Catyでの例を挙げます。Catyで使っているJSONは、オリジナルJSONをわずかに拡張して、タグという概念を入れているので割と素直に「XML→JSON」変換ができます。例を挙げます。

XML:

<greeting mode="friendly">こんにちは<smile /></greeting>

拡張JSON:

@greeting{
  "mode" : "friendly",
  "" : ["こんにちは", @smile {}]
}

@greeting, @smile がタグなんですが、これは単なるシンタックスシュガーで、次の形にマーシャリングされます。

{
  "$tag" : "greeting",
  "mode" : "friendly",
  "" : ["こんにちは", {"$tag" : "smile"}]
}

つまり、"$tag" という名前のプロパティ値にタグ名が入るだけです。ですから、JSON構文の範囲内に収まっています。どんな状況でも、特殊な名前のプロパティを予約してタグ名を埋め込むことができます。要素内容を入れるには、無名("")のプロパティの代わりに、"$content" のような名前を使ってもいいと思います。

変換の規則

上の実例で示した変換は次の規則にしたがっています。

  1. XML要素のタグ名は、拡張JSONのタグ名にする。
  2. XML要素の属性は、JSONオブジェクトのプロパティにする。
  3. XML要素の内容は、JSONオブジェクトの無名のプロパティにする。
  4. XML要素の内容であるプロパティ値は、リスト(JSON配列)とする。
  5. DOMの意味でのテキストノードは文字列とする。
  6. これらの規則は再帰的に適用する。

細かいことを言えば、文字参照、実体参照、コメント、CDATAセクション、PIなどの扱いも決めておかなくてはなりませんが、それら有象無象に対する常識的な処理はほぼ決まっているので、本質的な部分は上の規則で間に合います。

ただし、空内容要素に関しては注意が必要です。

空内容要素の変換

を先の規則にしたがいJSONに変換すると、ホントは次の形になります。

@smile {
  "" : []
}

要素内容(=子ノードのリスト)が空リストってことです。XML仕様/DOM仕様には曖昧性があって、空のリストと空文字列が並んだリストの区別はつきません。例えば、次のJSONデータも上と同じ意味です。

@smile {
  "" : ["", ""]
}

この曖昧性は、DOMの正規化手順で解消できて、"" : [] という形を標準だとみなせます。しかしそれにしても、"" : [] も余分な感じ。ローカルな正規化手順で空リストである無名プロパティは削除していいとすれば、@smile {} となります。

タグが多すぎる

別な実例を考えてみます。

<book>
 <title>Webを支える技術</title>
 <author>山本陽平</author>
 <isbn-10>4774142042</isbn-10>
</book>

これは次のように変換されます。

@book {
  "" : [
   @title { "" : ["Webを支える技術"] },
   @author { "" : ["山本陽平"] },
   @isbn-10  {"" : ["4774142042"] }
  ]
}

なんかタグが多すぎませんか。次のようにすればシンプルです。

@book {
   "title" : "Webを支える技術",
   "author"  : "山本陽平",
   "isbn-10" : "4774142042"
}

このようなスッキリした変換をするには、変換元のXMLインスタンスだけを見ていても無理です。メタ情報が必要なのです。

要素を3種類に分類する

メタ情報はできる限り簡単にしましょう。まず、要素(の型)を次に3種に分類します。

  1. 属性をまったく持たず、内容はテキストだけである。
  2. 属性が出現するかもしれないが、内容はテキストだけである。
  3. それ以外の一般的な要素。

各要素のタグ名ごとに、どの種類であるかの情報が与えられているとしましょう。そのとき、上の3種の分類ごとに変換方法を変える事ができます。

  1. タグ名をプロパティ名として、内容テキストを文字列プロパティ値とする。
  2. 内容テキストをリストにせずに、単なる文字列とする。
  3. 先に述べた変換規則を適用する。

<title>Webを支える技術</title> が、それぞれの変換方法でどう変換されるかというと:

  1. "title" : "Webを支える技術"
  2. @title {"" : "Webを支える技術"}
  3. @title {"" : ["Webを支える技術"]}

[追記]

書き忘れた: 子要素が属性を持たず内容はテキストだけであっても、それでただちに親の属性のように扱っていいとは限りません。さらに次の条件があります。

  • その子要素の出現位置はどこでもいい。何番目にあるかとか、他の子要素(兄弟)との前後関係などにまったく制約がない。
  • 同じタグ名の子要素が複数個は出現しない。

このような条件を満たす子要素なら、親の属性扱いしてもセマンティクスへの影響はありません。

[/追記]

どうすべきか

スッキリした変換のほうがうれしい感じはします。しかし、メタ情報の参照というのはたいていは不吉なことで、破綻の入り口だったりしますので、どうすべきかの判断は難しいところです。

[追記]続きは、http://d.hatena.ne.jp/m-hiyama/20100410/1270859793[/追記]