-
JSON 라이브러리 Gson과 Jackson - Jackson 편Android/Json 2019. 6. 16. 23:48
앞서 Json 어떤 라이브러리를 사용해야 하는가에 대해서 탐구해보았는데
gson와 jackson이 저/대용량에서 좋은 성능을 보여 어떤식으로 사용해야 하는지
간단하게 알아보도록 하자
IntelliJ에서 Kotlin으로 테스트를 해보았다.
Jackson
Site : https://github.com/FasterXML/jackson
Json 뿐만 아니라 XML/YAML/CSV 등 다양한 형식의 데이타를 지원하는 data-processing 툴
스트림 방식이므로 속도가 빠르며 유연하며 다양한 third party 데이타 타입을 지원한다.
Gradle Dependency
implementation "com.fasterxml.jackson.core:jackson-core:2.9.9"
implementation "com.fasterxml.jackson.core:jackson-annotations:2.9.9"
implementation "com.fasterxml.jackson.core:jackson-databind:2.9.9"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.9"
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 )
가장 기본적인 Person 객체를 생성 하였고 reference 객체와 enum 객체도 섞었다.
2. Java Object -> Json
fun main() { val person = Person(Name("홍", "길동"), 20, Gender.Male) val objectMapper = ObjectMapper() val result = objectMapper.writeValueAsString(person) println(result) }
[ 결과 ]
{"name":{"firstName":"홍","lastName":"길동"},"age":20,"gender":"Male"}
ObjectMapper : Jackson 라이브러리의 주요 액터 클래스로서 기본 POJO와의 JSON, 일반 JSON 트리 모델 (JsonNode) 간의 JSON 읽기 및 쓰기 기능 및 변환 수행 관련 기능을 제공
writeValueAsString : 객체를 받아 String으로 반환
[ Tip ] Json을 이쁘게 출력시키는 방법은 아래와 같이 코드를 작성
objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(person)
{
"name" : {
"firstName" : "홍",
"lastName" : "길동"
},
"age" : 20,
"gender" : "Male"
}
3. Json -> Java Object
fun main() { val json = "{\"name\":{\"firstName\":\"홍\",\"lastName\":\"길동\"},\"age\":20,\"gender\":\"Male\"}" val objectMapper = ObjectMapper() val result = objectMapper.readValue(json, Person::class.java) println(result) }
[ 결과 ]
cannot deserialize from Object value
음? 역직렬화가 되지 않는다.
이유는
[ 수정 ]
data class Person( val name: Name = Name(), val age: Int = 0, val gender: Gender = Gender.Male ) data class Name( val firstName: String = "", val lastName: String = "" )
파라미터 대한 기본값이 필요하다.
아마도 값이 존재하지 않을때 엄청난 크래쉬를 안고가기 싫어서 그런가 부다...
[ 결과 ]
Person(name=Name(firstName=홍, lastName=길동), age=20, gender=Male)
잘나온다 ㅎㅎ
4. Annotation
속성을 서포트 해주는 어노테이션! 총 3가지가 있다.
★ JsonIgnoreProperties / JsonIgnore
Serializer/Deserialize 시 제외시킬 프로퍼티를 지정
[ 코드 ]
@JsonIgnoreProperties("name", "age") data class Person( val name: Name = Name(), val age: Int = 0, val gender: Gender = Gender.Male )
[ 결과 ]
Person(name=Name(firstName=, lastName=), age=0, gender=Male)
보다시피 이름과 나이가 기본값인 공백과 0으로 설정된 것을 볼 수 있다.
JsonIgnoreProperties : 여러개를 한꺼번에
JsonIgnore : 한개만
상황에 따라서 잘 설정하면 될것 같다.
★ JsonProperty
getter/setter 의 이름을 property 와 다른 이름을 사용할 수 있도록 설정
Database 를 자바 클래스로 매핑하는데 DB의 컬럼명이 알기 어려울 경우등에 유용하게 사용
[ 코드 ]
data class Person( @JsonProperty("realName") val name: Name = Name(), val age: Int = 0, val gender: Gender = Gender.Male )
작동을 시켜보지만 별다른 변경점이 없다...
코틀린 DataClass 때문인데.. 별도의 모듈을 적용시켜 주어야 한다.
아래와 같이 코드를 변경!
val objectMapper = ObjectMapper()
objectMapper.registerModules(KotlinModule())[ 결과 ]
( 변경 전 ) : {"name":{"firstName":"홍","lastName":"길동"},"age":20,"gender":"Male"}
( 변경 후 ) : {"realName":{"firstName":"홍","lastName":"길동"},"age":20,"gender":"Male"}
★ JsonInclude
Serialize 시 동작을 지정한다. 기본적으로 잭슨은 값의 유무와 상관없이 무조건 serialize 하게 되지만
다음과 같이 설정할 경우 not null 이거나 none empty 일 경우에만 serialize 된다.
data class Person( @JsonInclude val name: Name?, val age: Int = 0, val gender: Gender = Gender.Male )
음.. 코틀린 상에서는 테스트가 되지를 않는다.. 아무래도 null에 민감한 아이여서 그런지 잘 작동하지는 않는다.
5. Custom Serializer/Deserialize
StdSerializer 와 StdDeserializer
두개모두 JsonSerializer/Deserializer 을 상속 받고 있다.
두개의 차이점은 Method의 차이가 아닌가 싶다.
자세한 Method는 아래 주소를 참조
직렬화
class PersonSerializer(t: Class<Person>) : StdSerializer<Person>(t) { override fun serialize(value: Person, gen: JsonGenerator, provider: SerializerProvider) { gen.writeStartObject() gen.writeObjectField("name", value.name) gen.writeNumberField("age", value.age) gen.writeStringField("gender", value.gender.toString()) gen.writeEndObject() } }
StdSerializer를 상속 후 객체를 Class 파라미터로 받아 넣어주고 알맞게 직렬화 코드를 짜주면 된다.
Name도 마찬가지로 Serializer 코드를 짜주었다
역직렬화
class PersonDeserializer(t: Class<Person>) : StdDeserializer<Person>(t) { private val objectMapper: ObjectMapper = ObjectMapper() init { val simpleModule = SimpleModule() simpleModule.addDeserializer(Name::class.java, NameDeserializer(Name::class.java)) this.objectMapper.registerModule(simpleModule) } override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Person { val objectCodec = p.codec val jsonNode = objectCodec.readTree<TreeNode>(p) val name = objectMapper.convertValue<Name>(jsonNode.get("name").toString(), Name::class.java) val age = jsonNode.get("age").toString().toInt() val gender = Gender.valueOf(jsonNode.get("gender").toString()) return Person(name, age, gender) } }
그리고 모듈을 하나 만들어 ObjectManager에 registerModule 시켜주면 된다
fun main() { val person = Person(Name("홍", "길동"), 20, Gender.Male) val module = SimpleModule() module.addSerializer(PersonSerializer(Person::class.java)) module.addSerializer(NameSerializer(Name::class.java)) module.addDeserializer(PersonDeserializer(Person::class.java)) module.addDeserializer(NameDeserializer(Name::class.java)) val objectMapper = ObjectMapper() objectMapper.registerModule(KotlinModule()) objectMapper.registerModule(module) val result = objectMapper.writeValueAsString(person) println(result) }
코드 짜는데 너무 힘들었습니다.. 1. 코틀린이여서 2. 정보가 옛날이여서... ㅋㅋㅋㅋ
코드는 나중에 리펙토링 한번 하곘습니다.
ObjectManager를 생성한 이유!
Person에는 Name이라는 레퍼런스가 있기때문에 Name의 역직렬화가 있어야 함으로
ObjectManager를 만들어서 Module로 넣어줍니다.
{"name":{"firstName":"홍","lastName":"길동"},"age":20,"gender":"Male"}
정상적으로 잘 작동 됩니다...
이렇게 해서 Jackson에 대해서 한번 알아보았구요...
나머지 관련은 추가적으로 포스팅 하겠습니다. ㅃ2~
'Android > Json' 카테고리의 다른 글
JSON 라이브러리 Gson과 Jackson - Gson편 (0) 2019.06.18 JSON 어떤 라이브러리를 사용해야 할까? (0) 2019.06.16