リリース2.4以降のEclipseLink MOXyでは、オブジェクトからJSONへの変換およびJSONからオブジェクトの変換を行う機能をサポートしています(JavaScriptオブジェクト表記)。この機能はRESTfulサービスを作成する際に役立ちます。JAX-RSサービスはXMLおよびJSONメッセージの両方を受け入れます。
この章の内容は次のとおりです。
EclipseLinkは、次のようなJSONの読取りおよび書込みを行う際のすべてのMOXyオブジェクトとXML間のオプションをサポートしています。
(JAXB仕様に加えて)EclipseLinkの高度で拡張されたマッピング機能
外部バインディング・ファイルのマッピングの格納
動的JAXBでの動的モデルの作成
マルチテナント・アプリケーションをサポートする拡張可能モデルの構築
例10-1に示すように、JAXBマーシャラまたはJAXBアンマーシャラのeclipselink.media-typeプロパティを使用して、アプリケーションで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に示すように、JAXBContext
を作成する際に、使用されるプロパティのMap
にeclipselink.media-type
プロパティを指定できます。
例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
、UnmarshallerProperties
およびMediaType
定数を使用してJSONドキュメントを使用するようアプリケーションを構成できます。
例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); ...
例10-4に、通常のJAXBの使用で必要となる依存性やコンパイル時の依存性を必要としない、基本的なJSONバインディングを示します。この例では、StreamSource
からユーザー・オブジェクトSearchResults
にJSONをアンマーシャリングし、新しいResult
をコレクションに追加し、その後新しいコレクションをSystem.out
に追加する方法を示します。
例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); } }
また、MOXy外部バインディング・ファイルをJSONドキュメントとして書き込むこともできます。例10-5に、bindings.json
を使用してCustomerクラスおよびPhoneNumberクラスをJSONにマップする方法を示します。
例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に、JAXBContext
をブートストラップする際に(例10-5で作成した)JSONファイルを使用する方法を示します。
例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")); ...
XMLでは単一のデータ型を持ちますが、JSONでは文字列、数値およびブールを区別します。例10-7に示すように、EclipseLinkはこれらのデータ型を自動的にサポートします。
JSONでは属性を使用しません。@XmlAttribute
注釈でマップされたものはすべて要素としてマーシャリングされます。デフォルトでは、EclipseLinkは属性と要素のイベントの両方をトリガーします。そのため、マップされた属性または要素のいずれかが値を処理できます。
この動作をオーバーライドするには、例10-8に示すように、JSON_ATTRIBUTE_PREFIX
プロパティを使用して属性の接頭辞を指定します。EclipseLinkは、マーシャリング時に属性名に接頭辞を付加して、アンマーシャリング時にそれを識別します。
次の例では、number
フィールドが接頭辞@を持つ属性としてマップされます。
例10-8 接頭辞の使用
jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_ATTRIBUTE_PREFIX, "@"); jsonMarshaller.setProperty(MarshallerProperties.JSON_ATTRIBUTE_PREFIX, "@") ;
{
"phone" : {
"area-code" : "613",
"@number" : "1234567"
}
}
また、例10-9に示すように、JAXBContext
を作成する際に、使用されるMapにJSON_ATTRIBUTE_PREFIX
プロパティを設定できます。コンテキストから作成されたすべてのマーシャラおよびアンマーシャラで、指定された接頭辞を使用します。
EclipseLinkは、ルート要素のないJSONドキュメントをサポートします。デフォルトでは、@XmlRootElement
注釈が存在しない場合、マーシャリングされたJSONドキュメントはルート要素を持ちません。例10-10に示すように、ドキュメントのマーシャリング時にJSON_INCLUDE_ROOT
プロパティを設定することで、この動作をオーバーライドできます(@XmlRootElement
が指定されている場合でも、JSON出力からルート要素を省略します)。
例10-10 ルート要素のないドキュメントのマーシャリング
marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);
ルート要素のないドキュメントをアンマーシャリングする場合、例10-10に示すようにJSON_INCLUDE_ROOT
プロパティを設定する必要があります。
例10-11 ルート要素のないドキュメントのアンマーシャリング
unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false); JAXBElement<SearchResults> jaxbElement = unmarshaller.unmarshal(source, SearchResults.class);
注意: ドキュメントにルート要素がない場合、アンマーシャリングするクラスを指定する必要があります。 |
JSONはネームスペースを持たないため、デフォルトですべてのネームスペースおよび接頭辞は、マーシャリングおよびアンマーシャリング時に無視されます。同一のローカル名に複数のマッピングを持つために、マッピングの区別ができないことによる問題である場合があります。
EclipseLinkでは、マーシャラおよびアンマーシャラに対してネームスペースと接頭辞間のマップ(NamespacePrefixMapper
のインスタンス)を指定できます。ネームスペースの接頭辞は、要素名に付加されてマーシャリングされたドキュメントに表示されます。EclipseLinkは、アンマーシャリング操作時に接頭辞を識別し、結果のJavaオブジェクトは適切なネームスペースに配置されます。
例10-12に、NAMESPACE_PREFIX_MAPPER
プロパティの使用方法を示します。
例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);
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-13に示すように、JSONへのマーシャリング時に、EclipseLinkはデフォルトで空のコレクションを[ ]
としてマーシャリングします。
JSON_MARSHAL_EMPTY_COLLECTIONS
プロパティを使用してこの動作をオーバーライドできます(空のコレクションは一切マーシャリングされません)。
jsonMarshaller.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, Boolean.FALSE) ;
{ "phone" : { } }
@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);
例10-14に示すように、JAXBは@XmlValueクラス
で1つ以上の@XmlAttributes
をサポートしています。
例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; } }
有効なJSONドキュメントを生成するために、EclipseLinkは例10-15に示すようにvalue
のラッパーを使用します。
例10-15 値ラッパーの使用
{ "employee" : { "name" : "Bob Smith", "mainPhone" : { "areaCode" : "613", "value" : "555-5555" }, "otherPhones" : [ { "areaCode" : "613", "value" : "123-1234" }, { "areaCode" : "613", "value" : "345-3456" } ] } }
デフォルトでは、EclipseLinkはvalueをラッパーの名前として使用します。例10-16に示すように、JSON_VALUE_WRAPPER
プロパティを使用して値ラッパーの名前をカスタマイズします。
例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に示すように、JAXBContext
を作成する際に、使用されるプロパティのMap
にJSON_VALUE_WRAPPER
プロパティを指定できます。
例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();
マップに指定すると、JAXBContent
から作成されたマーシャラおよびアンマーシャラは、自動的に指定した値ラッパーを使用します。