Java XML和JSON:Java SE的文檔處理 第2部分

在這篇文章中,咱們將繼續探索Java 11及更高版本中的XML和JSON。html

本文中的示例將向您介紹JSON-B,JSON綁定API for Java。在快速概述和安裝說明以後,我將向您展現如何使用JSON-B來序列化和反序列化Java對象,數組和集合; 如何使用JSON-B自定義序列化和反序列化; 以及如何在序列化或反序列化期間使用JSON-B適配器將源對象轉換爲目標對象。java

這篇文章的材料是全新的,但能夠被認爲是個人新書的另外一章(第13章),最近由Apress出版:git

什麼是JSON-B?

JSON-B
是一個標準的綁定層和API,用於將Java對象與JSON文檔進行轉換。它相似於XML綁定的Java體系結構(JAXB),它用於將Java對象轉換爲XML或從XML轉換成Java對象。

JSON-B構建於JSON-P之上,JSON-P是用於解析,生成,查詢和轉換JSON文檔的JSON處理API。JSON-B是由Java規範請求(JSR)367JSR 353(JSR for JSON-P)最終發佈一年多以後推出的。github

JSON-B API

JSON綁定Java API(JSON-B)網站引入了JSON-B並提供對各類資源的訪問,包括API文檔。根據文檔,JSON-B模塊存儲了六個包:json

  • javax.json.bind:定義將Java對象綁定到JSON文檔的入口點。
  • javax.json.bind.adapter:定義與適配器相關的類。
  • javax.json.bind.annotation:定義用於自定義Java程序元素和JSON文檔之間的映射的註釋。
  • javax.json.bind.config:定義用於自定義Java程序元素和JSON文檔之間的映射的策略和策略。
  • javax.json.bind.serializer:定義用於建立自定義序列化程序和反序列化程序的接口。
  • javax.json.bind.spi:定義用於插入自定義的服務提供者接口(SPI)JsonbBuilder

JSON-B網站還提供了Yasson的連接,Yasson是一個Java框架,提供Java類和JSON文檔之間的標準綁定層,以及JSON Binding API的官方參考實現。api

下載並安裝JSON-B

JSON-B 1.0是撰寫本文時的當前版本。您能夠從Maven存儲庫獲取此庫的Yasson參考實現。您須要下載如下JAR文件:數組

在編譯和運行使用這些庫的代碼時,將這些JAR文件添加到類路徑中:bash

javac -cp javax.json.bind-api-1.0.jar;. main source file
java -cp javax.json.bind-api-1.0.jar;yasson-1.0.3.jar;javax.json-1.1.4.jar;. main classfile複製代碼

使用JSON-B序列化和反序列化Java對象

javax.json.bind包提供了JsonbJsonbBuilder接口,它們充當此庫的入口點:架構

  • Jsonb提供了toJson()用於將Java對象的樹序列化爲JSON文檔的重載方法,以及fromJson()用於將JSON文檔反序列化爲Java對象樹的方法。
  • JsonbBuilder提供newBuilder()和其餘方法得到新的構建,並build()create()返回新方法Jsonb的對象。

如下代碼示例演示了JsonbJsonBuilder類型的基本用法:oracle

// Create a new Jsonb instance using the default JsonbBuilder implementation.
Jsonb jsonb = JsonbBuilder.create();

// Create an Employee object from a hypothetical Employee class.
Employee employee = ...

// Convert the Employee object to a JSON document stored in a string.
String jsonEmployee = jsonb.toJson(employee);

// Convert the previously-created JSON document to an Employee object.
Employee employee2 = jsonb.fromJson(jsonEmployee, Employee.class);複製代碼

此示例調用序列化Java對象JsonbString toJson(Object object)方法(Employee)。此方法傳遞給Java對象樹的根目錄以進行序列化。若是null經過,則toJson()拋出java.lang.NullPointerException。在序列化期間發生意外問題(例如I / O錯誤)時,它會拋出javax.json.bind.JsonbException

