ヘッダーをスキップ
Oracle® Fusion Middleware Oracle TopLinkソリューション・ガイド
12c (12.1.3)
E57549-01
  ドキュメント・ライブラリへ移動
ライブラリ
製品リストへ移動
製品
目次へ移動
目次

前
 
次
 

16 JSONドキュメントからのオブジェクトの変換

この章では、JSON (JavaScript Object Notation)からまたはJSONへオブジェクトを変換する場合、Oracle TopLinkがそれをどのようにサポートするかを説明します。この機能は、RESTfulサービスを作成する際に便利です(JAX-RSサービスはXMLとJSONの両方のメッセージを受け取ります)。

この章の内容は次のとおりです。

ユース・ケース

JSONドキュメントからまたはJSONドキュメントへユーザーがオブジェクトを変換する必要があります。

解決方法

TopLinkでは、EclipseLinkのMOXyの実装を介してJSONをサポートしています。

コンポーネント

関連情報については、次のEclipseLinkの例を参照してください。

16.1 ソリューションの概要

EclipseLinkでは、JSONから読み書きをする際のMOXyのオブジェクトからXMLへのオプションをすべてサポートしています。

16.2 ソリューションの実装

この項では、JSONドキュメントから、またJSONドキュメントにオブジェクトを変換する際の次のタスクについて説明します。

16.2.1 タスク1: 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の作成時、使用しているプロパティのMapeclipselink.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のとおり、MarshallerPropertiesUnmarshallerPropertiesおよび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.2.2 タスク2: 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-6JAXBContextをブートストラップする際に、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"));
...

16.2.3 タスク3: JSONデータ型の指定

XMLでは単一のデータ型を持ちますが、JSONでは文字列、数値およびブールを区別します。例16-7に示すように、EclipseLinkはこれらのデータ型を自動的にサポートします。

例16-7 JSONデータ型の使用

public class Address {
 
   private int id;
   private String city;
   private boolean isMailingAddress;
 
}
 
{
   "id" : 1,
   "city" : "Ottawa",
   "isMailingAddress" : true
}

16.2.4 タスク4: 属性のサポート

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プロパティを設定することも可能です。コンテキストから作成されたすべてのマーシャラおよびアンマーシャラで、指定された接頭辞を使用します。

例16-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);

16.2.5 タスク5: ルート要素の非サポート

EclipseLinkでは、ルート要素のないJSONドキュメントをサポートしています。デフォルトでは、@XmlRootElement注釈が全く存在しない場合、マーシャルされたJSONドキュメントにはルート要素がありません。この動作は、例16-10のとおり、ドキュメントのマーシャル時に(@XmlRootElementが指定されている場合もJSONの出力からルート要素を省くこと)JSON_INCLUDE_ROOTプロパティを設定することによってオーバーライドできます。

例16-10 ルート要素のないドキュメントのマーシャル

marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);

ルート要素のないドキュメントをアンマーシャルする場合、例16-11のとおり、アンマーシャルするクラスを指定する必要があります。

例16-11 ルート要素のないドキュメントのアンマーシャル

unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);
JAXBElement<SearchResults> jaxbElement = unmarshaller.unmarshal(source, SearchResults.class);

注意:

ドキュメントにルート要素がない場合は、アンマーシャルするクラスを指定する必要があります。


16.2.6 タスク5 名前空間の使用

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_MAPPERJSONのみのプロパティです。XMLのアンマーシャルでは、ドキュメントから名前空間の情報が直接取得されます。

JSONがマーシャルされると、名前空間はドット( . )で区切られた接頭辞をMapより取得します。

{
   "ns1.employee : {
      "ns2.id" : 123
   }
}
 

ドットのセパレータは、JSON_NAMESPACE_SEPARATORプロパティを使用して任意のカスタム文字に設定できます。ここではかわりにコロンを使用します( : )。

jsonMarshaller.setProperty(MarshallerProperties.JSON_NAMESPACE_SEPARATOR, ':');
jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_NAMESPACE_SEPARATOR, ':');

16.2.7 タスク6: コレクションの使用

JSONにマーシャルする際に、例16-13のとおり、EclipseLinkはデフォルトで空のコレクションを[ ]としてマーシャルします。

例16-13 空のコレクションのマーシャリング

{
   "phone" : {
      "myList" : [ ]
   }
}
 

この動作をオーバーライド(空のコレクションに対しマーシャルをまったく行わないように)するには、JSON_MARSHAL_EMPTY_COLLECTIONSプロパティを使用します。

jsonMarshaller.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, Boolean.FALSE) ;
{
   "phone" : {
   }
}
 

16.2.8 タスク7: ルートレベル・コレクションのマッピング

@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);

16.2.9 タスク8: テキスト値のラッピング

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の作成時、使用しているプロパティのMapJSON_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から作成されたマーシャラおよびアンマーシャラは、指定した値ラッパーを自動的に使用します。

16.3 その他の参考資料

この章のソリューションが実装されているその他のテクノロジおよびツールの詳細は、次の参考資料を参照してください。