Scala

µPickle(micro-pickle) 라이브러리를 이용한 Scala 에서의 JSON 변환

partner_jun 2017. 7. 11. 18:35

µPickle은 자바의 유명한 라이브러리 Jackson처럼 Scala의 객체를 JSON 문자열로 변환해주는 라이브러리다. 


공식 홈페이지에 따르면 0.4.3 현재 

Boolean, Byte, Char, Short, Int, Long, Float, Double등의 기본 자료형(당연히 String도 포함)

Tuple1부터 Tuple22까지,

불변 콜렉션인 Seq, List, Vector, Set, SortedSet, Option, Array, Maps,

CanBuildForm으로 구현 가능한 모든 콜렉션,

Duration(Scala.concurrent)Either(Left, Right 각 자료를 가지는),

case classcase object,

sealed traitseald class,

UUID,

null


등의 타입을 지원하고 있다.



0. Dependency 추가, 태그 Import

libraryDependencies += "com.lihaoyi" %% "upickle" % "0.4.4"
import upickle.default._

축약된 문법으로 사용하기 위해서는 직접 import문을 작성해야 한다.



1. case class <-> JSON

case class Sample1(name: String, age: Int)
val sample1 = Sample1("jack", 12)
val sample1Write: String = write(sample1)
println(sample1Write) // {"name":"jack","age":12}

val sample1Read: Sample1 = read[Sample1](sample1Write)
println(sample1Read) // Sample1(jack,12)

기본 자료형과 마찬가지로 write와 read 함수를 이용해 아주 간단하게 변환이 가능하다. 하지만 read 함수에는 타입을 표기하는 것이 좋은데, 암시적으로 타입 변환이 일어날 수 있기 때문이다.(혹은 아예 실행이 되지 않는다!)



2. case class + collection <-> JSON

case class Sample2(sample1Lst: List[Sample1])
val sample2 = Sample2(List(sample1, Sample1("john", 13)))
val sample2Write: String = write(sample2)
println(sample2Write) // {"sample1Lst":[{"name":"jack","age":12},{"name":"john","age":13}]}

val sample2Read: Sample2 = read[Sample2](sample2Write)
println(sample2Read) // Sample2(List(Sample1(jack,12), Sample1(john,13)))

콜렉션도 JSON 양식에 맞게 변환된다.



3. case class + case class <-> JSON

case class Sample3(sample1: Sample1, zipCode: Int)
val sample3 = Sample3(sample1, 100)
val sample3Write: String = write(sample3)
println(sample3Write) // {"sample1":{"name":"jack","age":12},"zipCode":100}

val sample3Read: Sample3 = read[Sample3](sample3Write)
println(sample3Read) // Sample3(Sample1(jack,12),100)



4. case class(recursive) <-> JSON

case class Sample4(sample4: Sample4)
val sample4Inner = Sample4(null)
val sample4 = Sample4(sample4Inner)
val sample4Write: String = write(sample4)
println(sample4Write) // {"sample3":{"sample3":null}}

val sample4Read: Sample4 = read[Sample4](sample4Write)
println(sample4Read) // Sample4(Sample4(null))

자기 자신을 참조하는 자료형도 변환된다. 물론 자신을 참조하는 그래프 형태라면 StackOverflow가 발생한다.



5. case class extends sealed trait <-> JSON

sealed trait Say {
val name: String
def say(str: String): String = s"$name said $str"
}
case class Sample5(override val name: String) extends Say
val sample5 = Sample5("jack")
val sayWrite: String = write[Say](sample5)
println(sayWrite) // {"$type":"blog.blog.UpickleExample.Sample5","name":"jack"}

val sayRead: Say = read[Say](sayWrite)
println(sayRead) // Sample5(jack)

sealed trait을 구현한 case class도 JSON으로 변환할 수 있다. 'sealed' trait이다.



*. case class 내부에 작성된 값.

case class Sample6(name: String) {
var age: Int = _
}

val sample6 = Sample6("john")
sample6.age = 20

val sample6Write: String = write(sample6)
println(sample6Write) // {"name":"john"}

val sample6Read: Sample6 = read[Sample6](sample6Write)
println(sample6Read) // Sample6(john)
println(sample6Read.age) // 0

case class의 constructor element가 아닌 값은 JSON으로 변환되지 않는다.



JSON 변환은 Scala만을 사용하는 프로젝트보다 Play Framework나 Akka를 활용하는 프로젝트에서 많이 사용한다. Play Framework의 JSON 라이브러리는 간단한 값 한둘을 가져오는데는 나쁘지 않지만 객체 자체를 변환해야 할 때는 굉장히 불편하다. 그런 경우 µPickle을 유용하게 사용할 수 있을 것이라고 생각한다.