Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JJUG Java仕様勉強会「JSON Processing / JSON Binding」

JJUG Java仕様勉強会「JSON Processing / JSON Binding」

2023/02/16に開催されたJJUG(日本Javaユーザーグループ)Java仕様勉強会「JSON Processing / JSON Binding」の発表資料です。
https://jjug.doorkeeper.jp/events/149818

当日の録画はこちらです。
https://www.youtube.com/watch?v=E-rPycGxG8E

Takakiyo Tanaka

February 16, 2023
Tweet

More Decks by Takakiyo Tanaka

Other Decks in Technology

Transcript

  1. 2 ⾃⼰紹介 2 n ⽥中 孝清 n ⽇本アイ・ビー・エム株式会社 オートメーションソフトウェア テクニカルセールス

    n WebSphere Application Serverなどの テクニカルセールスを20年以上担当 n Twitter @TTakakiyo
  2. 4 JSONとは(1) n JavaScript Object Notation – JavaScriptにおけるオブジェクトの表記法をベースとした軽量なデータ記述⾔語 – あらゆるプログラミング⾔語で容易に扱うことができ,汎⽤のデータ交換フォーマットとして多⽤

    – RESTfulなWebサービスで,主要なデータ型として使⽤されている n JSONの構⽂ – JSONのデータ構造はオブジェクトと配列の2つだけ定義されている • { } オブジェクト { "key" : "value" } • [ ] 配列 – 7つのvalue typeが定義されている • string, number, true, false, null, object, array 4 { "firstName": "John", "lastName": "Smith", "age": 25, "address" : { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": "10021" }, "phoneNumber": [ { "type": "home", "number": "212 555-1234" }, { "type": "fax", "number": "646 555-4567" } ] }
  3. 5 JSONとは(2) n ⽶国Yahoo!でSenior JavaScript Architectをしていた Douglas Crockford⽒が2001年頃に命名 – Douglas

    「私はJSONの考案者ではなく発⾒者だ」 – それ以前からも,似たようなデータ型の使⽤例がある – 単純かつ⾃然なデータ構造 – 2006年にRFC 4627で仕様が規定 – 左図のBNF記法が,ほぼ仕様の全て – 外部参照も実体参照も16進数値の仕組みもなし (コメント構⽂すらなし) n XMLの仕様が – 6章の本編仕様,8章の名前空間仕様, スキーマが三部(5章×3)の仕様群 n YAMLの仕様が – 10章,4階層の仕様書 などと⽐べると,圧倒的な単純さ 5 json element value object array string number "true" "false" "null" object '{' ws '}' '{' members '}' members member member ',' members member ws string ws ':' element array '[' ws ']' '[' elements ']' elements element element ',' elements element ws value ws string '"' characters '"' characters "" character characters character '0020' . '10FFFF' - '"' - '¥' '¥' escape escape '"' '¥' '/' 'b' 'f' 'n' 'r' 't' 'u' hex hex hex hex hex digit 'A' . 'F' 'a' . 'f' number integer fraction exponent integer digit onenine digits '-' digit '-' onenine digits digits digit digit digits digit '0' onenine onenine '1' . '9' fraction "" '.' digits exponent "" 'E' sign digits 'e' sign digits sign "" '+' '-' ws "" '0020' ws '000A' ws '000D' ws '0009' ws
  4. 6 エンタープライズJavaにおけるJSONの標準仕様 n SOAP通信の仕様を参考にする形でRESTfulの仕様が策定 6 SOAP通信(主にXLMを使⽤) RESTful通信(主にJSONを使⽤) システム間連携・Webサービス JAX-RPC ⇒

    Jakarta XML RPC JAX-WS ⇒ Jakarta XML Web Services JAX-RS ⇒ Jakarta RESTful Web Services オブジェクトモデルでデータを操作 JAX-P(Java SE仕様) JSON-P ⇒ Jakarta JSON Processing ストリームモデルでデータを操作 StAX(Java SE仕様) データとJavaのオブジェクトを 相互変換 JAX-B ⇒ Jakarta XML Binding JSON-B ⇒ Jakarta JSON Binding Java EE仕様 ⇒ Jakarta EE仕様
  5. 7 各仕様でのJSONの対応 n Java EE – Java EE 7以降のFull Platform,Web

    ProfileでJSON-Pを使⽤可能 – Java EE 8のFull Platform,Web ProfileでJSON-Bを使⽤可能 n Jakarta EE – Full Platform,Web Profile,Core Profileの全てで, Jakarta JSON Processingおよび Jakarta JSON Bindingが使⽤可能 7
  6. 8 8 JSON-P Java EE: JSON-P (Java API for JSON

    Processing) Jakarta EE: Jakarta JSON Processing
  7. 9 JSON Processing n JSONを直接あつかい, ⽣成,解析,照会,変換を⾏うためのAPIを提供する n Java EE 7の⼀部としてJSR

    353で仕様化 n JSON-Pとも略されるが, ハイフンなしのJSONPだと別の意味になるので検索時に注意 – JSONP(JSON with padding)は, JavaScriptでクロスサイトオリジンの境界を越えるためのテクニック n プログラミングモデルとして下記2つのモデルが定義されている – Object Model (javax.json / jakarta.jsonパッケージ) • メモリ上にJSONデータを表すツリー構造のオブジェクトを作成する • XMLのDocument Object Model(DOM) APIに類似したAPI – Streaming Model (javax.json.stream / jakarta.json.streamパッケージ) • イベントベースで1度に1エレメントずつJSONデータを解析する • 1度に1エレメントずつストリームへ書き込む • XMLのStreaming API for XML(StAX)に類似したAPI 9
  8. 10 Object Model APIにおけるJSONデータの表現 10 § JSONデータを表現するインターフェース § EnumとしてJsonValueのタイプが定義されている インターフェース

    説明 JsonValue JSONデータのエレメントを表現する。JsonStructure,JsonString,JsonNumberのスーパータイプ。 JsonStructure JsonValueのサブタイプ。JSONデータのオブジェクトあるいは配列を表す。 JsonObject JsonArray JsonStructureのサブタイプ。 JsonObjectはオブジェクト、JsonArrayは配列を表す。 JsonString JsonNumber JsonValueのサブタイプ。 JsonStringは⽂字列、JsonNumberは数値を表す。 Enum 説明 JsonValue.ValueType JsonValueオブジェクトのタイプ を⽰す。 • OBJECT • ARRAY • STRING • NUMBER • TRUE • FALSE • NULL JsonStructure JsonValue JsonNumber JsonString JsonArray JsonObject
  9. 11 Object Modelの作成やストリームへの読み取り/書き込みのためのAPI 11 § ストリームからの読み取り、Object Modelの作成、ストリームへの書き込みに関するクラスおよびインターフェース § 例外に関するインターフェース API

    説明 JsonException JSON処理中に例外が発⽣したことを⽰す。 API 説明 Json JSON Processingのオブジェクトを作成するためのファクトリークラス。 createReaderやcreateWriter、createObjectBuilderなどの静的メソッドが定義されている。 JsonReader ストリームからJSONデータを読み込み、メモリ上にObject Modelを作成する。 JsonWriter メモリ上のObject Modelをストリームに書き出す。 JsonObjectBuilder アプリケーションコードからエレメントを追加していくことでJsonObjectを作成する。 JsonArrayBuilder アプリケーションコードからエレメントを追加していくことでJsonArrayを作成する。
  10. 12 Object Model API: アプリケーションコードでObject Modelを作成 n Json.createObjectBuilder()およびJson.createArrayBuilder()メソッドを使⽤して JsonObject/JsonArrayを作成できる n

    OjbectでもArrayでもないJsonValueはJson.createValue(...)で作成できる 12 import javax.json.Json; import javax.json.JsonObject; ... JsonObject jsonobj = Json.createObjectBuilder() .add("firstName", "John") .add("lastName", "Smith") .add("age", 25) .add("address", Json.createObjectBuilder() .add("streetAddress", "21 2nd Street") .add("city", "New York") .add("state", "NY") .add("postalCode", "10021")) .add("phoneNumber", Json.createArrayBuilder() .add(Json.createObjectBuilder() .add("type", "home") .add("number", "212 555-1234")) .add(Json.createObjectBuilder() .add("type", "fax") .add("number", "646 555-4567"))) .build(); { "firstName": "John", "lastName": "Smith", "age": 25, "address" : { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": "10021" }, "phoneNumber": [ { "type": "home", "number": "212 555-1234" }, { "type": "fax", "number": "646 555-4567" } ] }
  11. 13 Object Model API: JSONデータを読み込んでObject Modelを作成 n テキストファイル内のJSONデータを読み込み,メモリ上にObject Modelを作成 –

    JsonReaderインターフェースを利⽤する 13 import java.io.FileReader; import javax.json.Json; import javax.json.JsonReader; import javax.json.JsonStructure; ... //テキストファイルからJSONデータを読み込むためにFileReaderクラスをインスタンス化する //jsondata.txtにJSONデータが格納されている FileReader fr = new FileReader("jsondata.txt"); //JsonReaderを作成する JsonReader reader = Json.createReader(fr)); //JSONデータを読み込み、JsonValueを作成する JsonValue jsonval = reader.readValue(); //作成したJsonValueをJSONテキストにして標準出⼒に出⼒する System.out.println(jsonval.toString()); { "firstName": "John", "lastName": "Smith", "age": 25, "address" : { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": "10021" }, "phoneNumber": [ { "type": "home", "number": "212 555-1234" }, { "type": "fax", "number": "646 555-4567" } ] } jsondata.txt
  12. 14 Object Model API: Object Modelのツリー構造をナビゲーション(1) n メモリ上に作成されたObject Modelはツリー構造になっており、 ツリー内の各要素にランダムアクセスが可能

    14 public static void navigateTree(JsonValue tree, String key) { if (key != null) System.out.print("Key " + key + ": "); //JsonValueインターフェースのgetValueType()メソッドは //JsonValue型(Enum JsonValue.ValueType)を戻す switch(tree.getValueType()) { case OBJECT: System.out.println("OBJECT"); JsonObject object = (JsonObject) tree; //java.util.Mapインターフェースから継承されたKeySet()メソッドで //JSONオブジェクトのkeyのセットを取得する for (String name : object.keySet()) //get()メソッドでJSONオブジェクトのkeyの名前を取得する navigateTree(object.get(name), name); break; case ARRAY: System.out.println("ARRAY"); JsonArray array = (JsonArray) tree; for (JsonValue val : array) navigateTree(val, null); break; //つづく→ // ←つづき case STRING: JsonString st = (JsonString) tree; System.out.println("STRING " + st.getString()); break; case NUMBER: JsonNumber num = (JsonNumber) tree; System.out.println("NUMBER " + num.toString()); break; case TRUE: case FALSE: case NULL: System.out.println(tree.getValueType().toString()); break; } }
  13. 15 Object Model API: Object Modelのツリー構造をナビゲーション(2) OBJECT Key firstName: STRING

    John Key lastName: STRING Smith Key age: NUMBER 25 Key address: OBJECT Key streetAddress: STRING 21 2nd Street Key city: STRING New York Key state: STRING NY Key postalCode: STRING 10021 Key phoneNumber: ARRAY OBJECT Key type: STRING home Key number: STRING 212 555-1234 OBJECT Key type: STRING fax Key number: STRING 646 555-4567 { "firstName": "John", "lastName": "Smith", "age": 25, "address" : { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": "10021" }, "phoneNumber": [ { "type": "home", "number": "212 555-1234" }, { "type": "fax", "number": "646 555-4567" } ] } jsondata.txt 標準出⼒ //jsondata.txtからJSONデータからJsonValueオブジェクトを作成し、navigateTreeメソッドの引数に指定する navigateTree((JsonValue)Json.createReader(new FileReader("jsondata.txt")).read(),null);
  14. 16 Streaming APIで利⽤する主なAPI § インターフェース § Enum § 例外 API

    説明 JsonParser ストリームやObject ModelからJSONデータを読むことができるイベントベースのParserを表す JsonGenerator ⼀度にひとつのエレメントをストリームに書き出す API 説明 JsonParser.Event JsonParserのイベント。以下10個のイベントが定義されている START_OBJECT/START_ARRAY/END_OBJECT/END_ARRAY/KEY_NAME/VALUE_STRING/ VALUE_NUMBER/VALUE_TRUE/VALUE_FALSE/VALUE_NULL API 説明 JsonGenerationException 不適当なJSONが⽣成されたことを⽰す JsonParsingException 不適当なJSONを解析したことを⽰す
  15. 17 Streaming API: JsonParserによるJSONデータの解析 n イベントベースのJsonParserによって ストリームおよびObject ModelからJSONデータを解析することができる 17 JsonParser

    parser = Json.createParser(new StringReader(jsondata)); //JsonParserインスタンスを取得 while (parser.hasNext()){ //parser.hasNext()メソッドで繰り返し処理を⾏う JsonParser.Event event = parser.next(); //次の解析のためのイベントを返す switch(event){ case START_ARRAY: case END_ARRAY: case START_OBJECT: case END_OBJECT: case VALUE_FALSE: case VALUE_NULL: case VALUE_TRUE: //イベント名を返す System.out.println(event.toString()); break; case KEY_NAME: //KEY_NAMEの名前を返す System.out.print(event.toString() + " " + parser.getString() + " - "); break; case VALUE_STRING: case VALUE_NUMBER: //string値あるいはnumber値を返す System.out.println(event.toString() + " " + parser.getString()); break; } START_OBJECT KEY_NAME firstName - VALUE_STRING John KEY_NAME lastName - VALUE_STRING Smith KEY_NAME age - VALUE_NUMBER 25 KEY_NAME address - START_OBJECT KEY_NAME streetAddress - VALUE_STRING 21 2nd Street KEY_NAME city - VALUE_STRING New York KEY_NAME state - VALUE_STRING NY KEY_NAME postalCode - VALUE_STRING 10021 END_OBJECT KEY_NAME phoneNumber - START_ARRAY START_OBJECT KEY_NAME type - VALUE_STRING home KEY_NAME number - VALUE_STRING 212 555-1234 END_OBJECT START_OBJECT KEY_NAME type - VALUE_STRING fax KEY_NAME number - VALUE_STRING 646 555-4567 END_OBJECT END_ARRAY END_OBJECT
  16. 18 Streaming API: JsonGeneratorによるJSONデータの⽣成 n JsonGeneratorを使⽤してJSONデータを ストリームに書き込むことかできる – 1度に1要素ずつ書く –

    writeStartObject() ・・・Objectを開始する⽂字を書く – writeStartArray() ・・・配列を開始する⽂字を書く – write() ・・・現在のオブジェクトあるいは配列コンテキストに書く – writeEnd() ・・・現在のコンテキストの終了を書く 18 FileWriter writer = new FileWriter("test.txt"); JsonGenerator gen = Json.createGenerator(writer); gen.writeStartObject() .write("firstName", "John") .write("lastName", "Smith") .write("age", 25) .write("streetAddress", "21 2nd Street") .write("city", "New York") .write("state", "NY") .write("postalCode", "10021") .writeStartArray("phoneNumbers") .writeStartObject() .write("type", "home") .write("number", "212 555-1234") .writeEnd() .writeStartObject() .write("type", "fax") .write("number", " 646 555-4567") .writeEnd() .writeEnd() .writeEnd(); gen.close();
  17. 19 Object Model APIとStreaming APIの⽐較 n Object Model APIとStreaming APIを⽐較すると以下のような特徴がある

    19 ⽐較軸 Object Model API Streaming API 柔軟性 ◦ - 効率性 - ◦ ストリームからの 読み込み/書き込み オブジェクト単位 1度に1エレメントずつ コードの記述量 少ない 多い 処理速度 若⼲遅い 若⼲早い メモリ使⽤量 若⼲多い 若⼲少ない XMLの類似API Document Object Model(DOM) API Streaming API for XML(StAX) ⽤途 ツリー内のコンテンツに ランダムアクセスが必要な処理に 適している エレメントの処理に 残りのデータを必要としないような ローカル処理に適している
  18. 20 20 JSON-B Java EE: JSON-B (Java API for JSON

    Binding) Jakarta EE: Jakarta JSON Binding
  19. 21 JSON Binding n JavaオブジェクトからJSONドキュメントへの変換(シリアライズ) JSONドキュメントからJavaオブジェクトへの逆変換(デシリアライズ)を 単純かつ統⼀的に⾏うためのAPI n Java EE

    8の⼀部としてJSR 367で仕様化「Java API for JSON Binding」 – 各種仕様書では「JSON-B」と省略されることが多い n Jakarta EEでも「Jakarta JSON Binding」として継続し,仕様の改善が続く – Jakartaでは,あまり省略されない 21 json { "xx": "yy" } Java Data シリアライズ︓toJson() デシリアライズ︓fromJson() メソッドが統⼀的 クラス
  20. 22 JSON Bindingのパッケージ構成 n Java EE(Jakarta EE 8)では,下記のようなパッケージで構成 (Jakarta EE

    9以降はjakartaパッケージ) 22 Package API(Exceptionは省略) Description javax.json.bind Interface: Jsonb, JsonbBuilder toJson()やfromJson()といった シリアライズ、デシリアライズするためのInterface javax.json.bind.adapter Interface: JsonbAdapter JsonbAdapter Interfaceを継承し実装することで、 カスタムマッピングを⾏う javax.json.bind.annotation Interface: JsonbAnnotation アノテーションを付与することで 出⼒形式をカスタマイズする javax.json.bind.config Interface: PropertyNamingStrategy, PropertyVisibility Strategy 出⼒順序やフィールドの可視性をカスタマイズする javax.json.bind.serializer Interface: DeserializeContext, JsonbDeserializer<T> JsonbSerializer<T>, SerializationContext JsonbSerializerまたは JsonbDeserializer Interfaceを継承し、 実装することでカスタムマッピングを実現 javax.json.bind.spi JsonbProvider (本資料では解説省略) JsonbBuilderの機能を補完するためのプラグイン
  21. 23 JSON Bindingの実装イメージ n 基本的にJsonb、JsonbBuilderインターフェースを⽤いて シリアライズ(toJson()),デシリアライズ(fromJson())を⾏う n 出⼒形式をカスタマイズしたい時はadapter, serializer, annotation,

    configパッケージを⽤いる Java Class JSON Doc javax.json.bind.Jsonb javax.json.bind.JsonbBuilder toJson() fromJson() javax.json.bind.annotation javax.json.bind.config javax.json.bind.adapter javax.json.bind.serializer カスタマイズ シリアライズ アノテーションによるカスタマイズ デシリアライズ JsonbConfigインスタンスの利⽤ JsonbAdapterの実装 JsonbSerializerの実装
  22. 24 JSON Bindingによるシリアライズ 24 Empoloyee クラス フィールド インスタンス化 String id

    a1234 String name fukui Stiring department cloud ① シリアライズするJavaクラス、インスタンスを作成 ② JsonbBuilder のstatic メソッド, create()を利⽤しJsonb型のインスタンスを作成 Jsonb jsonb = JsonbBuilder.create() ③ ②で作成したJsonbインスタンスが持つtoJson()メソッドの引数に①のインスタンスを与える jsonb.toJson( ) Empoloyee インスタンス { "department": "cloud", "id": "a1234", "name": "fukui" } ex)Employeeクラスのインスタンスをシリアライズする デフォルトのストラテジーでは • JSONのkeyはフィールド名が引き継がれる • 値がnullのフィールドは無視される(JSONに出⼒されない)
  23. 25 JSON Bindingによるシリアライズの実装例 25 ① シリアライズするためのJavaクラス、インスタンスを作成 ② Jsonbインスタンスを作成し、JsonBuilderのcreate()を使⽤ ③ toJson(obj)を使⽤(obj

    はシリアライズ対象のオブジェクト) public class Employee { private String id; private String name; private String department; public Employee() { } public Employee(String id, String name, String department) { this.id = id; this.name = name; this.department = department; } ... (セッター、ゲッターは省略(privateフィールドでは必須)) クラス メソッド定義 import javax.json.bind.Jsonb; import javax.json.bind.JsonbBuilder; ... public class SampleMethod { public String serializeEmployee(Employee emp) { Jsonb jsonb = JsonbBuilder.create(); String json = jsonb.toJson(emp); return json; } } シリアライズを⾏うメソッド定義 ① ② ③ インスタンス格納
  24. 26 JSON Bindingによるデシリアライズ 26 ①, シリアライズするJSONを⽤意 ②, JsonbBuilder のstatic メソッド,

    create()を利⽤したJsonb型のインスタンスを作成する。 ->デシリアライズと同様 Jsonb jsonb = JsonbBuilder.create() ③, ②で作成したJsonbインスタンスが持つfromJson()メソッドの第⼀引数にJSONドキュメント、第⼆引数に該当クラスを指定 jsonb.fromJson( , Employee.class ) JSON ドキュメント { "department": "cloud", "id": "a1234", "name": "fukui" } • JSONのkeyと⼀致するフィールド名が読み込まれる(デフォルトでは⼤⽂字⼩⽂字は区別されるので注意)
  25. 27 JSON Bindingによるデシリアライズの実装例 27 ① Javaクラス、インスタンスを作成、デシリアライズするJson⽂字列を取得 ② Jsonbインスタンスを作成し、JsonBuilderのcreate()を使⽤ ③ fromJson(json,

    Obj.class)を使⽤(json は⽂字列, Objは帰属されるクラス) public class Employee { private String id; private String name; private String department; public Emp() {} ...(コンストラクタ、セッター、ゲッター省略) } デフォルトコンストラクタ必須! import javax.json.bind.Jsonb; import javax.json.bind.JsonbBuilder; ... public class Sample { public Employee deserializeEmployee(String jsonEmployee) { Jsonb jsonb = JsonbBuilder.create(); Employee empClass = jsonb.fromJson(jsonEmployee, Employee.class); return empClass; } } ① ② ③ JSON⽂字列 帰属されるクラス クラス メソッド定義
  26. 28 JSON Bindingのデフォルトのマッピング(1) 28 § 基本型のマッピング – 基本データ型についてはデフォルトで下記のようにマッピング • String,

    char型はJSONで⽂字列に解釈される • Float, double, int, AtomicInteger, Byte型は数値として解釈される • boolean型は真偽値で解釈される public class BasicTypes { private String name; private char initial; private Float height; private double weight; private int age; private boolean isPassed; private AtomicInteger count; private Byte version; (セッター・ゲッター省略) } { "name":"Fukui", "initial":"F", "height":169.99, "weight":60.0, "age":20, "isPassed":true "count":4, "version":5, } BasicTypes basic = new BasicTypes(); basic.setName("Fukui"); basic.setInitial('F'); basic.setHeight(169.99f); basic.setWeight(60.000); basic.setAge(20); basic.setIsPassed(true); basic.setCount(new AtomicInteger(4)); basic.setVersion((byte) 5); クラス 値を格納 出⼒(実際には辞書式で出⼒)
  27. 29 JSON Bindingのデフォルトのマッピング(2) 29 § 配列・コレクション型のシリアライズ – List型, 固定⻑配列ともに、JSONドキュメントとしては配列として解釈される –

    Map<Key, Value>はネストされたオブジェクトとして{"key": "value"}が⽣成される – MapやList型のインスタンスにnull値が含まれる場合は、配列にnullと表⽰される { "ints": [1,2,3,4,5], "ints2d": [[1,2],[3,4],[5,6],[7,8]], "strs": ["aaa","bbb",null] "list": ["Java EE","Python"], "map": { "Fukui": 26, "Problem": null }, "set": ["Red"], } public class ArraysAndCollections { private int[] ints = new int[5]; private int[][] ints2d = new int[5][]; private String[] strs = new String[3]; private List<String> list; private Map<String, Integer> map; private Set<String> set; (セッター・ゲッター省略) } ArraysAndCollections ac = new ArraysAndCollections(); ac.setInts(new int[]{1, 2, 3, 4, 5}); ac.setInts2d(new int[][]{{1, 2}, {3, 4}, {5, 6}, {7, 8}}); ac.setStrs(new String[]{"aaa", "bbb" , null}); ac.setList(new ArrayList<String>() {{ add("Java EE"); add("Python"); }}); ac.setMap(new HashMap<String, Integer>() {{ put("Fukui", 26); put("Problem", null); }}); ac.setSet(new HashSet<String>() {{ add("Red"); }});
  28. 30 JSON Bindingのカスタマイズ ⼤きく⼆つの⽅法(詳細は時間の関係で割愛) n シリアライズ・デシリアライズするJavaクラスに アノテーションをつけてカスタマイズする n Jsonbをビルドする際にJsonConfigでカスタマイズする –

    変換の際の命名ストラテジーや順序ストラテジーなどの指定 – 数値,⽇付のフォーマットなどの指定 – 独⾃に実装したJsonbAdapterやJsonbSerializerの指定 etc. 30 @JsonbProperty("ID") public String userId; // JSONでのkeyを指定 @JsonbTransient public boolean internalFlag; // JSONへの出⼒を抑⽌ @JsonbDateFormat(value = "yyyy年MM⽉dd⽇", locale = "Locale.JAPANESE") public LocalDate when; // JSONへ出⼒する⽇付フォーマットを指定 Jsonb jsonb = JsonbBuilder.create(jsonbConfig); String json = jsonb.toJson(emp);
  29. 31 JSON BindingとJSON Processingの統合 n JSON Bindingの実装は, 必ずJSON Processingをサポートしなければならないと 仕様で規定されている

    n つまり,JsonValueやJsonObjectなどののJSON Processingの型は, toJson() にかけるとJSON⽂字列が得られるし, JSON⽂字列からfromJson()でJsonValueなどを得ることができる 31 シリアライズ︓toJson() デシリアライズ︓fromJson() { "xx": "yy" } "xx": "yy" JsonObject ⽂字列
  30. 32 参考情報︓JSON Binding 3.0の新機能 n JSON Binding 3.0の新機能については,寺⽥佳央さんの JakartaOne LiveStream

    Japan 2022のセッションを参照 「CN4J Studio Jakarta JSON Binding の概要と便利な使い⽅について」 https://www.youtube.com/watch?v=rj-NAGYlZRI – Polymorphic型 – Recordのサポート – JSON Bindingsのカスタマイズ 32
  31. 34 MediaType.APPLICATION_JSONでStringを返しても... 34 @GET @Produces(MediaType.APPLICATION_JSON) @Path("/bad") public String badJson() {

    return "This is a ¥"test¥"."; } curl -v http://192.168.140.1:9080/JsonTest/jsonb/bad ... < HTTP/1.1 200 OK < X-Powered-By: Servlet/4.0 < Date: Wed, 15 Feb 2023 21:48:22 GMT < Content-Type: application/json < Content-Language: en-US < Content-Length: 17 < * Connection #0 to host 192.168.140.1 left intact This is a "test". ⽂字列はJSONのvalueだが, " "でクオートされていないと 正しいJSONではない
  32. 35 正しくJSONにするには 35 @GET @Produces(MediaType.APPLICATION_JSON) @Path("/good") public JsonValue goodJson() {

    return Json.createValue("This is a ¥"test¥"."); } curl -v http://192.168.140.1:9080/JsonTest/jsonb/good ... < HTTP/1.1 200 OK < X-Powered-By: Servlet/4.0 < Date: Wed, 15 Feb 2023 21:55:21 GMT < Content-Type: application/json < Content-Language: en-US < Content-Length: 21 < * Connection #0 to host 192.168.140.1 left intact "This is a ¥"test¥"." " "でクオートされ ⽂字列中の"もエスケープされ 正しいJSONになっている
  33. 36 何が起こっている︖ n JAX-RSでは以下のような処理を⾏うProviderが提供され, 状況に応じて選択されて利⽤されている – 要求ストリームを処理してJavaのオブジェクトに変換するMessageBodyReader – Javaのオブジェクトを変換して応答ストリームに書き込むMessageBodyWriter –

    発⽣した例外を応答にマッピングするExceptionMapper – コンテキスト情報を提供するContextResolver n 独⾃のMessageBodyWriterなどを実装して,Providerとして組み込むこともできる n 現在の状況(Context)で,どのProviderが選択されるかを調べるProvidersという インターフェースが提供されている n 以上のしくみは,javax.ws.rs.ext / jakarta.ws.rs.ext パッケージで提供されている 36
  34. 37 どのようなMessageBodyWriterが選択されるか調べてみる(1) 37 @javax.ws.rs.core.Context private javax.ws.rs.ext.Providers providers; private String getWiterName(Class

    c) { return providers .getMessageBodyWriter(c, null, null, MediaType.APPLICATION_JSON_TYPE) .toString(); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/providers") public JsonValue providers() { return Json.createObjectBuilder() .add("int", getWiterName(Integer.TYPE)) .add("byte[]", getWiterName(byte[].class)) .add("String", getWiterName(String.class)) .add("JsonValue", getWiterName(javax.json.JsonValue.class)) .add("Properties", getWiterName(java.util.Properties.class)) .build(); }
  35. 38 どのようなMessageBodyWriterが選択されるか調べてみる(2) n 実⾏結果 (Open Libertyでjaxrs-2.1フィーチャーを有効にして実⾏) n ほとんどJSON-BのProviderが使⽤されているが, byte[]やStringでは,別のProviderが利⽤されてる 38

    $ curl http://localhost:9080/JsonTest/jsonb/providers | jq % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 346 100 346 0 0 15477 0 --:--:-- --:--:-- --:--:-- 19222 { "int": "com.ibm.ws.jaxrs21.providers.json.JsonBProvider@ecce30b8", "byte[]": "org.apache.cxf.jaxrs.provider.BinaryDataProvider@b192ae45", "String": "org.apache.cxf.jaxrs.provider.StringTextProvider@ec4ab4c", "JsonValue": "com.ibm.ws.jaxrs21.providers.json.JsonBProvider@ecce30b8", "Properties": "com.ibm.ws.jaxrs21.providers.json.JsonBProvider@ecce30b8" }
  36. 39 MessageBodyWriter n JSON-B(JSON Binding)が利⽤できる環境で MediaType.APPLICATION_JSONなどが指定されたら, JSON-BをつかったMessageBodyWriterが選択される n JAX-B(XML Binding)が利⽤できる環境で

    MediaType.APPLICATION_XMLなどが指定されたら, JAX-BをつかったMessageBodyWriterが選択される n ただし,メソッドが以下のような型を返す場合には, 標準のMessageBodyWriter(何も変換しないでそのまま出⼒)が使⽤される – byte[], String – java.io.InputStream, java.io.Reader, java.io.File – javax.ws.rs.core.StreamingOutput, javax.activation.DataSouce – ⼀部のMediaTypeについては,これ以外の型についても標準のMessageBodyWriterが規定 (JSONは関わってこないので今回は割愛) n これらはMediaTypeなどにあわせて整形済みの情報が⼊っていることが想定されている – なので,MediaType.APPLICATION_JSONでStringを返すメソッドは,整形済みのJSONをかえすこと︕ 39
  37. 40 JAX-RSでのJSON-Bのカスタマイズ(1) n カスタマイズしたJsonbでシリアライズ・デシリアライズしたい時は Jsonbを提供するContextResolverを実装してProviderとして登録する – アプリケーションで@Providerでアノテーションされたクラスは⾃動的に組み込まれる 40 @javax.ws.rs.ext.Provider public

    class MyJsonbContentResolver implements javax.ws.rs.ext.ContextResolver<Jsonb> { @Override public Jsonb getContext(Class<?> c) { JsonbConfig config = new JsonbConfig() .withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE_WITH_SPACES); return JsonbBuilder.newBuilder().withConfig(config).build(); } }
  38. 41 JAX-RSでのJSON-Bのカスタマイズ(2) 41 @Path("/user") @Produces(MediaType.APPLICATION_JSON) public class MyJsonbResource { public

    static class User { public String userId = "A0001"; public String firstName = "Takakiyo"; public String lastName = "Tanaka"; } @GET public User getUser() { return new User(); } } { "First Name": "Takakiyo", "Last Name": "Tanaka", "User Id": "A0001" } { "firstName": "Takakiyo", "lastName": "Tanaka", "userId": "A0001" } 前⾴のContextResolverがないと 前⾴のContextResolverがあると PropertyNamingStrategy.UPPER_CAMEL_CASE_WITH_SPACESの効果
  39. 43 Open Liberty / WebSphere Libertyとは n IBMが開発・公開しているOSSのJava EE /

    Jakarta EE / MicroProfileのランタイム n Eclipse Public Licenseで提供 / 無償で利⽤可能 n Open Libertyの成果を取り込み,IBMが販売する製品 n アプリや構成はOpen Libertyと同じものを利⽤可能 / バージョンはOpen Libertyと完全に同期 43 Open Liberty
  40. 44 WebSphere Liberty / Open Liberty コミュニティサイト nhttps://ibm.biz/JapanWebSphereUG – Japan

    WebSphere User Group – ログイン無しでどなたでも参照できます – 無料のIBM IDを登録いただければ ディスカッション・Q&Aなどの 書き込みもできます – IBMからの技術情報 – イベントの案内 25周年イベントの情報もお伝えします︕ 44