JSON, XML (J)
構造化文書(Structred Document)
テキストの中にも、構造をもったものて色々ありますよね。 例えば、HTML は、テキストの構造を markup してあるわけで。
構造化文書によく使われる JSON と XML を扱っていきます。 文書に構造があるので、Java オブジェクトとの対応関係をとることもできるので、 その方法を見ていきましょう。
JSON
JSON (JavaScript Object Notation) は、以下のようなテキストフォーマットです。
{ "name": "甲南大学",
"established": 1951,
"faculties":{
"知能情報学部": {
"established": 2008,
"departments": ["知能情報学科"]
},
"理工学部": {
"established": 2001,
"departments": [ "物理学科", "生物学科", "機能分子化学科" ]
},
...
}
}
構成要素は、以下のとおり。
オブジェクト
と呼ばれる、名前
と値
のペア。配列
と呼ばれる値のあつまり。値
には、文字列、数値、true/false, null, オブジェクト、配列を取りうる。つまり、ネスト構造も可能。
基本的には、木構造です。JavaScript 言語のオブジェクト記法法をベースに策定されたそうです。
各種 Web Service のデータフォーマットなどにも使われています。
JSON と Java
Java と JSON の変換ですが、JSON に対応するような Java のクラス群がある場合は、簡単に処理できます。
例えば、こんなクラスがあったとしたら(konanU.net.mapping以下にあります)
public class Univ {
public String name;
public int established;
public HashMap<String, Faculty> faculties;
}
public class Faculty {
public int established;
public List<String> departments;
}
Gsonというライブラリをつかったら、これだけで String に変換できます。gson の手配は、今回は maven というビルド環境がおこなってくれているはずです(コード例:UseGson)。
Gson gson = new Gson();
String univ2json = gson.toJson(univ);
System.out.println(univ2json);
もとに戻す場合は、こんな感じ。
Univ univFromJson = gson.fromJson(univ2json, Univ.class);
System.out.println(univFromJson);
こういう操作を mapping とか binding とか言います。今のは JSON フォーマットのテキストと object の mapping を行いました。 もっと進んで、関係データベースに mapping するのを O/R mapping などといいます。
XML
XML (Extensible Markup Language) は、HTML のようなmarkup language の一つですが、HTML と違って、いろんな種類のフォーマットを対象にできるようなっています。
例えば、XHTML も XML の一種ですし、SVG という画像表示用のフォーマットもあります。こんな感じです。
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
<circle cx="80" cy="80" r="60" fill="blue" />
<rect x="100" y="100" width="80" height="80" rx="2" ry="2" fill="red" />
</svg>
絵としてみると、こんな感じ(表示)。
XML の構成要素は、以下のとおり。
要素(element)
属性(attribute)
<要素名 属性1="属性値" 属性2="属性値" ...>
コンテンツ
</要素名>
要素は、上記のように開始タグから終了タグまでの部分を指し、 その間にコンテンツが入ります。コンテンツの中には、一般の文字列意外にも子要素が入っていても構いません。 コンテンツの含まない要素の場合は、
<要素名 属性1="属性値" 属性2="属性値" ... />
のように表記されます。
一方で、属性値にはネスト構造は許されません。あと、コンテンツ内の並び順には意味がありますが、属性の並び順が違っても同じ意味と見なされます。
XML と Java
Java と XML の変換ですが、XML にも JAXB などの binding tool が存在します。JAXB は少し前の Java では標準で入っていたのですが、最近は標準から外れたようです。
public class Univ {
public String name;
@XmlAttribute
public int established;
public HashMap<String, Faculty> faculties;
public Univ(){}
...
}
属性を表す部分は@XmlAttribute
という annotation が付記されており、JAXB は対象フィールドは属性
として扱ってくれます。
変換結果はこんな感じ。HashMap のところは、key, value
element として表現されています。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<univ established="1951">
<name>甲南大学</name>
<faculties>
<entry>
<key>理工学部</key>
<value>
<established>2001</established>
<departments>物理学科</departments>
<departments>生物学科</departments>
<departments>機能分子化学科</departments>
</value>
</entry>
<entry>
<key>知能情報学部</key>
<value>
<established>2008</established>
<departments>知能情報学科</departments>
</value>
</entry>
</faculties>
</univ>
Object から XML (String にするため、StringWriter を経由してます)
StringWriter out = new StringWriter();
JAXB.marshal(univ, out);
String univ2xml = out.toString();
System.out.println(univ2xml);
XML から Object (こちらもStringReader経由)
StringReader in = new StringReader(univ2xml);
Univ univFromXml = JAXB.unmarshal(in, Univ.class);
System.out.println(univFromXml);
JAXB ですが、対象オブジェクトが JAXB の規格にそった構造をもっていない場合は、marshaling の方法が分からないので、javax.xml.bind.annotation.adapters などで自分で定義する必要があります。
XML と Python については、後述する DOM っぽい API (ElementTree XML APIがありますが、今回は省略します。
DOM
一方で、任意の XML を扱いたい場合、XML を木構造として扱います。XML を DOM (Document Object Model) とよばれる木構造データ構造に変換してつかうのが一般的です。 DOM は、JavaScript などを持ちいて動的 Web Page を作成するのにもつかわれます。 ブラウザは、(X)HTML 文書を DOM データ構造に変換して持っています。 そのうえで、JavaScript から DOM データ構造に対する改変を許します。 これによって、HTML ページが動けるようになるわけです。
HTML 文書を変更すると表示が変わるというと奇異な感じがするかもしれませんが、HTML 文書をもとに、HTML の木構造= GUI 部品群が配置された、ある種の GUI アプリみたいなものです。
スキーマ
ここまで JSON にしろ XML をにしろ、任意の JSON や XML を相手にするのではなく、「ある種の目的」のための「ある種のフォーマット」にそった JSON や XML を相手にしてました。対応する Java のデータ構造が決まっているように、文書も「ある種のフォーマット」に従っている訳です。こういう文書の論理的構造のことを、スキーマ (Schema)とよびます。
Schema を記述するための言語もあって、XML では DTD や XML Schema, RELAX NG などが有名で、JSON だと JSON Schema などがあります。
授業の際は、簡単に例をあげて紹介します。
Java Reflection(参考)
Java はコンパイルする言語ですよね。メソッド呼出しするには、普通、プログラム作成段階から呼び出すメソッドなどが分かっていないとダメです。 なのに、JAXB や gson といったライブラリは、どうして「事前に知りもしないクラスの内部構造に応じた処理」ができるのでしょうか?
Java では、プログラム内で、各クラスの情報を扱うためのjava.lang.Class クラスがあります。たとえば、String
クラスのクラス情報は、
String.class
などで取得できますし、対象 obj
のクラス情報は
obj.getClass()
で取得できます。
Class オブジェクトからは、フィールド(java.lang.reflect.Field)やメソッド(java.lang.reflect.Method)の情報を取得し、データ取得やメソッド実行に利用することもできます。こういうのが使えるから、gson や JAXB といったライブラリも作れるんです。
これらの機能はリフレクション (Reflection) と呼ばれます。一般には、プログラム中で、自分のプログラムに関する情報を取得・変更できる機能のことを指す用語です。 まあ、ちょっと特殊な機能なので、あまり普段使う必要はないかと思いますが、存在は知っておくとよいでしょう。