此代碼段還調用Jsonb<T> T fromJson(String str, Class<T> type)通用方法,該方法被用於反序列化。此方法傳遞基於字符串的JSON文檔以反序列化,並返回生成的Java對象樹的根對象的類型。傳遞給此方法任一參數爲null時拋出NullPointerException; 在反序列化期間發生意外問題時拋出JsonbException

我從一個JSONBDemo提供JSON-B基本演示的應用程序中摘錄了代碼片斷。清單1展現了此演示的源代碼。

清單1. JSONBDemo.java(版本1)

import java.time.LocalDate;

import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;

public class JSONBDemo
{
   public static void main(String[] args)
   {
      Jsonb jsonb = JsonbBuilder.create();
      Employee employee = new Employee("John", "Doe", 123456789, false,
                                       LocalDate.of(1980, 12, 23),
                                       LocalDate.of(2002, 8, 14));
      String jsonEmployee = jsonb.toJson(employee);
      System.out.println(jsonEmployee);
      System.out.println();
      Employee employee2 = jsonb.fromJson(jsonEmployee, Employee.class);
      System.out.println(employee2);
   }
}複製代碼

main()首先建立一個Jsonb對象,後跟一個Employee對象。而後,它調用toJson()Employee對象序列化爲存儲在字符串中的JSON文檔。打印該文檔後,main()調用fromJson()與把字符串反序列化爲Employee

清單2. Employee.java(版本1)

import java.time.LocalDate;

public class Employee
{
   private String firstName;

   private String lastName;

   private int ssn;

   private boolean isMarried;

   private LocalDate birthDate;

   private LocalDate hireDate;

   private StringBuffer sb = new StringBuffer();

   public Employee() {}

   public Employee(String firstName, String lastName, int ssn, boolean isMarried,
                   LocalDate birthDate, LocalDate hireDate)
   {
      this.firstName = firstName;
      this.lastName = lastName;
      this.ssn = ssn;
      this.isMarried = isMarried;
      this.birthDate = birthDate;
      this.hireDate = hireDate;
   }

   public String getFirstName()
   {
      return firstName;
   }

   public String getLastName()
   {
      return lastName;
   }

   public int getSSN()
   {
      return ssn;
   }

   public boolean isMarried()
   {
      return isMarried;
   }

   public LocalDate getBirthDate()
   {
      return birthDate;
   }

   public LocalDate getHireDate()
   {
      return hireDate;
   }

   public void setFirstName(String firstName)
   {
      this.firstName = firstName;
   }

   public void setLastName(String lastName)
   {
      this.lastName = lastName;
   }

   public void setSSN(int ssn)
   {
      this.ssn = ssn;
   }

   public void setIsMarried(boolean isMarried)
   {
      this.isMarried = isMarried;
   }

   public void setBirthDate(LocalDate birthDate)
   {
      this.birthDate = birthDate;
   }

   public void setHireDate(LocalDate hireDate)
   {
      this.hireDate = hireDate;
   }

   @Override
   public String toString()
   {
      sb.setLength(0);
      sb.append("First name [");
      sb.append(firstName);
      sb.append("], Last name [");
      sb.append(lastName);
      sb.append("], SSN [");
      sb.append(ssn);
      sb.append("], Married [");
      sb.append(isMarried);
      sb.append("], Birthdate [");
      sb.append(birthDate);
      sb.append("], Hiredate [");
      sb.append(hireDate);
      sb.append("]");
      return sb.toString();
   }
}複製代碼

編制清單1和2以下:

javac -cp javax.json.bind-api-1.0.jar;. JSONBDemo.java複製代碼

運行應用程序以下:

java -cp javax.json.bind-api-1.0.jar;yasson-1.0.3.jar;javax.json-1.1.4.jar;. JSONBDemo複製代碼

您應該觀察如下輸出(爲了便於閱讀,分佈在多行中):

{"SSN":123456789,"birthDate":"1980-12-23","firstName":"John","hireDate":"2002-08-14",
 "lastName":"Doe","married":false}

