走進Java接口測試之fastjson指南

引言

在上文 我們介紹了 JSON 的基礎知識,本文我們深入研究阿里巴巴的開源 JSON 解析庫 fastjson。

什麼是fastjson?

fastjson 是阿里巴巴的開源 JSON 解析庫,它可以解析 JSON 格式的字符串,支持將 Java Bean 序列化爲 JSON 字符串,也可以從 JSON 字符串反序列化到 JavaBean。

截止2019/2/1:

  • Github Stars: 16434
  • Github Forks: 4661

GitHub:https://github.com/alibaba/fastjson/

fastjson的特點

速度快

fastjson 相對其他 JSON 庫的特點是快,從 2011 年 fastjson 發佈1.1.x版本之後,其性能從未被其他 Java 實現的 JSON 庫超越。

使用廣泛

fastjson 在阿里巴巴大規模使用,在數萬臺服務器上部署,fastjson 在業界被廣泛接受。在 2012 年被開源中國評選爲最受歡迎的國產開源軟件之一。

測試完備

fastjson 有非常多的 testcase,在1.2.11版本中,testcase 超過3321個。每次發佈都會進行迴歸測試,保證質量穩定。

使用簡單

fastjson 的 API 十分簡潔。

String text = JSON.toJSONString(obj); //序列化
VO vo = JSON.parseObject("{...}", VO.class); //反序列化

功能完備

支持泛型,支持流處理超大文本,支持枚舉,支持序列化和反序列化擴展。

fastjson使用

Maven配置

爲了開始使用FastJson,我們首先需要將它添加到我們的 pom.xml

  <!--引入效率插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--引入FastJson包-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <!--引入testng測試框架-->
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.14.3</version>
        </dependency>

將Java對象轉換爲JSON格式

讓我們定義以下Person Java bean

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Person {

   	@JSONField(name = "AGE")
    private int age;

    @JSONField(name = "FULL NAME")
    private String fullName;

    @JSONField(name = "DATE OF BIRTH")
    private Date dateOfBirth;
}

我們可以使用 JSON.toJSONString() 將 Java 對象轉換爲 JSON 字符串

private List<Person> listOfPersons;

	@BeforeTest
	public void setUp() {
		listOfPersons = new ArrayList<Person>();
		// 獲取當前時間,取得一個Calendar的實例
		Calendar calendar = Calendar.getInstance();
		// 設置日曆
		calendar.set(2019, 01, 31);
		// 實例化Java對象
		listOfPersons.add(new Person(15, "John Doe", calendar.getTime()));
		listOfPersons.add(new Person(20, "Janette Doe", calendar.getTime()));
	}

	@Test(description = "將Java對象轉換爲JSON格式")
	public void whenJavaList_thanConvertToJsonCorrect() {

		// 將Java對象轉換爲JSON字符串
		String personJsonFormat = JSON.toJSONString(listOfPersons);
}

這是結果:

[  
    {  
        "AGE":15,
        "DATE OF BIRTH":1468962431394,
        "FULL NAME":"John Doe"
    },
    {  
        "AGE":20,
        "DATE OF BIRTH":1468962431394,
        "FULL NAME":"Janette Doe"
    }
]

我們還可以進一步開始自定義輸出並控制排序,日期格式或序列化標誌等內容。
例如 - 讓我們更新 bean 並添加幾個字段:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Person {

	// 忽略序列化和反序列化
	@JSONField(name = "AGE", serialize = false, deserialize = false)
	private int age;

	// ordinal指定順序
	@JSONField(name = "LAST NAME", ordinal = 2)
	private String lastName;

	@JSONField(name = "FIRST NAME", ordinal = 1)
	private String firstName;

	// format格式化
	@JSONField(name = "DATE OF BIRTH", format = "dd/MM/yyyy",ordinal = 3)
	private Date dateOfBirth;

}

以下是我們可以與 @JSONField 註解一起使用的最基本參數列表,以便自定義轉換過程:

  • 參數格 format 用於正確格式化日期屬性
  • 默認情況下,fastjson 庫完全序列化Java bean,但我們可以使用參數 -serialize來忽略特定字段的序列化
  • 參數 ordinal 用於指定字段順序

這是新的輸出:

[
    {
        "FIRST NAME":"Doe",
        "LAST NAME":"Jhon",
        "DATE OF BIRTH":"31/01/2019"
    },
    {
        "FIRST NAME":"Doe",
        "LAST NAME":"Janette",
        "DATE OF BIRTH":"31/01/2019"
    }
]

fastjson 還支持非常有趣的 BeanToArray 序列化功能:

String jsonOutput= JSON.toJSONString(listOfPersons, SerializerFeature.BeanToArray);

