Bộ phân tích Jackson JSON cho Java rất phổ biến và cũng được sử dụng trong Spring framework. Java JSON Processing API không thân thiện với người dùng và không cung cấp các tính năng để chuyển đổi tự động từ JSON sang đối tượng Java và ngược lại. May mắn là chúng ta có một số API thay thế mà chúng ta có thể sử dụng để xử lý JSON.

Jackson JSON Java Parser
Để sử dụng Jackson JSON Java API trong dự án của chúng ta, chúng ta có thể thêm nó vào build path của dự án hoặc nếu bạn đang sử dụng Maven, chúng ta có thể thêm dependency dưới đây:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
</dependency>
Jar jackson-databind phụ thuộc vào các thư viện jackson-core và jackson-annotations. Vì vậy, nếu bạn thêm chúng trực tiếp vào build path, hãy đảm bảo bạn thêm cả ba, nếu không bạn sẽ gặp lỗi runtime. Jackson JSON Parser API giúp chuyển đổi JSON sang đối tượng POJO và hỗ trợ chuyển đổi sang Map từ dữ liệu JSON một cách dễ dàng. Jackson cũng hỗ trợ generics và chuyển đổi trực tiếp chúng từ JSON sang đối tượng.
Ví dụ về Jackson JSON
Đối với ví dụ chuyển đổi JSON sang POJO/đối tượng Java, chúng ta sẽ lấy một ví dụ phức tạp với các đối tượng và mảng lồng nhau. Chúng ta sẽ sử dụng mảng (arrays), danh sách (list) và Map trong các đối tượng Java để chuyển đổi. JSON phức tạp này được lưu trữ trong tệp employee.txt với cấu trúc dưới đây.
{
"id": 123,
"name": "Pankaj",
"permanent": true,
"address": {
"street": "Albany Dr",
"city": "San Jose",
"zipcode": 95129
},
"phoneNumbers": [
123456,
987654
],
"role": "Manager",
"cities": [
"Los Angeles",
"New York"
],
"properties": {
"age": "29 years",
"salary": "1000 USD"
}
}
Chúng ta có các lớp Java sau tương ứng với dữ liệu JSON:
package com.journaldev.jackson.model;
public class Address {
private String street;
private String city;
private int zipcode;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public int getZipcode() {
return zipcode;
}
public void setZipcode(int zipcode) {
this.zipcode = zipcode;
}
@Override
public String toString(){
return getStreet() + ", "+getCity()+", "+getZipcode();
}
}
Lớp Address tương ứng với đối tượng bên trong dữ liệu JSON gốc.
package com.journaldev.jackson.model;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class Employee {
private int id;
private String name;
private boolean permanent;
private Address address;
private long[] phoneNumbers;
private String role;
private List<String> cities;
private Map<String, String> properties;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isPermanent() {
return permanent;
}
public void setPermanent(boolean permanent) {
this.permanent = permanent;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public long[] getPhoneNumbers() {
return phoneNumbers;
}
public void setPhoneNumbers(long[] phoneNumbers) {
this.phoneNumbers = phoneNumbers;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("***** Employee Details *****\\n");
sb.append("ID="+getId()+"\\n");
sb.append("Name="+getName()+"\\n");
sb.append("Permanent="+isPermanent()+"\\n");
sb.append("Role="+getRole()+"\\n");
sb.append("Phone Numbers="+Arrays.toString(getPhoneNumbers())+"\\n");
sb.append("Address="+getAddress()+"\\n");
sb.append("Cities="+Arrays.toString(getCities().toArray())+"\\n");
sb.append("Properties="+getProperties()+"\\n");
sb.append("*****************************");
return sb.toString();
}
public List<String> getCities() {
return cities;
}
public void setCities(List<String> cities) {
this.cities = cities;
}
public Map<String, String> getProperties() {
return properties;
}
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
}
Employee là Java bean đại diện cho đối tượng JSON gốc. Bây giờ, hãy cùng xem cách chúng ta có thể chuyển đổi JSON sang đối tượng Java bằng Jackson JSON parser API.
package com.journaldev.jackson.json;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.journaldev.jackson.model.Address;
import com.journaldev.jackson.model.Employee;
public class JacksonObjectMapperExample {
public static void main(String[] args) throws IOException {
//read json file data to String
byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));
//create ObjectMapper instance
ObjectMapper objectMapper = new ObjectMapper();
//convert json string to object
Employee emp = objectMapper.readValue(jsonData, Employee.class);
System.out.println("Employee Object\\n"+emp);
//convert Object to json string
Employee emp1 = createEmployee();
//configure Object mapper for pretty print
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
//writing to console, can write to any output stream such as file
StringWriter stringEmp = new StringWriter();
objectMapper.writeValue(stringEmp, emp1);
System.out.println("Employee JSON is\\n"+stringEmp);
}
public static Employee createEmployee() {
Employee emp = new Employee();
emp.setId(100);
emp.setName("David");
emp.setPermanent(false);
emp.setPhoneNumbers(new long[] { 123456, 987654 });
emp.setRole("Manager");
Address add = new Address();
add.setCity("Bangalore");
add.setStreet("BTM 1st Stage");
add.setZipcode(560100);
emp.setAddress(add);
List<String> cities = new ArrayList<String>();
cities.add("Los Angeles");
cities.add("New York");
emp.setCities(cities);
Map<String, String> props = new HashMap<String, String>();
props.put("salary", "1000 Rs");
props.put("age", "28 years");
emp.setProperties(props);
return emp;
}
}
Khi chạy chương trình trên, chúng ta sẽ nhận được kết quả sau:
Employee Object
***** Employee Details *****
ID=123
Name=Pankaj
Permanent=true
Role=Manager
Phone Numbers=[123456, 987654]
Address=Albany Dr, San Jose, 95129
Cities=[Los Angeles, New York]
Properties={age=29 years, salary=1000 USD}
*****************************
Employee JSON is
//printing same as above json file data
com.fasterxml.jackson.databind.ObjectMapper là lớp quan trọng nhất trong Jackson API, cung cấp các phương thức readValue() và writeValue() để chuyển đổi JSON sang Đối tượng Java và Đối tượng Java sang JSON. Lớp ObjectMapper có thể được tái sử dụng và chúng ta có thể khởi tạo nó một lần dưới dạng đối tượng Singleton. Có rất nhiều phiên bản quá tải của các phương thức readValue() và writeValue() để làm việc với mảng byte, File, input/output stream và các đối tượng Reader/Writer.
Jackson JSON – Chuyển đổi JSON sang Map
Đôi khi, chúng ta có một đối tượng JSON như dưới đây, trong tệp data.txt:
{
"name": "David",
"role": "Manager",
"city": "Los Angeles"
}
Và chúng ta muốn chuyển đổi nó sang một Map, chứ không phải sang đối tượng Java có các thuộc tính và khóa tương tự. Chúng ta có thể thực hiện điều này rất dễ dàng trong Jackson JSON API với hai phương thức cùng đoạn mã dưới đây:
//converting json to Map
byte[] mapData = Files.readAllBytes(Paths.get("data.txt"));
Map<String,String> myMap = new HashMap<String, String>();
ObjectMapper objectMapper = new ObjectMapper();
myMap = objectMapper.readValue(mapData, HashMap.class);
System.out.println("Map is: "+myMap);
//another way
myMap = objectMapper.readValue(mapData, new TypeReference<HashMap<String,String>>() {});
System.out.println("Map using TypeReference: "+myMap);
Sau khi chúng ta thực thi đoạn mã trên, chúng ta sẽ nhận được kết quả sau:
Map is: {name=David, role=Manager, city=Los Angeles}
Map using TypeReference: {name=David, role=Manager, city=Los Angeles}
Jackson JSON – Đọc khóa JSON cụ thể
Đôi khi chúng ta có dữ liệu JSON và chỉ quan tâm đến giá trị của một vài khóa nhất định. Trong trường hợp đó, việc chuyển đổi toàn bộ JSON sang đối tượng không phải là một ý hay. Jackson JSON API cung cấp tùy chọn đọc dữ liệu JSON dưới dạng cấu trúc cây (tree-like), giống như DOM Parser, và thông qua đó chúng ta có thể đọc các phần tử cụ thể của đối tượng JSON. Đoạn mã dưới đây cung cấp cách đọc các mục cụ thể từ một tệp JSON.
//read json file data to String
byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));
//create ObjectMapper instance
ObjectMapper objectMapper = new ObjectMapper();
//read JSON like DOM Parser
JsonNode rootNode = objectMapper.readTree(jsonData);
JsonNode idNode = rootNode.path("id");
System.out.println("id = "+idNode.asInt());
JsonNode phoneNosNode = rootNode.path("phoneNumbers");
Iterator<JsonNode> elements = phoneNosNode.elements();
while(elements.hasNext()){
JsonNode phone = elements.next();
System.out.println("Phone No = "+phone.asLong());
}
Khi chúng ta thực thi đoạn mã trên, chúng ta sẽ nhận được kết quả sau:
id = 123
Phone No = 123456
Phone No = 987654
Jackson JSON – Chỉnh sửa tài liệu JSON
Jackson JSON Java API cung cấp các phương thức hữu ích để thêm, chỉnh sửa và xóa các khóa từ dữ liệu JSON. Sau đó, chúng ta có thể lưu nó dưới dạng một tệp JSON mới hoặc ghi ra bất kỳ stream nào. Đoạn mã dưới đây cho chúng ta thấy cách thực hiện điều này một cách dễ dàng.
byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));
ObjectMapper objectMapper = new ObjectMapper();
//create JsonNode
JsonNode rootNode = objectMapper.readTree(jsonData);
//update JSON data
((ObjectNode) rootNode).put("id", 500);
//add new key value
((ObjectNode) rootNode).put("test", "test value");
//remove existing key
((ObjectNode) rootNode).remove("role");
((ObjectNode) rootNode).remove("properties");
objectMapper.writeValue(new File("updated_emp.txt"), rootNode);
Nếu bạn chạy đoạn mã trên và kiểm tra tệp mới, bạn sẽ thấy rằng nó không còn các khóa “role” và “properties”. Bạn cũng sẽ nhận thấy rằng giá trị của “id” đã được cập nhật thành 500 và một khóa mới “test” đã được thêm vào tệp updated_emp.txt.
Jackson JSON Streaming API Ví dụ
Jackson JSON Java API cũng cung cấp hỗ trợ streaming (truyền tải dữ liệu theo luồng), điều này rất hữu ích khi làm việc với dữ liệu JSON lớn vì nó đọc toàn bộ tệp dưới dạng các tokens (mã thông báo) và sử dụng ít bộ nhớ hơn. Vấn đề duy nhất với API streaming là chúng ta cần xử lý tất cả các tokens trong khi phân tích cú pháp dữ liệu JSON. Nếu chúng ta có dữ liệu JSON là {"role":"Manager"}, thì chúng ta sẽ nhận được các tokens theo thứ tự sau: { (bắt đầu đối tượng), "role" (tên khóa), "Manager" (giá trị khóa) và } (kết thúc đối tượng). Dấu hai chấm (:) là ký tự phân cách trong JSON và do đó không được coi là một token.
package com.journaldev.jackson.json;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.journaldev.jackson.model.Address;
import com.journaldev.jackson.model.Employee;
public class JacksonStreamingReadExample {
public static void main(String[] args) throws JsonParseException, IOException {
//create JsonParser object
JsonParser jsonParser = new JsonFactory().createParser(new File("employee.txt"));
//loop through the tokens
Employee emp = new Employee();
Address address = new Address();
emp.setAddress(address);
emp.setCities(new ArrayList<String>());
emp.setProperties(new HashMap<String, String>());
List<Long> phoneNums = new ArrayList<Long>();
boolean insidePropertiesObj=false;
parseJSON(jsonParser, emp, phoneNums, insidePropertiesObj);
long[] nums = new long[phoneNums.size()];
int index = 0;
for(Long l :phoneNums){
nums[index++] = l;
}
emp.setPhoneNumbers(nums);
jsonParser.close();
//print employee object
System.out.println("Employee Object\\n\\n"+emp);
}
private static void parseJSON(JsonParser jsonParser, Employee emp,
List<Long> phoneNums, boolean insidePropertiesObj) throws JsonParseException, IOException {
//loop through the JsonTokens
while(jsonParser.nextToken() != JsonToken.END_OBJECT){
String name = jsonParser.getCurrentName();
if("id".equals(name)){
jsonParser.nextToken();
emp.setId(jsonParser.getIntValue());
}else if("name".equals(name)){
jsonParser.nextToken();
emp.setName(jsonParser.getText());
}else if("permanent".equals(name)){
jsonParser.nextToken();
emp.setPermanent(jsonParser.getBooleanValue());
}else if("address".equals(name)){
jsonParser.nextToken();
//nested object, recursive call
parseJSON(jsonParser, emp, phoneNums, insidePropertiesObj);
}else if("street".equals(name)){
jsonParser.nextToken();
emp.getAddress().setStreet(jsonParser.getText());
}else if("city".equals(name)){
jsonParser.nextToken();
emp.getAddress().setCity(jsonParser.getText());
}else if("zipcode".equals(name)){
jsonParser.nextToken();
emp.getAddress().setZipcode(jsonParser.getIntValue());
}else if("phoneNumbers".equals(name)){
jsonParser.nextToken();
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
phoneNums.add(jsonParser.getLongValue());
}
}else if("role".equals(name)){
jsonParser.nextToken();
emp.setRole(jsonParser.getText());
}else if("cities".equals(name)){
jsonParser.nextToken();
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
emp.getCities().add(jsonParser.getText());
}
}else if("properties".equals(name)){
jsonParser.nextToken();
while(jsonParser.nextToken() != JsonToken.END_OBJECT){
String key = jsonParser.getCurrentName();
jsonParser.nextToken();
String value = jsonParser.getText();
emp.getProperties().put(key, value);
}
}
}
}
}
JsonParser là Jackson JSON Streaming API dùng để đọc dữ liệu JSON. Chúng ta đang sử dụng nó để đọc dữ liệu từ tệp, sau đó phương thức parseJSON() được dùng để lặp qua các tokens và xử lý chúng để tạo đối tượng Java của chúng ta. Lưu ý rằng phương thức parseJSON() được gọi đệ quy cho “address” vì đây là một đối tượng lồng nhau trong dữ liệu JSON. Để phân tích cú pháp mảng, chúng ta đang lặp qua tài liệu JSON. Chúng ta có thể sử dụng lớp JsonGenerator để tạo dữ liệu JSON bằng Streaming API.
package com.journaldev.jackson.json;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Set;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.journaldev.jackson.model.Employee;
public class JacksonStreamingWriteExample {
public static void main(String[] args) throws IOException {
Employee emp = JacksonObjectMapperExample.createEmployee();
JsonGenerator jsonGenerator = new JsonFactory()
.createGenerator(new FileOutputStream("stream_emp.txt"));
//for pretty printing
jsonGenerator.setPrettyPrinter(new DefaultPrettyPrinter());
jsonGenerator.writeStartObject(); // start root object
jsonGenerator.writeNumberField("id", emp.getId());
jsonGenerator.writeStringField("name", emp.getName());
jsonGenerator.writeBooleanField("permanent", emp.isPermanent());
jsonGenerator.writeObjectFieldStart("address"); //start address object
jsonGenerator.writeStringField("street", emp.getAddress().getStreet());
jsonGenerator.writeStringField("city", emp.getAddress().getCity());
jsonGenerator.writeNumberField("zipcode", emp.getAddress().getZipcode());
jsonGenerator.writeEndObject(); //end address object
jsonGenerator.writeArrayFieldStart("phoneNumbers");
for(long num : emp.getPhoneNumbers())
jsonGenerator.writeNumber(num);
jsonGenerator.writeEndArray();
jsonGenerator.writeStringField("role", emp.getRole());
jsonGenerator.writeArrayFieldStart("cities"); //start cities array
for(String city : emp.getCities())
jsonGenerator.writeString(city);
jsonGenerator.writeEndArray(); //closing cities array
jsonGenerator.writeObjectFieldStart("properties");
Set<String> keySet = emp.getProperties().keySet();
for(String key : keySet){
String value = emp.getProperties().get(key);
jsonGenerator.writeStringField(key, value);
}
jsonGenerator.writeEndObject(); //closing properties
jsonGenerator.writeEndObject(); //closing root object
jsonGenerator.flush();
jsonGenerator.close();
}
}
JsonGenerator dễ sử dụng hơn so với JsonParser. Đó là tất cả cho hướng dẫn tham khảo nhanh về Jackson JSON Parser Java API. Jackson JSON Java API rất dễ sử dụng và cung cấp nhiều tùy chọn để hỗ trợ các nhà phát triển làm việc với dữ liệu JSON. Bạn có thể tải dự án từ liên kết dưới đây và thử nghiệm để khám phá thêm các tùy chọn về Jackson JSON API.
Tham khảo: Trang GitHub của Jackson