First name [John], Last name [Doe], SSN [123456789], Married [false],
 Birthdate [1980-12-23], Hiredate [2002-08-14]
 複製代碼

使用JSON-B的規則

在玩這個應用程序時,我觀察到一些有趣的行爲,這些行爲使我制定瞭如下有關Employee的規則:

  • class必須是public; 不然,拋出異常。
  • toJson()不會使用非publicgetter方法序列化字段。
  • fromJson()不會使用非publicsetter方法反序列化字段。
  • fromJson()在沒有public no argument構造函數的狀況下拋出JsonbException

爲了在Java對象字段和JSON數據之間無縫轉換,JSON-B必須支持各類Java類型。例如,JSON-B支持如下基本Java類型:

  • java.lang.Boolean
  • java.lang.Byte
  • java.lang.Character
  • java.lang.Double
  • java.lang.Float
  • java.lang.Integer
  • java.lang.Long
  • java.lang.Short
  • java.lang.String

其餘類型,例如java.math.BigIntegerjava.util.Datejava.time.LocalDate支持。查看JSON-B規範以獲取支持類型的完整列表。

使用JSON-B序列化和反序列化數組和集合

上一節重點介紹了單個Java對象的序列化和反序列化。JSON-B還支持序列化和反序列化對象數組和集合的功能。清單3提供了一個演示。

清單3. JSONBDemo.java(版本2)

import java.time.LocalDate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;

public class JSONBDemo
{
   public static void main(String[] args)
   {
      arrayDemo();
      listDemo();
   }

   // Serialize and deserialize an array of Employee objects.
   static void arrayDemo()
   {
      Jsonb jsonb = JsonbBuilder.create();
      Employee[] employees =
      {
         new Employee("John", "Doe", 123456789, false,
                      LocalDate.of(1980, 12, 23),
                      LocalDate.of(2002, 8, 14)),
         new Employee("Jane", "Smith", 987654321, true,
                      LocalDate.of(1982, 6, 13),
                      LocalDate.of(2001, 2, 9))
      };
      String jsonEmployees = jsonb.toJson(employees);
      System.out.println(jsonEmployees);
      System.out.println();
      employees = null;
      employees = jsonb.fromJson(jsonEmployees, Employee[].class);
      for (Employee employee: employees)
      {
         System.out.println(employee);
         System.out.println();
      }
   }

   // Serialize and deserialize a List of Employee objects.
   static void listDemo()
   {
      Jsonb jsonb = JsonbBuilder.create();
      List<Employee> employees =
         Arrays.asList(new Employee("John", "Doe", 123456789, false,
                                    LocalDate.of(1980, 12, 23),
                                    LocalDate.of(2002, 8, 14)),
                       new Employee("Jane", "Smith", 987654321, true,
                                    LocalDate.of(1982, 6, 13),
                                    LocalDate.of(1999, 7, 20)));
      String jsonEmployees = jsonb.toJson(employees);
      System.out.println(jsonEmployees);
      System.out.println();
      employees = null;
      employees = jsonb.fromJson(jsonEmployees,
                                 new ArrayList<>(){}.
                                     getClass().getGenericSuperclass());
      System.out.println(employees);
   }
}複製代碼

清單3是表1的簡單擴展,並使用相同的Employee清單2.此外,在呈現類,此代碼示例和toJson()fromJson()方法調用相同。

將JSON文檔反序列化爲Java對象數組時,將表達式Employee[].class做爲第二個參數傳遞給fromJson(),以便它能夠建立適當的數組。將JSON對象反序列化爲列表或其餘集合時,會將表達式new ArrayList<>(){}.getClass().getGenericSuperclass()做爲第二個參數傳遞。JDK 11會推斷Employee,因此我沒必要指定ArrayList<Employee>

理想狀況下,應該能夠傳遞ArrayList<Employee>.class,以告知fromJson()集合的預期參數化類型進行實例化。可是,因爲類型擦除,這種表達是非法的。相反,我能夠指定ArrayList.class哪一個會起做用。可是,它還會生成未經檢查的警告消息。表達式越複雜,就不會產生警告。本質上,它實例化一個匿名子類ArrayList<Employee>,獲取它的Class對象,並使用該Class對象來獲取其超類的參數化類型,這剛好是ArrayList<Employee>。此參數化類型可用於fromJson()

