この章では、JSON (JavaScript Object Notation)からまたはJSONへオブジェクトを変換する場合、Oracle TopLinkがそれをどのようにサポートするかを説明します。この機能は、RESTfulサービスを作成する際に便利です(JAX-RSサービスはXMLとJSONの両方のメッセージを受け取ります)。
この章の内容は次のとおりです。
ユース・ケース
JSONドキュメントからまたはJSONドキュメントへユーザーがオブジェクトを変換する必要があります。
解決方法
TopLinkでは、EclipseLinkのMOXyの実装を介してJSONをサポートしています。
コンポーネント
TopLink 12cリリース1 (12.1.2)以上。
注意: TopLinkのコア機能は、オープン・ソースのEclipse Foundationの永続性フレームワークであるEclipseLinkによって提供されています。EclipseLinkでは、Java Persistence API (JPA)、Java Architecture for XML Binding (JAXB)、および標準に基づいたその他の永続性テクノロジと、それらの標準の拡張が実装されます。TopLinkには、EclipseLinkのすべてに加え、Oracleの追加機能が含まれています。 |
JSONドキュメント。
例
関連情報については、次のEclipseLinkの例を参照してください。
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/JSON_Metadata
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/MOXy_JSON_Provider
EclipseLinkでは、JSONから読み書きをする際のMOXyのオブジェクトからXMLへのオプションをすべてサポートしています。
(JAXB仕様に加え) EclipseLinkの高度な拡張マッピング機能
外部のバインディング・ファイルへのマッピングの保存
動的JAXBを使用した動的モデルの作成
マルチテナント・アプリケーションをサポートする拡張可能なモデルの構築
この項では、JSONドキュメントから、またJSONドキュメントにオブジェクトを変換する際の次のタスクについて説明します。
JAXBマーシャラまたはアンマーシャラのeclipselink.media-typeプロパティを使用すると、例16-1のとおり、アプリケーションでJSONドキュメントを作成および使用できます。
例16-1 マーシャリングおよびアンマーシャリング
... Marshaller m = jaxbContext.createMarshaller(); m.setProperty("eclipselink.media-type", "application/json"); Unmarshaller u = jaxbContext.createUnmarshaller(); u.setProperty("eclipselink.media-type", "application/json"); ...
また、例16-2のとおり、JAXBContext
の作成時、使用しているプロパティのMap
にeclipselink.media-type
プロパティを指定することもできます。
例16-2 Mapの使用
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();
Mapに指定すると、JAXBContent
から作成されたマーシャラおよびアンマーシャラは、指定したメディア・タイプを自動的に使用します。
例16-3のとおり、MarshallerProperties
、UnmarshallerProperties
およびMediaType
定数を使用して、アプリケーションでJSONドキュメントを使用するよう構成できます。
例16-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); ...
例16-4では、JAXBの標準的な使用で必要な以外の、コンパイル時の依存関係を必要としない基本的なJSONのバインディングを示します。この例では、JSONをStreamSource
からユーザー・オブジェクトSearchResults
にアンマーシャルし、コレクションに新しいResult
を追加し、新しいコレクションをSystem.out
にマーシャルする方法を説明します。
例16-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ドキュメントとして記述できます。例16-5に、bindings.json
を使用し、JSONにCustomerおよびPhoneNumberクラスをマップする方法を説明します。
例16-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"} ] } } ] } }
例16-6にJAXBContext
をブートストラップする際に、JSONファイル(例16-5で作成)を使用する方法を説明します。
例16-6 JAXBContextのブートストラップでのJSONの使用
Map<String, Object> properties = new HashMap<String, Object>(2); properties.put("eclipselink.oxm.metadata-source", "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にはデータ型は1つしかありませんが、JSONでは文字列、数字、ブールを区別します。EclipseLinkでは、例16-7のとおりこれらのデータ型を自動的にサポートします。
JSONでは属性は使用せず、@XmlAttribute
注釈でマップされたものをすべて、要素としてマーシャルします。EclipseLinkは、デフォルトで属性および要素イベントを両方トリガーするので、マップされた属性または要素のいずれかが値を処理できるようになります。
この動作は、例16-8のとおり、JSON_ATTRIBUTE_PREFIX
プロパティを使用し、属性の接頭辞を指定することによってオーバーライドできます。EclipseLinkでは、マーシャル時に属性名に接頭辞を追加し、アンマーシャル時にそれを認識します。
次の例では、number
フィールドが接頭辞@とともに属性としてマップされます。
例16-8 接頭辞の使用
jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_ATTRIBUTE_PREFIX, "@"); jsonMarshaller.setProperty(MarshallerProperties.JSON_ATTRIBUTE_PREFIX, "@") ;
{
"phone" : {
"area-code" : "613",
"@number" : "1234567"
}
}
また、例16-9のとおり、JAXBContext
の作成時にMapでJSON_ATTRIBUTE_PREFIX
プロパティを設定することも可能です。このコンテキストで作成されるすべてのマーシャラおよびアンマーシャラでは、指定した接頭辞が使用されます。
EclipseLinkでは、ルート要素のないJSONドキュメントをサポートしています。デフォルトでは、@XmlRootElement
注釈が全く存在しない場合、マーシャルされたJSONドキュメントにはルート要素がありません。この動作は、例16-10のとおり、ドキュメントのマーシャル時に(@XmlRootElement
が指定されている場合もJSONの出力からルート要素を省くこと)JSON_INCLUDE_ROOT
プロパティを設定することによってオーバーライドできます。
ルート要素のないドキュメントをアンマーシャルする場合、例16-11のとおり、アンマーシャルするクラスを指定する必要があります。
例16-11 ルート要素のないドキュメントのアンマーシャル
unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false); JAXBElement<SearchResults> jaxbElement = unmarshaller.unmarshal(source, SearchResults.class);
注意: ドキュメントにルート要素がない場合は、アンマーシャルするクラスを指定する必要があります。 |
JSONでは名前空間を使用しないので、デフォルトではすべての名前空間および接頭辞はマーシャル時およびアンマーシャル時に無視されます。同じローカル名でマッピングが複数ある場合、マッピングを区別する方法がないので、場合によってはこれが問題になることがあります。
EclipseLinkでは、接頭辞を付ける名前空間のMap(またはNamespacePrefixMapper
のインスタンス)をマーシャラおよびアンマーシャラに提供できます。名前空間の接頭辞は、要素名が付加されマーシャルされたドキュメントに表示されます。EclipseLinkでは、アンマーシャル時に接頭辞を認識し、結果のJavaオブジェクトが正しい名前空間に配置されます。
例16-12に、NAMESPACE_PREFIX_MAPPER
プロパティの使用方法を示します。
例16-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がマーシャルされると、名前空間はドット( . )で区切られた接頭辞をMapより取得します。
{ "ns1.employee : { "ns2.id" : 123 } }
ドットのセパレータは、JSON_NAMESPACE_SEPARATOR
プロパティを使用して任意のカスタム文字に設定できます。ここではかわりにコロンを使用します( : )。
jsonMarshaller.setProperty(MarshallerProperties.JSON_NAMESPACE_SEPARATOR, ':'); jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_NAMESPACE_SEPARATOR, ':');
JSONにマーシャルする際に、例16-13のとおり、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);
JAXBでは、例16-14のとおり、@XmlValue
クラスで1つ以上の@XmlAttributes
をサポートしています。
例16-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では例16-15に示すvalue
のラッパーを使用します。
例16-15 valueラッパーの使用
{ "employee" : { "name" : "Bob Smith", "mainPhone" : { "areaCode" : "613", "value" : "555-5555" }, "otherPhones" : [ { "areaCode" : "613", "value" : "123-1234" }, { "areaCode" : "613", "value" : "345-3456" } ] } }
デフォルトでEclipseLinkは、ラッパー名としてvalueを使用します。値ラッパー名をカスタマイズするには、例16-16のとおり、JSON_VALUE_WRAPPER
プロパティを使用します。
例16-16 Valueラッパー名のカスタマイズ
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" } ] } }
また、例16-17のとおり、JAXBContext
の作成時、使用しているプロパティのMap
にJSON_VALUE_WRAPPER
プロパティを指定することもできます。
例16-17 Mapの使用
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();
Mapに指定すると、JAXBContent
から作成されたマーシャラおよびアンマーシャラは、指定した値ラッパーを自動的に使用します。
この章のソリューションが実装されているその他のテクノロジおよびツールの詳細は、次の参考資料を参照してください。
Oracle TopLinkによるJAXBアプリケーションの開発