10 JSONドキュメントの使用
リリース2.4以降のOracle TopLink MOXyでは、オブジェクトからJSONへの変換およびJSONからオブジェクトの変換を行う機能をサポートしています(JavaScriptオブジェクト表記)。この機能はRESTfulサービスを作成する際に役立ちます。JAX-RSサービスはXMLおよびJSONメッセージの両方を受け入れます。
この章の内容は次のとおりです。
JSONドキュメントの理解
TopLinkは、JSONの読取りおよび書込み時に、次のようなMOXyのオブジェクトからXMLへのオプションをすべてサポートします。
-
(JAXB仕様に加えて) TopLinkの高度で拡張されたマッピング機能
-
外部バインディング・ファイルのマッピングの格納
-
動的JAXBでの動的モデルの作成
-
マルチテナント・アプリケーションをサポートする拡張可能モデルの構築
JSONドキュメントのマーシャリングおよびアンマーシャリング
例10-1に示すように、JAXBマーシャラまたはJAXBアンマーシャラのeclipselink.media-typeプロパティを使用して、アプリケーションでJSONドキュメントを生成および使用します。
例10-2に示すように、JAXBContextを作成する際に、使用されるプロパティのMapにeclipselink.media-typeプロパティを指定できます。
また、例10-3に示すように、MarshallerProperties、UnmarshallerPropertiesおよびMediaType定数を使用してJSONドキュメントを使用するようアプリケーションを構成できます。
例10-1 マーシャリングおよびアンマーシャリング
...
Marshaller m = jaxbContext.createMarshaller();
m.setProperty("eclipselink.media-type", "application/json");
Unmarshaller u = jaxbContext.createUnmarshaller();
u.setProperty("eclipselink.media-type", "application/json");
...
例10-2 マップの使用
import org.eclipse.persistence.jaxb.JAXBContextProperties;
import org.eclipse.persistence.oxm.MediaType;
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("eclipselink.media-type", "application/json");
JAXBContext ctx = JAXBContext.newInstance(new Class[] { Employee.class }, properties);
Marshaller jsonMarshaller = ctx.createMarshaller();
Unmarshaller jsonUnmarshaller = ctx.createUnmarshaller();
マップに指定すると、JAXBContentから作成されたマーシャラおよびアンマーシャラは、自動的に指定したメディア・タイプを使用します。
例10-3 MarshallerPropertiesおよびUnarshallerPropertiesの使用
import org.eclipse.persistence.jaxb.MarshallerProperties; import org.eclipse.persistence.jaxb.UnarshallerProperties; import org.eclipse.persistence.oxm.MediaType; m.setProperty(MarshallerProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON); u.setProperty(UnmarshallerProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON); ...
JSONバインディングの指定
例10-4に、通常のJAXBの使用で必要となる依存性やコンパイル時の依存性を必要としない、基本的なJSONバインディングを示します。この例では、StreamSourceからユーザー・オブジェクトSearchResultsにJSONをアンマーシャリングし、新しいResultをコレクションに追加し、その後新しいコレクションをSystem.outに追加する方法を示します。
また、MOXy外部バインディング・ファイルをJSONドキュメントとして書き込むこともできます。例10-5に、bindings.jsonを使用してCustomerクラスおよびPhoneNumberクラスをJSONにマップする方法を示します。
例10-6に、JAXBContextをブートストラップする際に(例10-5で作成した)JSONファイルを使用する方法を示します。
例10-4 基本的なJSONバインディングの使用
package org.example;
import org.example.model.Result;
import org.example.model.SearchResults;
import java.util.Date;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(SearchResults.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setProperty("eclipselink.media-type", "application/json");
StreamSource source = new StreamSource("http://search.twitter.com/search.json?q=jaxb");
JAXBElement<SearchResults> jaxbElement = unmarshaller.unmarshal(source, SearchResults.class);
Result result = new Result();
result.setCreatedAt(new Date());
result.setFromUser("bsmith");
result.setText("You can now use EclipseLink JAXB (MOXy) with JSON :)");
jaxbElement.getValue().getResults().add(result);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("eclipselink.media-type", "application/json");
marshaller.marshal(jaxbElement, System.out);
}
}
例10-5 外部バインディングの使用
{
"package-name" : "org.example",
"xml-schema" : {
"element-form-default" : "QUALIFIED",
"namespace" : "http://www.example.com/customer"
},
"java-types" : {
"java-type" : [ {
"name" : "Customer",
"xml-type" : {
"prop-order" : "firstName lastName address phoneNumbers"
},
"xml-root-element" : {},
"java-attributes" : {
"xml-element" : [
{"java-attribute" : "firstName","name" : "first-name"},
{"java-attribute" : "lastName", "name" : "last-name"},
{"java-attribute" : "phoneNumbers","name" : "phone-number"}
]
}
}, {
"name" : "PhoneNumber",
"java-attributes" : {
"xml-attribute" : [
{"java-attribute" : "type"}
],
"xml-value" : [
{"java-attribute" : "number"}
]
}
} ]
}
}
例10-6 JSONを使用したJAXBContextのブートストラップ
Map<String, Object> properties = new HashMap<String, Object>(2);
properties.put("eclipselink-oxm-xml", "org/example/binding.json");
properties.put("eclipselink.media-type", "application/json");
JAXBContext context = JAXBContext.newInstance("org.example", Customer.class.getClassLoader() , properties);
Unmarshaller unmarshaller = context.createUnmarshaller();
StreamSource json = new StreamSource(new File("src/org/example/input.json"));
...
JSONデータ型の指定
XMLでは単一のデータ型を持ちますが、JSONでは文字列、数値およびブールを区別します。例10-7に示すように、TopLinkはこれらのデータ型を自動的にサポートします。
例10-7 JSONデータ型の使用
public class Address {
private int id;
private String city;
private boolean isMailingAddress;
}
{
"id" : 1,
"city" : "Ottawa",
"isMailingAddress" : true
}
属性のサポート
JSONでは属性を使用しません。@XmlAttribute注釈でマップされたものはすべて要素としてマーシャリングされます。デフォルトでは、TopLinkは属性のイベントと要素のイベントの両方をトリガーします。そのため、マップされた属性または要素のいずれかが値を処理できます。
この動作をオーバーライドするには、例10-8に示すように、JSON_ATTRIBUTE_PREFIXプロパティを使用して属性の接頭辞を指定します。TopLinkは、マーシャリング時に属性名に接頭辞を付加して、アンマーシャリング時にそれを識別します。
次の例では、numberフィールドが接頭辞@を持つ属性としてマップされます。
また、例10-9に示すように、JAXBContextを作成する際に、使用されるMapにJSON_ATTRIBUTE_PREFIXプロパティを設定できます。コンテキストから作成されたすべてのマーシャラおよびアンマーシャラで、指定された接頭辞を使用します。
例10-8 接頭辞の使用
jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_ATTRIBUTE_PREFIX, "@"); jsonMarshaller.setProperty(MarshallerProperties.JSON_ATTRIBUTE_PREFIX, "@") ;
{
"phone" : {
"area-code" : "613",
"@number" : "1234567"
}
}例10-9 Mapへの接頭辞の設定
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.JSON_ATTRIBUTE_PREFIX, "@");
JAXBContext ctx = JAXBContext.newInstance(new Class[] { Phone.class }, properties);
ルート要素なしのサポート
TopLinkは、ルート要素のないJSONドキュメントをサポートします。デフォルトでは、@XmlRootElement注釈が存在しない場合、マーシャリングされたJSONドキュメントはルート要素を持ちません。例10-10に示すように、ドキュメントのマーシャリング中にJSON_INCLUDE_ROOTプロパティを設定することで、この動作をオーバーライドできます(@XmlRootElementが指定されている場合でも、JSON出力からルート要素が省略されます)。
ルート要素のないドキュメントをアンマーシャリングする場合、例10-10に示すようにJSON_INCLUDE_ROOTプロパティを設定する必要があります。
注意:
ドキュメントにルート要素がない場合、アンマーシャリングするクラスを指定する必要があります。
例10-10 ルート要素のないドキュメントのマーシャリング
marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);
例10-11 ルート要素のないドキュメントのアンマーシャリング
unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false); JAXBElement<SearchResults> jaxbElement = unmarshaller.unmarshal(source, SearchResults.class);
ネームスペースの使用
JSONはネームスペースを持たないため、デフォルトですべてのネームスペースおよび接頭辞は、マーシャリングおよびアンマーシャリング時に無視されます。同一のローカル名に複数のマッピングを持つために、マッピングの区別ができないことによる問題である場合があります。
TopLinkでは、マーシャラおよびアンマーシャラに対してネームスペースと接頭辞間のマップ(NamespacePrefixMapperのインスタンス)を指定できます。ネームスペースの接頭辞は、要素名に付加されてマーシャリングされたドキュメントに表示されます。TopLinkは、アンマーシャリング操作時に接頭辞を識別し、結果のJavaオブジェクトは適切なネームスペースに配置されます。
例10-12に、NAMESPACE_PREFIX_MAPPERプロパティの使用方法を示します。
MarshallerProperties.NAMESPACE_PREFIX_MAPPERはXMLおよびJSONの両方に適用されます。UnmarshallerProperties.JSON_NAMESPACE_PREFIX_MAPPERはJSONのみのプロパティです。XMLアンマーシャリングは、ネームスペース情報をドキュメントから直接取得できます。
JSONのマーシャリング時に、ネームスペースにマップからドット( . )で区切られた接頭辞が付与されます。
{
"ns1.employee : {
"ns2.id" : 123
}
}
ドット区切りは、JSON_NAMESPACE_SEPARATORプロパティを使用すると任意のカスタム文字に設定できます。ここでは、かわりにコロン( : )が使用されます。
jsonMarshaller.setProperty(MarshallerProperties.JSON_NAMESPACE_SEPARATOR, ':'); jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_NAMESPACE_SEPARATOR, ':');
例10-12 ネームスペースの使用
Map<String, String> namespaces = new HashMap<String, String>();
namespaces.put("namespace1", "ns1");
namespaces.put("namespace2", "ns2");
jsonMarshaller.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, namespaces);
jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_NAMESPACE_PREFIX_MAPPER, namespaces);
コレクションの使用
例10-13に示すように、JSONへのマーシャリング時に、TopLinkはデフォルトで空のコレクションを[ ]としてマーシャリングします。
JSON_MARSHAL_EMPTY_COLLECTIONSプロパティを使用してこの動作をオーバーライドできます(空のコレクションは一切マーシャリングされません)。
jsonMarshaller.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, Boolean.FALSE) ;
{
"phone" : {
}
}
例10-13
{
"phone" : {
"myList" : [ ]
}
}
ルート・レベル・コレクションのマッピング
@XmlRootElement(name="root")注釈を使用してルート・レベルを指定する場合、JSONドキュメントを次のようにマーシャリングできます。
marshaller.marshal(myListOfRoots, System.out);
[ {
"root" : {
"name" : "aaa"
}
}, {
"root" : {
"name" : "bbb"
}
} ]
ルート要素はドキュメント内に存在するため、次を使用してアンマーシャリングできます。
unmarshaller.unmarshal(json);
クラスが@XmlRootElementを持たない場合(またはJSON_INCLUDE_ROOT = falseの場合)、マーシャリングで次が生成されます。
[ {
"name":"aaa"
}, {
"name":"bbb"
} ]
ルート要素が存在しないため、次のようにアンマーシャリングするクラスを示す必要があります。
unmarshaller.unmarshal(json, Root.class);
XML値のラップ
例10-14に示すように、JAXBは@XmlValueクラスで1つ以上の@XmlAttributesをサポートしています。
有効なJSONドキュメントを生成するために、TopLinkは例10-15に示すようにvalueのラッパーを使用します。
デフォルトでは、TopLinkはvalueをラッパーの名前として使用します。例10-16に示すように、JSON_VALUE_WRAPPERプロパティを使用して値ラッパーの名前をカスタマイズします。
例10-17に示すように、JAXBContextを作成する際に、使用されるプロパティのMapにJSON_VALUE_WRAPPERプロパティを指定できます。
マップに指定すると、JAXBContentから作成されたマーシャラおよびアンマーシャラは、自動的に指定した値ラッパーを使用します。
例10-14 @XmlAttributesの使用
public class Phone {
@XmlValue
public String number;
@XmlAttribute
public String areaCode;
public Phone() {
this("", "");
}
public Phone(String num, String code) {
this.number = num;
this.areaCode = code;
}
}
例10-15 値ラッパーの使用
{
"employee" : {
"name" : "Bob Smith",
"mainPhone" : {
"areaCode" : "613",
"value" : "555-5555"
},
"otherPhones" : [ {
"areaCode" : "613",
"value" : "123-1234"
}, {
"areaCode" : "613",
"value" : "345-3456"
} ]
}
}
例10-16
jsonMarshaller.setProperty(MarshallerProperties.JSON_VALUE_WRAPPER, "$"); jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_VALUE_WRAPPER, "$");
次が生成されます。
{
"employee" : {
"name" : "Bob Smith",
"mainPhone" : {
"areaCode" : "613",
"$" : "555-5555"
},
"otherPhones" : [ {
"areaCode" : "613",
"$" : "123-1234"
}, {
"areaCode" : "613",
"$" : "345-3456"
} ]
}
}
例10-17 マップの使用
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.JSON_VALUE_WRAPPER, "$");
JAXBContext ctx = JAXBContext.newInstance(new Class[] { Employee.class }, properties);
Marshaller jsonMarshaller = ctx.createMarshaller();
Unmarshaller jsonUnmarshaller = ctx.createUnmarshaller();