編譯清單3和2,並運行生成的應用程序。您應該觀察如下輸出(爲了便於閱讀,分佈在多行中):

[{"SSN":123456789,"birthDate":"1980-12-23","firstName":"John","hireDate":"2002-08-14",
  "lastName":"Doe","married":false},
 {"SSN":987654321,"birthDate":"1982-06-13","firstName":"Jane","hireDate":"2001-02-09",
  "lastName":"Smith","married":true}]

First name [John], Last name [Doe], SSN [123456789], Married [false],
 Birthdate [1980-12-23], Hiredate [2002-08-14]

First name [Jane], Last name [Smith], SSN [987654321], Married [false],
 Birthdate [1982-06-13], Hiredate [2001-02-09]

[{"SSN":123456789,"birthDate":"1980-12-23","firstName":"John","hireDate":"2002-08-14",
  "lastName":"Doe","married":false},
 {"SSN":987654321,"birthDate":"1982-06-13","firstName":"Jane","hireDate":"1999-07-20",
  "lastName":"Smith","married":true}]

[{firstName=John, lastName=Doe, hireDate=2002-08-14, birthDate=1980-12-23, married=false,
  SSN=123456789},
 {firstName=Jane, lastName=Smith, hireDate=1999-07-20, birthDate=1982-06-13, married=true,
  SSN=987654321}]
  複製代碼

在JSON-B中自定義序列化和反序列化

雖然JSON-B經過支持各類Java類型爲您作了不少事情,但您可能須要自定義其行爲; 例如,更改序列化屬性的輸出順序。JSON-B支持編譯時和運行時自定義。

編譯時自定義

JSON-B經過其javax.json.bind.annotation包中的各類註釋類型支持編譯時自定義。例如,您可使用JsonbDateFormat提供自定義日期格式並更改JsonbProperty字段的名稱。清單4的Employee類中說明了這兩種註釋類型。

清單4. Employee.java(版本2)

import java.time.LocalDate;

import javax.json.bind.annotation.JsonbDateFormat;
import javax.json.bind.annotation.JsonbProperty;

public class Employee
{
   @JsonbProperty("first-name")
   private String firstName;

   @JsonbProperty("last-name")
   private String lastName;

   private int ssn;

   private boolean isMarried;

   @JsonbDateFormat("MM-dd-yyyy")
   private LocalDate birthDate;

   @JsonbDateFormat("MM-dd-yyyy")
   private LocalDate hireDate;

   private StringBuffer sb = new StringBuffer();

   public Employee() {}

   public Employee(String firstName, String lastName, int ssn, boolean isMarried,
                   LocalDate birthDate, LocalDate hireDate)
   {
      this.firstName = firstName;
      this.lastName = lastName;
      this.ssn = ssn;
      this.isMarried = isMarried;
      this.birthDate = birthDate;
      this.hireDate = hireDate;
   }

   public String getFirstName()
   {
      return firstName;
   }

   public String getLastName()
   {
      return lastName;
   }

   public int getSSN()
   {
      return ssn;
   }

   public boolean isMarried()
   {
      return isMarried;
   }

   public LocalDate getBirthDate()
   {
      return birthDate;
   }

   public LocalDate getHireDate()
   {
      return hireDate;
   }

   public void setFirstName(String firstName)
   {
      this.firstName = firstName;
   }

   public void setLastName(String lastName)
   {
      this.lastName = lastName;
   }

   public void setSSN(int ssn)
   {
      this.ssn = ssn;
   }

   public void setIsMarried(boolean isMarried)
   {
      this.isMarried = isMarried;
   }

   public void setBirthDate(LocalDate birthDate)
   {
      this.birthDate = birthDate;
   }

   public void setHireDate(LocalDate hireDate)
   {
      this.hireDate = hireDate;
   }

