scala 世界中,關於處理 json 的 library 很多, 這裡列出了 13 個: Scala standard libray, jawn, sphere-json, argonaut, circe, json4s, spray-json-shapeless, spray-json, lift-json, play-json, rapture,用 google trends 查了一下,json4s查詢的次數還是多一些,json4s 的目標就是提供一個單一的 AST 供其他Scala類庫來使用,以下測試如何使用 json4s。
準備使用 json4s
在 build.sbt 加上 org.json4s 的 library
val json4sVersion = "3.3.0"
libraryDependencies ++= Seq(
// json4s
"org.json4s" %% "json4s-native" % json4sVersion,
"org.json4s" %% "json4s-jackson" % json4sVersion,
"org.json4s" %% "json4s-ext" % json4sVersion
)
json4s 需要 import 以下的 packages
import org.json4s._
import org.json4s.JsonDSL._
import org.json4s.native.JsonMethods._
主要提供 parse, render 進行 json 轉換,render 需要輸入 JValue 物件,必須利用 JsonDSL 提供 implicit 轉換,parse 很明確,就是 parsing JSON 字串。
測試 json4s
import java.text.SimpleDateFormat
import org.json4s.JsonDSL._
import org.json4s._
import org.json4s.native.JsonMethods._
import org.scalatest.{FunSpec, Matchers}
class TestJson4s extends FunSpec with Matchers {
implicit val formats = new DefaultFormats {
//override def dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
override def dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
}
//implicit val formats = Serialization.formats(ShortTypeHints(List()))
// implicit val formats = new DefaultFormats {
// override val dateFormat: DateFormat = new DateFormat {
// override def parse(s: String): Option[Date] = Some(new Date(s.toLong * 1000))
// override def format(d: Date): String = (d.getTime/1000).toString
// }
// }
case class Student(name: String, age: Int)
describe("Testing json4s marshalling, unmarshalling") {
it("parse json and extract to class") {
// 將原始的 json 字串,轉換成 json4s JObject 物件
println()
println(">將 json 轉換成 JObject")
val tom = parse( """ { "name" : "tom","age":23,"number":[1,2,3,4] } """)
println(s"tom:${tom.getClass.getName}")
println(tom)
val tstring: String = tom.toString
//JObject(List((name,JString(tom)), (age,JInt(23)), (number,JArray(List(JInt(1), JInt(2), JInt(3), JInt(4))))))
tstring should be("JObject(List((name,JString(tom)), (age,JInt(23)), (number,JArray(List(JInt(1), JInt(2), JInt(3), JInt(4))))))")
// 上面的轉換之後 並不是 Scala 的原生物件
// 要用 Map[String, _] 轉換
println()
println(">把 JObject 轉成 Scala 物件")
for ((k, v) <- tom.values.asInstanceOf[Map[String, _]]) {
println(s"${k} -> ${v}:${v.getClass}")
}
// name -> tom:class java.lang.String
// age -> 23:class scala.math.BigInt
// number -> List(1, 2, 3, 4):class scala.collection.immutable.$colon$colon
// 可以用 \ 取得某個欄位的值
println()
println(">可以用 \\ 取得某個欄位的值")
println(s"name -> ${(tom \ "name").values}")
println(s"age -> ${(tom \ "age").values}")
println(s"number -> ${(tom \ "number").values}")
// name -> tom
// age -> 23
// number -> List(1, 2, 3, 4)
val name = (tom \ "name").values
name should be("tom")
// 可直接將 tom 轉換成 Student
val student: Student = tom.extract[Student]
println()
println(">可直接將 tom 轉換成 Student")
println(student)
// Student(tom,23)
student.name should be("tom")
///// 列印 json
println()
println("> 列印 json 時要呼叫 compact")
println(compact(render(Map("name" -> "tom", "age" -> "23"))))
//结果:{"name":"tom","age":"23"}
println()
println("> 只支援 Tuple2[String, A] 這種格式的 tuple")
val tuple = ("name", "tom")
println(compact(render(tuple)))
//结果:{"name":"tom"}
println()
println("> 可以用 ~ 將兩個 tuple 連接在一起")
val tuple2 = ("name", "tom") ~ ("age", 23)
println(tuple2)
println(compact(render(tuple2)))
//结果:
//JObject(List((name,JString(tom)), (age,JInt(23))))
//{"name":"tom","age":23}
println()
println("> 當 value 為 None 時,就不會序列化")
val tuple3 = ("name", "tom") ~("age", None: Option[Int])
println(tuple3)
//结果:
//JObject(List((name,JString(tom)), (age,JNothing)))
//{"name":"tom"}
println()
println("> 處理 date 必須要加上 implicit val formats, 用 pretty 可以讓列印結果容易閱讀")
case class Winner(id: Long, numbers: List[Int])
case class Lotto(id: Long, winningNumbers: List[Int], winners: List[Winner], drawDate: Option[java.util.Date])
val winners = List(Winner(23, List(2, 45, 34, 23, 3, 5)), Winner(54, List(52, 3, 12, 11, 18, 22)))
val lotto = Lotto(5, List(2, 45, 34, 23, 7, 5, 3), winners, Option(new java.util.Date()))
val customDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val json =
("lotto" ->
("lotto-id" -> lotto.id) ~
("winning-numbers" -> lotto.winningNumbers) ~
//("draw-date" -> lotto.drawDate.map(_.toString)) ~
("draw-date" -> lotto.drawDate.map( customDateFormatter.format(_) )) ~
("winners" ->
lotto.winners.map { w =>
(("winner-id" -> w.id) ~
("numbers" -> w.numbers))
}))
println(compact(render(json)))
// {"lotto":{"lotto-id":5,"winning-numbers":[2,45,34,23,7,5,3],"draw-date":"2016-05-19 12:03:36","winners":[{"winner-id":23,"numbers":[2,45,34,23,3,5]},{"winner-id":54,"numbers":[52,3,12,11,18,22]}]}}
println(pretty(render(json)))
//
// {
// "lotto":{
// "lotto-id":5,
// "winning-numbers":[2,45,34,23,7,5,3],
// "draw-date":"2016-05-19 12:03:36",
// "winners":[{
// "winner-id":23,
// "numbers":[2,45,34,23,3,5]
// },{
// "winner-id":54,
// "numbers":[52,3,12,11,18,22]
// }]
// }
// }
//
println()
println("> 用 merge 合併兩個 json")
val t1 = parse(
"""
{"name":"tom",
"age":23,
"class":["xiaoerban"]
}
""")
val t2 = parse(
"""
{"name":"tom",
"age":23,
"class":["xiaosanban"]
}
""")
val t3 = t1 merge t2
println(pretty(render(t3)))
// {
// "name":"tom",
// "age":23,
// "class":["xiaoerban","xiaosanban"]
// }
println()
println("> 用 Diff 檢查兩個 JObject 的差異")
val Diff(changed, added, deleted) = t3 diff t1
println("changed", changed)
println("added", added)
println("deleted", deleted)
// (changed,JNothing)
// (added,JNothing)
// (deleted,JObject(List((class,JArray(List(JString(xiaosanban)))))))
println()
println("> 可以將 xml 轉換成 json")
import org.json4s.Xml.toJson
val xml =
<users>
<user>
<id>1</id>
<name>Harry</name>
</user>
<user>
<id>2</id>
<name>David</name>
</user>
</users>
val json2 = toJson(xml)
println(pretty(render(json2)))
}
}
}
以下為執行的結果
>將 json 轉換成 JObject
tom:org.json4s.JsonAST$JObject
JObject(List((name,JString(tom)), (age,JInt(23)), (number,JArray(List(JInt(1), JInt(2), JInt(3), JInt(4))))))
>把 JObject 轉成 Scala 物件
name -> tom:class java.lang.String
age -> 23:class scala.math.BigInt
number -> List(1, 2, 3, 4):class scala.collection.immutable.$colon$colon
>可以用 \ 取得某個欄位的值
name -> tom
age -> 23
number -> List(1, 2, 3, 4)
>可直接將 tom 轉換成 Student
Student(tom,23)
> 列印 json 時要呼叫 compact
{"name":"tom","age":"23"}
> 只支援 Tuple2[String, A] 這種格式的 tuple
{"name":"tom"}
> 可以用 ~ 將兩個 tuple 連接在一起
JObject(List((name,JString(tom)), (age,JInt(23))))
{"name":"tom","age":23}
> 當 value 為 None 時,就不會序列化
JObject(List((name,JString(tom)), (age,JNothing)))
> 處理 date 必須要加上 implicit val formats, 用 pretty 可以讓列印結果容易閱讀
{"lotto":{"lotto-id":5,"winning-numbers":[2,45,34,23,7,5,3],"draw-date":"2016-05-19 13:57:54","winners":[{"winner-id":23,"numbers":[2,45,34,23,3,5]},{"winner-id":54,"numbers":[52,3,12,11,18,22]}]}}
{
"lotto":{
"lotto-id":5,
"winning-numbers":[2,45,34,23,7,5,3],
"draw-date":"2016-05-19 13:57:54",
"winners":[{
"winner-id":23,
"numbers":[2,45,34,23,3,5]
},{
"winner-id":54,
"numbers":[52,3,12,11,18,22]
}]
}
}
> 用 merge 合併兩個 json
{
"name":"tom",
"age":23,
"class":["xiaoerban","xiaosanban"]
}
> 用 Diff 檢查兩個 JObject 的差異
(changed,JNothing)
(added,JNothing)
(deleted,JObject(List((class,JArray(List(JString(xiaosanban)))))))
> 可以將 xml 轉換成 json
{
"users":{
"user":[{
"id":"1",
"name":"Harry"
},{
"id":"2",
"name":"David"
}]
}
}
[info] TestJson4s:
[info] Testing json4s marshalling, unmarshalling
[info] - parse json and extract to class
[info] Run completed in 371 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
沒有留言:
張貼留言