Jackson JSON Java 파서 API 예제 튜토리얼

Jackson JSON Java Parser은 매우 인기가 있으며 Spring 프레임워크에서도 사용됩니다. Java JSON 처리 API는 사용자 친화적이지 않으며 Json을 Java 객체로 자동 변환하는 기능을 제공하지 않습니다. 다행히 JSON 처리에 사용할 수 있는 대안 API가 있습니다. 지난 글에서는 Google Gson API에 대해 배우고 얼마나 쉽게 사용할 수 있는지 확인했습니다.

Jackson JSON Java Parser

우리 프로젝트에서 Jackson JSON Java API를 사용하려면 프로젝트 빌드 경로에 추가하거나 Maven을 사용하는 경우 아래 종속성을 추가할 수 있습니다.

<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>2.2.3</version>
</dependency>

jackson-databind jar은 jackson-corejackson-annotations 라이브러리에 의존하므로 빌드 경로에 직접 추가하는 경우 세 가지 모두를 추가해야합니다. 그렇지 않으면 실행중에 오류가 발생할 수 있습니다. Jackson JSON 파서 API는 JSON을 POJO 객체로 변환하는 쉬운 방법을 제공하며 JSON 데이터에서 Map으로의 쉬운 변환을 지원합니다. Jackson은 제네릭도 지원하며 직접 JSON에서 객체로 변환합니다.

Jackson JSON 예제

JSON을 POJO/Java 객체로 변환하는 우리의 예제에서 중첩된 객체와 배열이 포함 된 복잡한 예제를 살펴 보겠습니다. 변환을위한 Java 객체에 배열, 목록 및 Map을 사용할 것입니다. 우리의 복잡한 JSON은 아래 구조의 employee.txt 파일에 저장되어 있습니다:

{
  "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"
  }
}

JSON 데이터에 해당하는 다음과 같은 Java 클래스가 있습니다.

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();
	}
}

Address 클래스는 루트 JSON 데이터의 내부 객체에 해당합니다.

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는 루트 JSON 객체를 나타내는 Java 빈입니다. 이제 Jackson JSON 파서 API를 사용하여 JSON을 Java 객체로 어떻게 변환 할 수 있는지 살펴 보겠습니다.

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 {
		
		// json 파일 데이터를 문자열로 읽기
		byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));
		
		// ObjectMapper 인스턴스 생성
		ObjectMapper objectMapper = new ObjectMapper();
		
		// json 문자열을 객체로 변환
		Employee emp = objectMapper.readValue(jsonData, Employee.class);
		
		System.out.println("Employee Object\n"+emp);
		
		// 객체를 json 문자열로 변환
		Employee emp1 = createEmployee();
		// pretty print를 위해 Object mapper 구성
		objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
		
		// 콘솔에 쓰기, 파일 등의 출력 스트림에 쓸 수 있음
		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 cities = new ArrayList();
		cities.add("Los Angeles");
		cities.add("New York");
		emp.setCities(cities);

		Map props = new HashMap();
		props.put("salary", "1000 Rs");
		props.put("age", "28 years");
		emp.setProperties(props);

		return emp;
	}

}

위의 프로그램을 실행하면 다음과 같은 출력이 나옵니다.

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은 Jackson API에서 가장 중요한 클래스이며, JSON을 Java 객체로 변환하고 Java 객체를 JSON으로 변환하기 위한 readValue() 및 writeValue() 메서드를 제공합니다. ObjectMapper 클래스는 재사용할 수 있으며 싱글톤 객체로 한 번만 초기화할 수 있습니다. byte 배열, 파일, 입력/출력 스트림 및 Reader/Writer 객체와 함께 작동하는 readValue() 및 writeValue() 메서드의 오버로드된 버전이 많이 있습니다.

Jackson JSON – JSON을 Map으로 변환

가끔은 아래와 같은 JSON 객체가 data.txt 파일에 있습니다:

{
  "name": "David",
  "role": "Manager",
  "city": "Los Angeles"
}

그리고 우리는 이를 맵으로 변환하고 동일한 속성과 키를 가진 자바 객체로 변환하고 싶습니다. Jackson JSON API에서 아래 코드와 함께 두 가지 메서드로 매우 쉽게 수행할 수 있습니다:

//json을 Map으로 변환
byte[] mapData = Files.readAllBytes(Paths.get("data.txt"));
Map myMap = new HashMap();

ObjectMapper objectMapper = new ObjectMapper();
myMap = objectMapper.readValue(mapData, HashMap.class);
System.out.println("Map is: "+myMap);

//다른 방법
myMap = objectMapper.readValue(mapData, new TypeReference>() {});
System.out.println("Map using TypeReference: "+myMap);

위의 코드 조각을 실행하면 다음 출력이 생성됩니다:

Map is: {name=David, role=Manager, city=Los Angeles}
Map using TypeReference: {name=David, role=Manager, city=Los Angeles}

Jackson JSON – 특정 JSON 키 읽기