   @Override
   public String toString()
   {
      sb.setLength(0);
      sb.append("First name [");
      sb.append(firstName);
      sb.append("], Last name [");
      sb.append(lastName);
      sb.append("], SSN [");
      sb.append(ssn);
      sb.append("], Married [");
      sb.append(isMarried);
      sb.append("], Birthdate [");
      sb.append(birthDate);
      sb.append("], Hiredate [");
      sb.append(hireDate);
      sb.append("]");
      return sb.toString();
   }
}複製代碼

清單4用JsonbProperty註釋firstNamelastName字段,並用JsonbDateFormat註釋birthDatehireDate字段。JsonbProperty致使firstName序列化爲first-namelastName序列化爲last-name。此註釋類型還會致使first-name反序列化firstNamelast-name反序列化lastNameJsonbDateFormat致使生日和僱用日期在月 - 日 - 年中序列化,而不是默認的年 - 月 - 日訂單,並致使JSON-B在反序列化時考慮序列化的月 - 日 - 年訂單。

編譯清單1和清單4,而後運行生成的應用程序。您應該觀察如下輸出(爲了便於閱讀,分佈在多行中):

{"SSN":123456789,"birthDate":"12-23-1980","first-name":"John","hireDate":"08-14-2002",
 "last-name":"Doe","married":false}

First name [John], Last name [Doe], SSN [123456789], Married [false],
 Birthdate [1980-12-23], Hiredate [2002-08-14]
 複製代碼

運行時自定義

JSON-B經過javax.json.bind.JsonbConfigJsonbBuilder支持運行時自定義。在實例JsonbConfig中,調用各類withXXX方法(例如,withPropertyOrderStrategy)來配置該任務,而且使配置JsonbConfig提供給對象JsonBuilder,可能經過將其做爲JsonbBuilderstatic Jsonb create(JsonbConfig config)方法的參數。查看清單5。

清單5. JSONBDemo.java(版本3)

import java.time.LocalDate;

import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;

import static javax.json.bind.config.PropertyOrderStrategy.*;

public class JSONBDemo
{
   public static void main(String[] args)
   {
      JsonbConfig config = new JsonbConfig()
                               .withPropertyOrderStrategy(REVERSE);
      Jsonb jsonb = JsonbBuilder.create(config);
      Employee employee = new Employee("John", "Doe", 123456789, false,
                                       LocalDate.of(1980, 12, 23),
                                       LocalDate.of(2002, 8, 14));
      String jsonEmployee = jsonb.toJson(employee);
      System.out.println(jsonEmployee);
      System.out.println();
      Employee employee2 = jsonb.fromJson(jsonEmployee, Employee.class);
      System.out.println(employee2);
   }
}複製代碼

清單5的main()方法首先實例化JsonbConfig而後調用此類的JsonbConfig withPropertyOrderStrategy(String propertyOrderStrategy)方法將屬性順序策略更改成javax.json.bind.config.PropertyOrderStrategy.REVERSE。此策略順序致使屬性以與正常輸出方式相反的順序輸出。

JsonbConfig對象被傳遞給create(JsonbConfig)配置所獲得的Jsonb對象JsonbBuilder最終返回。該方法的其他部分與清單1中所示的相同。

編譯清單2和5,而後運行生成的應用程序。您應該觀察如下輸出(爲了便於閱讀,分佈在多行中):

{"married":false,"lastName":"Doe","hireDate":"2002-08-14","firstName":"John",
 "birthDate":"1980-12-23","SSN":123456789}

First name [John], Last name [Doe], SSN [123456789], Married [false],
 Birthdate [1980-12-23], Hiredate [2002-08-14]
 複製代碼

您可使用JSON-B的註釋類型之一完成相同的反向屬性順序任務。我會留下弄清楚如何作這個練習。

在JSON-B中使用適配器

最後,JSON-B支持

適配器
,它是在序列化或反序列化期間將源對象轉換爲目標對象的對象。例如,您可使用適配器來加密JSON文檔中對象的字段名稱和值。

