1. 개요
이 기사에서는 Jersey 또는 Jackson과 같은 타사 의존성을 사용하지 않고 핵심 Java EE만 사용하여 JSON을 처리하는 방법을 보여줍니다. 우리가 사용하게 될 거의 모든 것은 javax.json 패키지에서 제공됩니다.
2. JSON 문자열 에 객체 쓰기
Java 객체를 JSON 문자열 로 변환하는 것은 매우 쉽습니다. 간단한 Person 클래스 가 있다고 가정해 보겠습니다 .
public class Person {
private String firstName;
private String lastName;
private Date birthdate;
// getters and setters
}
해당 클래스의 인스턴스를 JSON String 으로 변환하려면 먼저 JsonObjectBuilder 인스턴스를 만들고 add() 메서드 를 사용하여 속성/값 쌍을 추가해야 합니다.
JsonObjectBuilder objectBuilder = Json.createObjectBuilder()
.add("firstName", person.getFirstName())
.add("lastName", person.getLastName())
.add("birthdate", new SimpleDateFormat("DD/MM/YYYY")
.format(person.getBirthdate()));
add () 메서드에는 몇 가지 오버로드된 버전이 있습니다. 두 번째 매개변수로 대부분의 기본 유형(박스형 객체 포함)을 받을 수 있습니다.
속성 설정을 마치면 개체를 String 에 작성하기만 하면 됩니다 .
JsonObject jsonObject = objectBuilder.build();
String jsonString;
try(Writer writer = new StringWriter()) {
Json.createWriter(writer).write(jsonObject);
jsonString = writer.toString();
}
그리고 그게 다야! 생성된 문자열 은 다음과 같습니다.
{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978"}
2.1. JsonArrayBuilder 를 사용하여 배열 만들기
이제 예제에 좀 더 복잡함을 더하기 위해 Person 클래스가 전자 메일 주소 List을 포함하는 emails 라는 새 속성을 추가하도록 수정 되었다고 가정해 보겠습니다.
public class Person {
private String firstName;
private String lastName;
private Date birthdate;
private List<String> emails;
// getters and setters
}
해당 List의 모든 값을 JsonObjectBuilder 에 추가하려면 JsonArrayBuilder 의 도움이 필요합니다 .
JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
for(String email : person.getEmails()) {
arrayBuilder.add(email);
}
objectBuilder.add("emails", arrayBuilder);
우리는 JsonArrayBuilder 개체를 두 번째 매개 변수로 사용하는 add() 메서드 의 또 다른 오버로드 버전을 사용하고 있습니다.
이제 두 개의 이메일 주소가 있는 Person 개체 에 대해 생성된 문자열을 살펴보겠습니다 .
{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978",
"emails":["michael.scott@dd.com","michael.scarn@gmail.com"]}
2.2. PRETTY_PRINTING 으로 출력 형식 지정
따라서 우리는 Java 객체를 유효한 JSON String 으로 성공적으로 변환했습니다 . 이제 다음 섹션으로 이동하기 전에 간단한 서식을 추가하여 출력을 "JSON과 유사"하고 읽기 쉽게 만들어 보겠습니다.
이전 예제에서는 간단한 Json 을 사용하여 JsonWriter 를 만들었습니다 . createWriter() 정적 메소드. 생성된 String 을 더 잘 제어하기 위해 Java 7의 JsonWriterFactory 기능을 활용하여 특정 구성으로 작성자를 생성합니다.
Map<String, Boolean> config = new HashMap<>();
config.put(JsonGenerator.PRETTY_PRINTING, true);
JsonWriterFactory writerFactory = Json.createWriterFactory(config);
String jsonString;
try(Writer writer = new StringWriter()) {
writerFactory.createWriter(writer).write(jsonObject);
jsonString = writer.toString();
}
코드가 다소 장황해 보일 수 있지만 실제로는 많은 작업을 수행하지 않습니다.
먼저 구성 맵을 생성자에 전달하는 JsonWriterFactory 인스턴스를 만듭니다 . 맵에는 PRETTY_PRINTING 속성을 true로 설정하는 항목이 하나만 포함되어 있습니다. 그런 다음 Json.createWriter() 를 사용하는 대신 해당 팩토리 인스턴스를 사용하여 작성자를 만듭니다 .
새 출력에는 JSON 문자열 을 특징짓는 독특한 줄 바꿈 및 표가 포함됩니다 .
{
"firstName":"Michael",
"lastName":"Scott",
"birthdate":"06/15/1978",
"emails":[
"michael.scott@dd.com",
"michael.scarn@gmail.com"
]
}
3. 문자열 에서 자바 객체 만들기
이제 반대 작업을 수행해 보겠습니다. JSON 문자열 을 Java 개체로 변환합니다.
변환 프로세스의 주요 부분은 JsonObject 를 중심으로 이루어 집니다 . 이 클래스의 인스턴스를 만들려면 정적 메서드 Json.createReader() 다음에 readObject() 를 사용합니다 .
JsonReader reader = Json.createReader(new StringReader(jsonString));
JsonObject jsonObject = reader.readObject();
createReader() 메소드는 InputStream 을 매개변수로 사용합니다. 이 예제에서는 JSON이 String 개체에 포함되어 있기 때문에 StringReader 를 사용하고 있지만 FileInputStream 을 사용하여 파일에서 콘텐츠를 읽는 데 동일한 메서드를 사용할 수 있습니다 .
JsonObject 의 인스턴스가 있으면 getString() 메서드 를 사용하여 속성을 읽고 얻은 값을 Person 클래스의 새로 생성된 인스턴스에 할당할 수 있습니다.
Person person = new Person();
person.setFirstName(jsonObject.getString("firstName"));
person.setLastName(jsonObject.getString("lastName"));
person.setBirthdate(dateFormat.parse(jsonObject.getString("birthdate")));
3.1. JsonArray 를 사용하여 List 값 가져오기
JsonObject 에서 List 값을 추출하려면 JsonArray 라는 특수 클래스를 사용해야 합니다 .
JsonArray emailsJson = jsonObject.getJsonArray("emails");
List<String> emails = new ArrayList<>();
for (JsonString j : emailsJson.getValuesAs(JsonString.class)) {
emails.add(j.getString());
}
person.setEmails(emails);
그게 다야! 우리는 Json String 에서 Person 의 완전한 인스턴스를 만들었습니다 .
4. 값 쿼리
이제 JSON String 내부에 있는 매우 특정한 데이터 조각에 관심이 있다고 가정해 보겠습니다 .
애완 동물 가게의 클라이언트를 나타내는 아래 JSON을 고려하십시오. 어떤 이유로 애완 동물 List에서 세 번째 애완 동물의 이름을 가져와야 한다고 가정해 보겠습니다.
{
"ownerName": "Robert",
"pets": [{
"name": "Kitty",
"type": "cat"
}, {
"name": "Rex",
"type": "dog"
}, {
"name": "Jake",
"type": "dog"
}]
}
단일 값을 얻기 위해 전체 텍스트를 Java 개체로 변환하는 것은 그다지 효율적이지 않습니다. 따라서 전체 변환 시련을 거치지 않고 JSON 문자열 을 쿼리하는 몇 가지 전략을 확인하겠습니다 .
4.1. 개체 모델 API를 사용하여 쿼리
JSON 구조에서 알려진 위치를 사용하여 속성 값을 쿼리하는 것은 간단합니다. 이전 예제에서 사용된 것과 동일한 클래스 인 JsonObject 의 인스턴스를 사용할 수 있습니다 .
JsonReader reader = Json.createReader(new StringReader(jsonString));
JsonObject jsonObject = reader.readObject();
String searchResult = jsonObject
.getJsonArray("pets")
.getJsonObject(2)
.getString("name");
여기서 주의할 점은 get*() 메서드 의 올바른 시퀀스를 사용하여 jsonObject 속성 을 탐색하는 것입니다.
이 예제에서는 먼저 getJsonArray()를 사용하여 "pets" List에 대한 참조를 가져옵니다. getJsonArray() 는 3개의 레코드가 있는 List을 반환합니다. 그런 다음 인덱스를 매개변수로 사용 하여 List의 세 번째 항목을 나타내는 다른 JsonObject 를 반환하는 getJsonObject() 메서드를 사용합니다. 마지막으로 getString() 을 사용하여 찾고 있는 문자열 값을 얻습니다.
4.2. Streaming API를 사용한 쿼리
JSON 문자열 에 대해 정확한 쿼리를 수행하는 또 다른 방법 은 JsonParser 를 기본 클래스로 하는 스트리밍 API를 사용하는 것 입니다.
JsonParser 는 JS에 대한 매우 빠른 읽기 전용 정방향 액세스를 제공하지만 개체 모델보다 다소 복잡하다는 단점이 있습니다.
JsonParser jsonParser = Json.createParser(new StringReader(jsonString));
int count = 0;
String result = null;
while(jsonParser.hasNext()) {
Event e = jsonParser.next();
if (e == Event.KEY_NAME) {
if(jsonParser.getString().equals("name")) {
jsonParser.next();
if(++count == 3) {
result = jsonParser.getString();
break;
}
}
}
}
이 예제는 이전 예제와 동일한 결과를 제공합니다. 애완 동물 List 에서 세 번째 애완 동물 의 이름 을 반환합니다 .
Json.createParser() 를 사용하여 JsonParser 가 생성 되면 찾고 있는 속성에 도달할 때까지 반복자(따라서 JsonParser 의 "순방향 액세스" 특성 )를 사용하여 JSON 토큰을 탐색해야 합니다. .
반복자를 통과할 때마다 JSON 데이터의 다음 토큰으로 이동합니다. 따라서 현재 토큰이 예상 유형인지 확인하는 데 주의를 기울여야 합니다. 이것은 next() 호출 에 의해 반환된 이벤트 를 확인하여 수행됩니다.
토큰의 종류는 다양합니다. 이 예에서는 속성의 이름을 나타내는 KEY_NAME 유형(예: "ownerName", "pets", "name", "type")에 관심이 있습니다. 세 번째로 "이름" 값이 있는 KEY_NAME 토큰을 통과 하면 다음 토큰에 List에서 세 번째 애완 동물의 이름을 나타내는 문자열 값이 포함된다는 것을 알 수 있습니다.
특히 더 복잡한 JSON 구조의 경우 개체 모델 API를 사용하는 것보다 확실히 어렵습니다. 항상 그렇듯이 하나 또는 다른 것 사이의 선택은 처리할 특정 시나리오에 따라 다릅니다.
5. 결론
우리는 몇 가지 간단한 예제를 통해 Java EE JSON 처리 API에 대한 많은 근거를 다루었습니다. JSON 처리에 대한 다른 멋진 내용을 알아보려면 Jackson 기사 시리즈를 확인하세요 .
GitHub 리포지토리 에서 이 기사에 사용된 클래스의 소스 코드와 일부 단위 테스트를 확인하십시오 .