この章では、JSON (JavaScript Object Notation)からまたはJSONへオブジェクトを変換する場合、Oracle TopLinkがそれをどのようにサポートするかを説明します。この機能は、RESTfulサービスを作成する際に便利です(JAX-RSサービスはXMLとJSONの両方のメッセージを受け取ります)。
この章の内容は次のとおりです。
ユース・ケース
JSONドキュメントからまたはJSONドキュメントへユーザーがオブジェクトを変換する必要があります。
解決方法
TopLinkでは、EclipseLinkのMOXyの実装を介してJSONをサポートしています。
コンポーネント
TopLink 12c (12.1.2.0.0)以上。
|
注意: 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ドキュメントにオブジェクトを変換する際の次のタスクについて説明します。
例16-1に示すように、JAXBマーシャラまたはJAXBアンマーシャラのeclipselink.media-typeプロパティを使用して、アプリケーションで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では単一のデータ型を持ちますが、JSONでは文字列、数値およびブールを区別します。例16-7に示すように、EclipseLinkはこれらのデータ型を自動的にサポートします。
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アプリケーションの開発