適配器由原始Java對象,包含修改/附加字段的適配/轉換對象和適配器對象組成,適配器對象是該javax.json.bind.adapter.Adapter<Original,Adapted>類型的實例。

Adapter類型提供如下方法:

  • Original adaptFromJson(Adapted obj):在反序列化期間調用此方法以轉換AdaptedOriginal
  • Adapted adaptToJson(Original obj):在序列化期間調用此方法以轉換OriginalAdapted,而後將其序列化爲JSON。

這兩種方法都用一個throws Exception子句聲明,代表它能夠在轉換期間拋出任何類型的異常。

清單6給出了一個源代碼IdentityAdapter,一個不會改變任何東西的適配器。可是,它會打印出本來能夠調整的對象,並演示適配器架構。

清單6. IdentityAdapter.java

import javax.json.bind.adapter.JsonbAdapter;

public class IdentityAdapter implements JsonbAdapter<Employee, Employee>
{
   @Override
   public Employee adaptFromJson(Employee obj)
   {
      System.out.println("Deserializing: " + obj);
      return obj;
   }

   @Override
   public Employee adaptToJson(Employee obj)
   {
      System.out.println("Serializing: " + obj);
      return obj;
   }
}複製代碼

您使用JsonbConfig及其JsonbConfig withAdapters(JsonbAdapter...)方法來註冊一個或多個適配器:

JsonbConfig config = new JsonbConfig()
                         .withAdapters(new IdentityAdapter());複製代碼

這個對象傳遞給JsonbBuildercreate(JsonbConfig)方法,正如我前面表現。爲了完整起見,清單7的JSONBDemo源代碼演示了這兩個任務。

清單7. JSONBDemo.java(版本4)

import java.time.LocalDate;

import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;

public class JSONBDemo
{
   public static void main(String[] args)
   {
      JsonbConfig config = new JsonbConfig()
                               .withAdapters(new IdentityAdapter());
      Jsonb jsonb = JsonbBuilder.create(config);
      Employee employee = new Employee("John", "Doe", 123456789, false,
                                       LocalDate.of(1980, 12, 23),
                                       LocalDate.of(2002, 8, 14));
      String jsonEmployee = jsonb.toJson(employee);
      System.out.println(jsonEmployee);
      System.out.println();
      Employee employee2 = jsonb.fromJson(jsonEmployee, Employee.class);
      System.out.println(employee2);
   }
}複製代碼

編譯清單2,6和7,並運行生成的應用程序。您應該觀察如下輸出(爲了便於閱讀,分佈在多行中):

Serializing: First name [John], Last name [Doe], SSN [123456789], Married [false],
 Birthdate [1980-12-23], Hiredate [2002-08-14]
{"SSN":123456789,"birthDate":"1980-12-23","firstName":"John","hireDate":"2002-08-14",
 "lastName":"Doe","married":false}

Deserializing: First name [John], Last name [Doe], SSN [123456789], Married [false],
 Birthdate [1980-12-23], Hiredate [2002-08-14]
First name [John], Last name [Doe], SSN [123456789], Married [false],
 Birthdate [1980-12-23], Hiredate [2002-08-14]
 複製代碼

結論

JSON-B很好地補充了JSON-P,我在本書的第12章

Java XML和JSON,第二版中介紹了它
。在這篇文章中,我介紹了JSON-B並向您展現瞭如何使用它來序列化和反序列化Java對象,數組和集合。我還向您展現瞭如何使用JSON-B自定義序列化和反序列化,並向您介紹了JSON-B適配器,它們可用於在序列化或反序列化期間將源對象轉換爲目標對象。

我確信JSON-B將繼續發展,而且多是我書第三版的一個很好的補充。同時,我建議您經過探索本文未涉及的各類方法和註釋類型來了解有關JSON-B的更多信息。

英文原文:www.javaworld.com/article/335…

查看更多文章:www.apexyun.com

公衆號:銀河系1號

聯繫郵箱:public@space-explore.com

(未經贊成,請勿轉載)

相關文章
相關標籤/搜索