1. 程式人生 > >最強常用開發庫總結 - JSON庫詳解

最強常用開發庫總結 - JSON庫詳解

最強常用開發庫總結 - JSON庫詳解

JSON應用非常廣泛,對於Java常用的JSON庫要完全掌握。@pdai

JSON簡介

JSON是什麼

  • JSON 指的是 JavaScript 物件表示法(JavaScript Object Notation)
  • JSON 是輕量級的文字資料交換格式
  • JSON 獨立於語言:JSON 使用 Javascript語法來描述資料物件,但是 JSON 仍然獨立於語言和平臺。JSON 解析器和 JSON 庫支援許多不同的程式語言。 目前非常多的動態(PHP,JSP,.NET)程式語言都支援JSON。
  • JSON 具有自我描述性,更易理解

結構與型別

  • 只有兩種結構:物件內的鍵值對集合結構和陣列,物件用{}表示、內部是”key”:”value”,陣列用[]表示,不同值用逗號分開
  • 基本數值有7個: false / null / true / object / array / number / string
  • 再加上結構可以巢狀,進而可以用來表達複雜的資料

一個簡單例項

{
   "Image": {
       "Width":  800,
       "Height": 600,
       "Title":  "View from 15th Floor",
       "Thumbnail": {
           "Url":    "http://www.example.com/image/481989943",
           "Height": 125,
           "Width":  "100"
       },
       "IDs": [116, 943, 234, 38793]
     }
}

JSON優秀資源

  • awesome-json

JSON線上解析工具

  • JSON 線上解析

JSON類庫

Java中並沒有內建JSON的解析,因此使用JSON需要藉助第三方類庫。

下面是幾個常用的 JSON 解析類庫:

  • FastJson: 阿里巴巴開發的 JSON 庫,效能十分優秀。
  • Jackson: 社群十分活躍且更新速度很快。
  • Gson: 谷歌開發的 JSON 庫,功能十分全面。

效能測試對比

從下面的測試結果可以看出,序列化次數比較小的時候,Gson效能最好,當不斷增加的時候到了100000,Gson明細弱於Jackson和FastJson, 這時候FastJson效能是真的牛,另外還可以看到不管數量少還是多,Jackson一直表現優異。而那個Json-lib可以直接忽略。

  • JSON序列化效能

  • JSON反序列化效能

更多請參考: Java幾種常用JSON庫效能比較

FastJson

Fastjson 簡介

Fastjson 是一個 Java 庫,可以將 Java 物件轉換為 JSON 格式,當然它也可以將 JSON 字串轉換為 Java 物件。

Fastjson 可以操作任何 Java 物件,即使是一些預先存在的沒有原始碼的物件。

  • Fastjson Github
  • Fastjson 中文 Wiki

Fastjson 特性

  • 提供伺服器端、安卓客戶端兩種解析工具,效能表現較好。
  • 提供了 toJSONString() 和 parseObject() 方法來將 Java 物件與 JSON 相互轉換。呼叫toJSONString方 法即可將物件轉換成 JSON 字串,parseObject 方法則反過來將 JSON 字串轉換成物件。
  • 允許轉換預先存在的無法修改的物件(只有class、無原始碼)。
  • Java泛型的廣泛支援。
  • 允許物件的自定義表示、允許自定義序列化類。
  • 支援任意複雜物件(具有深厚的繼承層次和廣泛使用的泛型型別)。

下載和使用

你可以在 maven 中央倉庫中直接下載:http://repo1.maven.org/maven2/com/alibaba/fastjson/

配置 maven 依賴:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>x.x.x</version>
</dependency>

其中 x.x.x 是版本號,根據需要使用特定版本,建議使用最新版本。

序列化一個物件成JSON字串

User user = new User();
user.setName("校長");
user.setAge(3);
user.setSalary(new BigDecimal("123456789.0123"));
String jsonString = JSON.toJSONString(user);
System.out.println(jsonString);
// 輸出 {"age":3,"name":"校長","old":false,"salary":123456789.0123}

反序列化一個JSON字串成Java物件

String jsonString = "{\"age\":3,\"birthdate\":1496738822842,\"name\":\"校長\",\"old\":true,\"salary\":123456789.0123}";
 User u = JSON.parseObject(jsonString ,User.class);
 System.out.println(u.getName());
 // 輸出 校長

String jsonStringArray = "[{\"age\":3,\"birthdate\":1496738822842,\"name\":\"校長\",\"old\":true,\"salary\":123456789.0123}]";
List<User> userList = JSON.parseArray(jsonStringArray, User.class);
System.out.println(userList.size());
// 輸出 1