這是在這種情況下輸出的樣子:

[
    [
        15,
        1469003271063,
        "John Doe"
    ],
    [
        20,
        1469003271063,
        "Janette Doe"
    ]
]

完整示例:

private List<Person> listOfPersons;

	@BeforeTest
	public void setUp() {
		listOfPersons = new ArrayList<Person>();
		// 獲取當前時間,取得一個Calendar的實例
		Calendar calendar = Calendar.getInstance();
		// 設置日曆
		calendar.set(2019, 01, 31);
		// 實例化Java對象
		listOfPersons.add(new Person(15, "John", "Doe", calendar.getTime()));
		listOfPersons.add(new Person(20, "Janette", "Doe", calendar.getTime()));
	}

	@Test(description = "將Java對象轉換爲JSON格式")
	public void whenJavaList_thanConvertToJsonCorrect() {

		// 將Java對象轉換爲JSON字符串
		String personJsonFormat = JSON.toJSONString(listOfPersons);
		// 斷言字符串是否相同
		Assert.assertEquals(personJsonFormat, "[{\"FIRST NAME\":\"Doe\",\"LAST NAME\":\"John\",\"DATE OF BIRTH\":"
				+ "\"01/02/2019\"},{\"FIRST NAME\":\"Doe\",\"LAST NAME\":\"Janette\",\"DATE OF BIRTH\":"
				+ "\"01/02/2019\"}]");
	}

創建JSON對象

與其他 JSON 庫一樣,從頭開始創建 JSON 對象非常簡單,只需要組合JSONObject 和 JSONArray 對象:

@Test(description = "創建JSON對象")
	public void whenGenerateJson_thanGenerationCorrect() {
		// 組合JSONObject和JSONArray對象
		JSONArray jsonArray = new JSONArray();
		for (int i = 0; i < 2; i++) {
			JSONObject jsonObject = new JSONObject();
			jsonObject.put("FIRST NAME", "John" + i);
			jsonObject.put("LAST NAME", "Doe" + i);
			jsonObject.put("DATE OF BIRTH", "2019/2/1 12:12:12");
			jsonArray.add(jsonObject);
		}

		Assert.assertEquals(jsonArray.toString(), "[{\"LAST NAME\":\"Doe0\",\"DATE OF BIRTH\":"
				+ "\"2019/2/1 12:12:12\",\"FIRST NAME\":\"John0\"},{\"LAST NAME\":\"Doe1\","
				+ "\"DATE OF BIRTH\":\"2019/2/1 12:12:12\",\"FIRST NAME\":\"John1\"}]");
		System.out.println(jsonArray.toString());
	}

以下是輸出結果:

[
   {
      "LAST NAME":"Doe0",
      "DATE OF BIRTH":"2019/2/1 12:12:12",
      "FIRST NAME":"John0"
   },
   {
      "LAST NAME":"Doe1",
      "DATE OF BIRTH":"2019/2/1 12:12:12",
      "FIRST NAME":"John1"
   }
]

將JSON字符串解析爲Java對象

現在我們知道如何從頭開始創建 JSON 對象,以及如何將 Java 對象轉換爲它們的 JSON 格式,讓我們把重點放在如何解析 JSON 格式上:

@Test(description = "將JSON字符串解析爲Java對象")
	public void whenJson_thanConvertToObjectCorrect() {
		// 將Java對象轉換爲JSON字符串
		String personJsonFormat = JSON.toJSONString(listOfPersons.get(0));
		System.out.println(personJsonFormat);

		// 從JSON字符串中獲取Java對象
		Person newPerson = JSON.parseObject(personJsonFormat, Person.class);
		System.out.println(newPerson.toString());

		// 使用參數serialize忽略Age字段的序列化
		Assert.assertEquals(newPerson.getAge(), 0);
		System.out.println(newPerson.getAge());

		Assert.assertEquals(newPerson.getFirstName(), listOfPersons.get(0).getFirstName());
		System.out.println(newPerson.getFirstName());

		Assert.assertEquals(newPerson.getLastName(), listOfPersons.get(0).getLastName());
		System.out.println(newPerson.getLastName());
	}

我們可以使用 JSON.parseObject() 從 JSON 字符串中獲取 Java 對象。
請注意,如果已經聲明瞭自己的參數化構造函數,則必須定義no-args 或默認構造函數,否則將拋出 com.alibaba.fastjson.JSONException

這是新創建的對象。

Person(age=0, lastName=John, firstName=Doe, dateOfBirth=Sun JAN 31 00:00:00 CST 2019)

使用ContextValueFilter配置JSON轉換