가끔 우리는 json 데이터가 있을 때 몇 개의 키 값에만 관심이 있으므로 전체 JSON을 객체로 변환하는 것은 좋지 않은 아이디어입니다. Jackson JSON API는 DOM 파서와 같은 트리 형식으로 json 데이터를 읽을 수 있는 옵션을 제공하며, 이를 통해 JSON 객체의 특정 요소를 읽을 수 있습니다. 아래 코드는 json 파일에서 특정 항목을 읽는 스니펫을 제공합니다.

// JSON 파일 데이터를 문자열로 읽기
byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));

// ObjectMapper 인스턴스 생성
ObjectMapper objectMapper = new ObjectMapper();

// DOM 파서처럼 JSON 읽기
JsonNode rootNode = objectMapper.readTree(jsonData);
JsonNode idNode = rootNode.path("id");
System.out.println("id = "+idNode.asInt());

JsonNode phoneNosNode = rootNode.path("phoneNumbers");
Iterator elements = phoneNosNode.elements();
while(elements.hasNext()){
	JsonNode phone = elements.next();
	System.out.println("Phone No = "+phone.asLong());
}

위의 코드 스니펫을 실행하면 다음과 같은 출력을 얻게 됩니다.

id = 123
Phone No = 123456
Phone No = 987654

Jackson JSON – JSON 문서 편집

Jackson JSON Java API는 JSON 데이터에서 키를 추가, 편집 및 삭제하는 유용한 메서드를 제공하며, 그런 다음 새로운 json 파일로 저장하거나 스트림에 쓸 수 있습니다. 아래 코드는 이를 쉽게 수행하는 방법을 보여줍니다.

byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));

ObjectMapper objectMapper = new ObjectMapper();

// JsonNode 생성
JsonNode rootNode = objectMapper.readTree(jsonData);

// JSON 데이터 업데이트
((ObjectNode) rootNode).put("id", 500);
// 새로운 키 값 추가
((ObjectNode) rootNode).put("test", "test value");
// 기존 키 제거
((ObjectNode) rootNode).remove("role");
((ObjectNode) rootNode).remove("properties");
objectMapper.writeValue(new File("updated_emp.txt"), rootNode);

위의 코드를 실행하고 새 파일을 확인해보면 “role”과 “properties” 키가 없음을 알 수 있습니다. 또한 “id” 값이 500으로 업데이트되고 “test”라는 새 키가 updated_emp.txt 파일에 추가되었음을 알 수 있습니다.

Jackson JSON 스트리밍 API 예제

Jackson JSON Java API는 대용량 json 데이터 작업에 유용한 스트리밍 지원도 제공합니다. 전체 파일을 토큰으로 읽고 적은 메모리를 사용하기 때문입니다. 스트리밍 API의 유일한 문제점은 JSON 데이터를 구문 분석하는 동안 모든 토큰을 처리해야 한다는 것입니다. JSON 데이터가 {“role”:“Manager”}인 경우에는 다음과 같은 순서로 토큰을 얻게 됩니다. – { (시작 객체), “role” (키 이름), “Manager” (키 값) 및 } (끝 객체). 콜론 (:)은 JSON의 구분 기호이며 따라서 토큰으로 간주되지 않습니다.

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 {
		
		//JsonParser 객체 생성
		JsonParser jsonParser = new JsonFactory().createParser(new File("employee.txt"));
		
		//토큰 반복
		Employee emp = new Employee();
		Address address = new Address();
		emp.setAddress(address);
		emp.setCities(new ArrayList());
		emp.setProperties(new HashMap());
		List phoneNums = new ArrayList();
		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();
		//직원 객체 출력
		System.out.println("Employee Object\n\n"+emp);
	}

	private static void parseJSON(JsonParser jsonParser, Employee emp,
			List phoneNums, boolean insidePropertiesObj) throws JsonParseException, IOException {
		
		//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();
				//중첩된 객체, 재귀 호출
				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는 json 데이터를 읽기 위한 jackson json 스트리밍 API이며, 파일에서 데이터를 읽고 parseJSON() 메서드를 사용하여 토큰을 반복하고 이를 처리하여 우리의 자바 객체를 생성합니다. parseJSON() 메서드가 “address”에 대해 재귀적으로 호출되는 것에 유의하십시오. 이는 json 데이터에서 중첩된 객체이기 때문입니다. 배열을 구문 분석하기 위해 json 문서를 반복합니다. 우리는 스트리밍 API로 json 데이터를 생성하기 위해 JsonGenerator 클래스를 사용할 수 있습니다.

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"));
		//보기 좋게 출력하기 위해
		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 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는 JsonParser와 비교하여 사용하기 쉽습니다. 이것은 Jackson JSON 파서 Java API에 대한 빠른 참조 튜토리얼입니다. Jackson JSON Java API는 사용하기 쉽고 JSON 데이터로 작업하는 개발자들을 위한 다양한 옵션을 제공합니다. 아래 링크에서 프로젝트를 다운로드하여 더 많은 Jackson Json API 옵션을 탐색해보세요.

Jackson JSON 프로젝트 다운로드

참고: Jackson GitHub 페이지

Source:
https://www.digitalocean.com/community/tutorials/jackson-json-java-parser-api-example-tutorial