對於日期的處理

預設序列化Date輸出使用”yyyy-MM-dd HH:mm:ss”格式,可以用UseISO8601DateFormat特性換成”yyyy-MM-dd’T’HH:mm:ss”格式。

JSON.defaultTimeZone = TimeZone.getTimeZone("Asia/Shanghai");
JSON.defaultLocale = Locale.US;
        
public static class Model {
    @JSONField(format = "MMM dd, yyyy h:mm:ss aa")
    private java.util.Date date;

    public java.util.Date getDate() {
        return date;
    }

    public void setDate(java.util.Date date) {
        this.date = date;
    }

    @JSONField(format = "MMM-dd-yyyy h:mm:ss aa")
    public java.sql.Date date2;
}

Bean和陣列轉換

  • 官方例子 - BeanToArray_cn

設定欄位名

public class A {
    @JSONField(name="ID")
    private int id;

    public int getId() {return id;}
    public void setId(int value) {this.id = id;}
}

設定是否不序列化某欄位

public class A {
    @JSONField(serialize=false)
    public Date date;
}

public class A {
    @JSONField(deserialize=false)
    public Date date;
}

設定欄位順序

public static class VO {
    @JSONField(ordinal = 3)
    private int f0;

    @JSONField(ordinal = 2)
    private int f1;

    @JSONField(ordinal = 1)
    private int f2;
}

自定義序列化和反序列化

  • fastjson SerializerFeature詳解
  • ObjectDeserializer_cn

FastJson漏洞問題

儘量使用最新版本。

  • fastjson遠端程式碼執行漏洞技術分析與防護方案

JackSon

JackSon簡介

  • Jackson Github
  • Jackson Wiki
  • Jackson 文件

Jackson元件

3個核心模組:

  • Streaming: jackson-core jar,定義了底層的streaming API和實現了Json特性。
  • Annotations: jackson-annotations jar,包含了標準的Jackson註解。本文暫不介紹。
  • Databind: jackson-databind jar,實現了資料繫結和物件序列化,它依賴於streaming和annotations的包。

第三方資料型別模組

這些擴充套件是外掛式的Jackson模組,用ObjectMapper.registerModule()註冊,並且通過新增serializers和deserializers以便Databind包(ObjectMapper / ObjectReader / ObjectWriter)可以讀寫這些型別,來增加對各種常用的Java庫的資料型別的支援。

資料格式模組

Jackson也有處理程式對JAX-RS標準實現者例如Jersey, RESTeasy, CXF等提供了資料格式支援。處理程式實現了MessageBodyReader和MessageBodyWriter,目前支援的資料格式包括JSON, Smile, XML, YAML和CBOR。

資料格式提供了除了Json之外的資料格式支援,它們絕大部分僅僅實現了streaming API abstractions,以便資料繫結元件可以按照原來的方式使用。另一些(幾乎不需要)提供了databind標準功能來處理例如schemas。

Jackson的使用

引用maven jar包:

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.10.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.10.1</version>
</dependency>

序列化一個物件成JSON字串

public void toJson() throws JsonProcessingException {

    ObjectMapper mapper = new ObjectMapper();

    City case1 = new City();
    case1.setCity("SZ");
    case1.setAge(123);

    String jsonStr = mapper.writeValueAsString(case1);
    System.out.println("JSON:" + jsonStr);
}
// 輸出:JSON:{"city":"SZ","age":123}

反序列化一個JSON字串成Java物件

public void toObj() throws JsonParseException, JsonMappingException, IOException {
    ObjectMapper mapper = new ObjectMapper();
    String inputjsonstr = "{\"city\":\"SZ\",\"age\":123}";
    
    City readcase = mapper.readValue(inputjsonstr, City.class);

    System.out.println("city info:" + readcase);
}

如果裡面有未知屬性,比如json中有desc欄位,但是City中沒有相應欄位,會報錯, 需要設定如下:

mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

常用註解

  • @JsonProperty("xxx"): 將當前的屬性名在json字串中重新命名為當前設定的這個值,比如在示例中,將age-->mAge
  • @JsonIgnore: 將被標註的屬性在生成json字串的時候,直接忽略
  • @JsonInclude: 是一個類級別的設定,JsonInclude.Include.NON_EMPTY標識只有非NULL的值才會被納入json string之中,其餘的都被忽略,比如這裡的location屬性,並沒有出現在最終的結果字串中。
  • @JsonSerialize: 使用自定義的類來實現自定義的欄位轉換。寫入操作。
  • @JsonDeserialize: 解析的時候,自定義的轉換器;讀取操作。
  • @JsonAutoDetect: 設定類的訪問策略,是否所有的屬性都可以,還是按照一定的方式來提取。
  • @JsonRawValue: 無轉換的將屬性值寫入到json 字串中。 寫入操作
  • @JsonValue: 標註方法,用以替代預設的方法,由該方法來完成json的字元輸出。

