JSON 라이브러리 Gson과 Jackson - Gson편
앞서 jackson을 어떻게 사용해야 하는가에 대해서 탐구해 보았는데
이번은 gson에 대해서 탐구를 해보겠다
Gson
Site : https://github.com/google/gson
Gson은 Java Object를 JSON 표현으로 변환하는 데 사용할 수있는 Java 라이브러리
또한 JSON 문자열을 동등한 Java 객체로 변환하는 데 사용할 수 있다.
Gson은 소스 코드가없는 기존 오브젝트를 포함하여
임의의 Java 오브젝트에 대해 작업 할 수 있다.
Gradle Dependency
implementation 'com.google.code.gson:gson:2.8.5'
1. POJO ( Plain Old Java Object ) 생성
enum class Gender {
Male,
Female;
}
data class Person(
val name: Name,
val age: Int,
val gender: Gender
)
data class Name(
val firstName: String,
val lastName: String
)
Jackson과 동일한 객체를 사용하였다.
2. Java Object -> Json
fun main() {
val person = Person(Name("홍", "길동"), 20, Gender.Male)
val json = Gson().toJson(person)
println(json)
}
[ 결과 ]
{"age":20,"gender":"Male","name":{"firstName":"홍","lastName":"길동"}}
기본적으로 Gson객체를 생성후 toJson 메서드를 사용하면된다.
Jackson과 다르게 field를 정렬된 상태로 출력을 시킨다.
[ Tip ] Json을 이쁘게 출력시키는 방법은 아래와 같이 코드를 작성
val gson = GsonBuilder().setPrettyPrinting().create()
gson.toJson(person)
{
"age": 20,
"gender": "Male",
"name": {
"firstName": "홍",
"lastName": "길동"
}
}
3. Json -> Java Object
fun main() {
val json = "{\"age\":20,\"gender\":\"Male\",\"fullName\":{\"firstName\":\"홍\",\"lastName\":\"길동\"}}"
val result = Gson().fromJson(json, Person::class.java)
println(result)
}
[ 결과 ]
Person(name=Name(firstName=홍, lastName=길동), age=20, gender=Male)
Jackson과 다르게 기본값을 주지 않았지만 잘 작동한다.
4. Annotation
속성을 서포트 해주는 어노테이션! 2가지가 있다.
★ @Expose
Serialize/Deserialize 시 제외시킬 프로퍼티를 지정 Jackson과 간단하게 아래와 같이 어노테이션을 붙혀주면 된다.
data class Person(
@Expose(serialize = false)
val name: Name,
val age: Int,
val gender: Gender
)
위의 코드로 예상을 하면 name의 직렬화를 false로 하였으니
역직렬화를 할 경우 name이 null이 나와야 할 것이다.
{"age":20,"gender":"Male","name":{"firstName":"홍","lastName":"길동"}}
Person(name=Name(firstName=홍, lastName=길동), age=20, gender=Male)
음..? 적용이 되지 않는다... 또 Jackson처럼 코틀린 때문에 그런가 싶었더니
Gson에 옵션을 추가 시켜주어야 한다고 한다.
val gson = GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()
gson 객체를 생성하고 excludeFieldsWithoutExposeAnnotation를 해주어야 한다.
다시 빌드를 해보면
Serialize : {}
DeSerialize : Person(name=null, age=0, gender=null)
이번엔 모든 프로퍼티가 직렬화가 되지 않았다..
알고보니 이녀석은 좀 까다로운놈!
아래의 표를 참고하자
Code | Serialize | Deserialize |
@Expose(deserialize = false) val name: Name |
YES | NO |
@Expose val name: Name |
YES | YES |
@Expose(serialize = false) val name: Name |
NO | YES |
@Expose(serialize = false, deserialize = false) val name: Name |
NO | NO |
val name: Name |
NO | NO |
[ 결과 ]
{"age":20,"gender":"Male"}
Person(name=null, age=20, gender=Male)
보다시피 이름과 나이가 기본값인 공백과 0으로 설정된 것을 볼 수 있다.
상황에 따라서 잘 설정하면 될것 같다.
★ SerializedName
Jackson의 JsonProperty와 같이
getter/setter 의 이름을 property 와 다른 이름을 사용할 수 있도록 설정
Database 를 자바 클래스로 매핑하는데 DB의 컬럼명이 알기 어려울 경우등에 유용하게 사용
data class Person(
@SerializedName("fullName")
val name: Name = Name(),
val age: Int = 0,
val gender: Gender = Gender.Male
)
작동을 시켜보지만 별다른 변경점이 없다...
코틀린 DataClass 때문인데.. 별도의 모듈을 적용시켜 주어야 한다.
아래와 같이 코드를 변경!
val json = Gson().toJson(person)
println(json)
[ 결과 ]
( 변경 전 ) : {"age":20,"gender":"Male","name":{"firstName":"홍","lastName":"길동"}}
( 변경 후 ) : {"age":20,"gender":"Male","fullName":{"firstName":"홍","lastName":"길동"}}
5. Custom Serializer/Deserialize
JsonSerializer/Deserializer
사실상 Json을 쉽게 작성하고자 만들어 진것이 Gson!
굳이 내가 만든 객체의 프로퍼티가 Primitive라면 커스텀을 만들 필요는 없다...
여기서는 Date라는 클래스에 대해서만 작성되었고
Date는 Parsing을 어떻게 하냐에 따라서 2019-06-18도 Date 2019.06.18도 Date이다
이러한 경우에만 커스텀을 만들어 주면 될 것 같다. 이하 Jackson 동일
직렬화
class DateSerializer : JsonSerializer {
@SuppressLint("SimpleDateFormat")
val dateFormat = SimpleDateFormat("yyyy-MM-dd")
override fun serialize(src: Date, typeOfSrc: Type?,
context: JsonSerializationContext?): JsonElement {
return JsonPrimitive(dateFormat.format(src))
}
}
역 직렬화
class DateDeserializer : JsonDeserializer {
@SuppressLint("SimpleDateFormat")
val dateFormat = SimpleDateFormat("yyyy-MM-dd")
override fun deserialize(json: JsonElement, typeOfT: Type?,
context: JsonDeserializationContext?): Date {
return dateFormat.parse(json.asString)
}
}
등록
val gson = GsonBuilder()
.registerTypeAdapter(Date::class.java, DateSerializer())
.registerTypeAdapter(Date::class.java, DateDeserializer())
.create()
Person 코드 변경
data class Person(
val name: Name,
val age: Int,
val gender: Gender,
val birth: Date
)
실행
val birth = SimpleDateFormat("yyyy-MM-dd").parse("2019-06-18")
val person = Person(Name("홍", "길동"), 20, Gender.Male, birth)
val json = gson.toJson(person)
val result = gson.fromJson(json, Person::class.java)
println(json)
println(result)
[ 결과 ]
D/gson: {"age":20,"birth":"2019-06-18","gender":"Male","name":{"firstName":"홍","lastName":"길동"}}
D/gson: Person(name=Name(firstName=홍, lastName=길동), age=20, gender=Male,
birth=Tue Jun 18 00:00:00 GMT+09:00 2019)
정상적으로 Date로 파싱되는 것을 볼 수 있다.
이로써 Gson도 끝났다.. 상황에 따라 Jackson을 쓰던 gson을 쓰면 될 것 같다.