ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JSON 라이브러리 Gson과 Jackson - Gson편
    Android/Json 2019. 6. 18. 13:38

    앞서 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을 쓰면 될 것 같다.

    댓글

Designed by Tistory.