GSON

Gson簡介

Gson是這樣一個Java類庫,它可以將Java物件轉換為相應的JSON形式,也可以將JSON字串轉換為對應的Java物件。
Gson可以使用任意Java物件,包括哪些預先存在的、不在你的原始碼中的物件(因此,你並不知道物件的屬性)。

  • Gson使用者指南(中文翻譯)

Gson的目標

  • 提供一種機制,使得將Java物件轉換為JSON或相反如使用toString()以及構造器(工廠方法)一樣簡單。
  • 允許預先存在的不可變的物件轉換為JSON或與之相反。
  • 允許自定義物件的表現形式
  • 支援任意複雜的物件
  • 輸出輕量易讀的JSON

Gson的使用

使用Gson的首要類是Gson類,你可以僅僅通過new Gson()的方式建立它。你也可以通過GsonBuilder類去建立Gson例項,這個類允許你進行一系列配置,例如版本控制等等。

Gson例項不會儲存任何進行Json操作時的狀態。因此,你可以自由的服用相同的Gson物件進行諸多的Json序列化和反序列化操作。

引用maven jar包:

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.2-jre</version>
</dependency>

序列化

// 基礎型別
Gson gson = new Gson();
gson.toJson(1);            ==> prints 1
gson.toJson("abcd");       ==> prints "abcd"
gson.toJson(new Long(10)); ==> prints 10
int[] values = { 1 };
gson.toJson(values);       ==> prints [1]

// 物件
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);  
==> json is {"value1":1,"value2":"abc"}

// 陣列
Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};
gson.toJson(ints);     ==> prints [1,2,3,4,5]
gson.toJson(strings);  ==> prints ["abc", "def", "ghi"]

// 集合
Gson gson = new Gson();
Collection<Integer> ints = Lists.immutableList(1,2,3,4,5);
String json = gson.toJson(ints); ==> json is [1,2,3,4,5]

其中的物件程式碼:

class BagOfPrimitives {
  private int value1 = 1;
  private String value2 = "abc";
  private transient int value3 = 3;
  BagOfPrimitives() {
    // no-args constructor
  }
}

反序列化

// 基礎型別
Gson gson = new Gson();
int one = gson.fromJson("1", int.class);
Integer one = gson.fromJson("1", Integer.class);
Long one = gson.fromJson("1", Long.class);
Boolean false = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);
String anotherStr = gson.fromJson("[\"abc\"]", String.class);

// 物件
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);   
==> obj2 is just like obj

// 陣列
Gson gson = new Gson();
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class); 
==> ints2 will be same as ints

// 集合
Gson gson = new Gson();
Type collectionType = new TypeToken<Collection<Integer>>(){}.getType();
Collection<Integer> ints2 = gson.fromJson(json, collectionType);
ints2 is same as ints

自定義序列化和反序列化機制

有時候,預設的實現並不是你想要的。這在處理類庫時常常發生(例如DateTime)。Gson允許你註冊自己自定義的序列化器和反序列化器。該過程分為兩部分:

  • Json序列化器:需要為一個物件自定義序列化機制。

  • Json反序列化器:需要為一個型別自定義反序列化機制。

例項構造者:並不需要,如果無參構造器是可用的或者註冊了一個反序列化器。

GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(MyType2.class, new MyTypeAdapter());
gson.registerTypeAdapter(MyType.class, new MySerializer());
gson.registerTypeAdapter(MyType.class, new MyDeserializer());
gson.registerTypeAdapter(MyType.class, new MyInstanceCreator());

registerTypeAdapter會檢查型別介面卡是否實現了上面三個介面中的一個以上並且它們都註冊了型別介面卡。

更多請參考:Gson使用者指南(中文翻譯)

參考文章

  • https://github.com/FasterXML/jackson
  • https://www.runoob.com/w3cnote/fastjson-intro.html
  • https://blog.csdn.net/m0_37076574/article/details/81317403
  • https://blog.csdn.net/blueheart20/article/details/52212221
  • https://blog.csdn.net/gjb724332682/article/details/51586701
  • https://www.jianshu.com/p/1e20b28c39d1
  • https://www.jianshu.com/p/923a9fe78108

更多內容

最全的Java後端知識體系 https://www.pdai.tech, 每天更新中...。