在某些情況下,我們可能需要更多地控制從 Java 對象到 JSON 格式的轉換過程。
在這種情況下,我們可以使用 ContextValueFilter 對象對轉換流應用其他過濾和自定義處理:

@Test(description = "使用ContextValueFilter配置JSON轉換")
	public void givenContextFilter_whenJavaObject_thanJsonCorrect() {
		// 使用ContextValueFilter對象對轉換流應用其他過濾和自定義處理
		ContextValueFilter valueFilter = new ContextValueFilter() {
			public Object process(BeanContext context, Object object, String name, Object value) {
				// 隱藏了 DATE OF BIRTH 字段,強制一個常量值
				if (name.equals("DATE OF BIRTH")) {
					return "NOT TO DISCLOSE";
				}
				// 忽略了所有不是John或Doe的字段
				if (value.equals("John") || value.equals("Doe")) {
					return ((String) value).toUpperCase();
				} else {
					return null;
				}
			}
		};
		// 將Java對象轉換爲JSON字符串並過濾及自定義處理
		String personJsonFormat = JSON.toJSONString(listOfPersons, valueFilter);
		System.out.println(personJsonFormat);

	}

在這個例子中,我們隱藏了 DATE OF BIRTH 字段,通過強制一個常量值,我們也忽略了所有不是 John 或 Doe 的字段:

[
   {
      "FIRST NAME":"DOE",
      "LAST NAME":"JOHN",
      "DATE OF BIRTH":"NOT TO DISCLOSE"
   },
   {
      "FIRST NAME":"DOE",
      "DATE OF BIRTH":"NOT TO DISCLOSE"
   }
]

正如你所看到的,這是一個非常基本的示例,當然可以在更復雜的測試場景中使用相同的概念 - 結合 fastjson 在實際項目中提供的這些功能強大且輕量級的工具集。

使用NameFilter和SerializeConfig

fastjson 提供了一組工具來在處理任意對象時自定義 JSON 操作 - 我們沒有源碼的對象。
讓我們假設我們有一個最初在本文中聲明的 Person Java bean 的編譯版本,我們需要對字段命名和基本格式進行一些增強:

	@Test(description = "使用NameFilter和SerializeConfig")
	public void givenSerializeConfig_whenJavaObject_thanJsonCorrect() {

		// formatName過濾器來處理字段名稱。
		NameFilter formatName = new NameFilter() {
			public String process(Object object, String name, Object value) {
				return name.toLowerCase().replace(" ", "_");
			}
		};
		SerializeConfig.getGlobalInstance().addFilter(Person.class, formatName);

		// 將對象轉換爲JSON格式,快速在日期字段上應用相同的格式規則。
		String jsonOutput = JSON.toJSONStringWithDateFormat(listOfPersons, "yyyy-MM-dd");

		System.out.println(jsonOutput);
		Assert.assertEquals(jsonOutput, "[{\"first_name\":\"Doe\",\"last_name\":\"John\","
				+ "\"date_of_birth\":\"2019-02-01\"},{\"first_name\":\"Doe\",\"last_name\":"
				+ "\"Janette\",\"date_of_birth\":\"2019-02-01\"}]");


		// 重新設置自定義序列化器
		SerializeConfig.getGlobalInstance().put(Person.class, null);
	}

我們使用 NameFilter 匿名類聲明瞭 formatName 過濾器來處理字段名稱。新創建的過濾器與 Person 類相關聯,然後添加到全局實例 - 它基本上是 SerializeConfig 類中的靜態屬性。
現在我們可以輕鬆地將對象轉換爲 JSON 格式,如本文前面所示。

請注意,我們使用了 toJSONStringWithDateFormat() 而不是 toJSONString() 來快速在日期字段上應用相同的格式規則。

這是輸出:

[
   {
      "first_name":"Doe",
      "last_name":"John",
      "date_of_birth":"2019-03-03"
   },
   {
      "first_name":"Doe",
      "last_name":"Janette",
      "date_of_birth":"2019-03-03"
   }
]

如你所見 - 字段名稱已更改,日期值確實已正確格式化。
將 SerializeFilter 與 ContextValueFilter 相結合可以完全控制任意和複雜Java 對象的轉換過程。

小結

在本文中,我們展示瞭如何使用 fastjson 將Java bean 轉換成 JSON 字符串,以及如何反過來。我們還展示瞭如何使用 fastjson 的一些核心特性來定製 JSON 輸出。
如你所見,fastjson庫提供了一個相對簡單但仍然非常強大的API。JSON.toJSONStringJSON.parseObject 可滿足大多數需求。

本文源碼:
https://github.com/zuozewei/Java-API-Test-Examples/tree/master/SpringBoot-fastjson-demo

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章