json4s是一個基於scala的json解析庫。java
假設使用SBT進行配置:es6
val json4sJackson = "org.json4s" %% "json4s-jackson" % "{latestVersion}"
scala> import org.json4s._ scala> import org.json4s.jackson.JsonMethods._ scala> parse(""" { "numbers" : [1, 2, 3, 4] } """) res0: org.json4s.JsonAST.JValue = JObject(List((numbers,JArray(List(JInt(1), JInt(2), JInt(3), JInt(4)))))) scala> parse("""{"name":"Toy","price":35.35}""", useBigDecimalForDouble = true) res1: org.json4s.package.JValue = JObject(List((name,JString(Toy)), (price,JDecimal(35.35))))
有兩種方式生成JSON:json
DoubleMode 將浮點型數據轉換成JDouble
,這種方式下DSL使用:數組
import org.json4s.JsonDSL._ // or import org.json4s.JsonDSL.WithDouble._
BigDecimalMode 將浮點型數據轉換成JDecimal
,這種方式下DSL使用:ide
import org.json4s.JsonDSL.WithBigDecimal._
基本數據類型映射到JSON
的基本數據類型函數
任何Seq
類型映射到JSON
的數組類型this
scala> val json = List(1, 2, 3) scala> compact(render(json)) res0: String = [1,2,3]
Tuple2[String, Any]
將生成字段es5
scala> val json = ("name" -> "joe") scala> compact(render(json)) res1: String = {"name":"joe"}
scala> val json = ("name" -> "joe") ~ ("age" -> 35) scala> compact(render(json)) res2: String = {"name":"joe","age":35}
全部取值都是可選的,若是值不存在,則直接剔除掉鍵值對scala
scala> val json = ("name" -> "joe") ~ ("age" -> Some(35)) scala> compact(render(json)) res3: String = {"name":"joe","age":35} scala> val json = ("name" -> "joe") ~ ("age" -> (None: Option[Int])) scala> compact(render(json)) res4: String = {"name":"joe"}
自定義DSL必須實現下面的隱式轉換:code
type DslConversion = T => JValue
object JsonExample extends App { import org.json4s._ import org.json4s.JsonDSL._ import org.json4s.jackson.JsonMethods._ 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, None) val json = ("lotto" -> ("lotto-id" -> lotto.id) ~ ("winning-numbers" -> lotto.winningNumbers) ~ ("draw-date" -> lotto.drawDate.map(_.toString)) ~ ("winners" -> lotto.winners.map { w => (("winner-id" -> w.id) ~ ("numbers" -> w.numbers))})) println(compact(render(json))) }
輸出結果爲:
scala> JsonExample {"lotto":{"lotto-id":5,"winning-numbers":[2,45,34,23,7,5,3],"winners": [{"winner-id":23,"numbers":[2,45,34,23,3,5]},{"winner-id":54,"numbers":[52,3,12,11,18,22]}]}}
使用pretty能夠美化輸出,注意生成的字符串中沒有draw-date字段:
scala> pretty(render(JsonExample.json)) { "lotto":{ "lotto-id":5, "winning-numbers":[2,45,34,23,7,5,3], "winners":[{ "winner-id":23, "numbers":[2,45,34,23,3,5] },{ "winner-id":54, "numbers":[52,3,12,11,18,22] }] } }
兩個JSON對象之間還能夠進行合併與比較:
scala> import org.json4s._ scala> import org.json4s.jackson.JsonMethods._ scala> val lotto1 = parse("""{ "lotto":{ "lotto-id":5, "winning-numbers":[2,45,34,23,7,5,3] "winners":[{ "winner-id":23, "numbers":[2,45,34,23,3,5] }] } }""") scala> val lotto2 = parse("""{ "lotto":{ "winners":[{ "winner-id":54, "numbers":[52,3,12,11,18,22] }] } }""") scala> val mergedLotto = lotto1 merge lotto2 scala> pretty(render(mergedLotto)) res0: String = { "lotto":{ "lotto-id":5, "winning-numbers":[2,45,34,23,7,5,3], "winners":[{ "winner-id":23, "numbers":[2,45,34,23,3,5] },{ "winner-id":54, "numbers":[52,3,12,11,18,22] }] } } scala> val Diff(changed, added, deleted) = mergedLotto diff lotto1 changed: org.json4s.JsonAST.JValue = JNothing added: org.json4s.JsonAST.JValue = JNothing deleted: org.json4s.JsonAST.JValue = JObject(List((lotto,JObject(List(JField(winners, JArray(List(JObject(List((winner-id,JInt(54)), (numbers,JArray( List(JInt(52), JInt(3), JInt(12), JInt(11), JInt(18), JInt(22))))))))))))))
JSON中的值能夠經過for生成表達式來提取。
scala> import org.json4s._ scala> import org.json4s.native.JsonMethods._ scala> val json = parse(""" { "name": "joe", "children": [ { "name": "Mary", "age": 5 }, { "name": "Mazy", "age": 3 } ] } """) scala> for { JObject(child) <- json JField("age", JInt(age)) <- child } yield age res0: List[BigInt] = List(5, 3) scala> for { JObject(child) <- json JField("name", JString(name)) <- child JField("age", JInt(age)) <- child if age > 4 } yield (name, age) res1: List[(String, BigInt)] = List((Mary,5))
JSON AST還支持像XPath同樣進行查詢:
The example json is: { "person": { "name": "Joe", "age": 35, "spouse": { "person": { "name": "Marilyn" "age": 33 } } } } Translated to DSL syntax: scala> import org.json4s._ scala> import org.json4s.native.JsonMethods._ or scala> import org.json4s.jackson.JsonMethods._ scala> import org.json4s.JsonDSL._ scala> val json = ("person" -> ("name" -> "Joe") ~ ("age" -> 35) ~ ("spouse" -> ("person" -> ("name" -> "Marilyn") ~ ("age" -> 33) ) ) ) scala> json \\ "spouse" res0: org.json4s.JsonAST.JValue = JObject(List( (person,JObject(List((name,JString(Marilyn)), (age,JInt(33))))))) scala> compact(render(res0)) res1: String = {"person":{"name":"Marilyn","age":33}} scala> compact(render(json \\ "name")) res2: String = {"name":"Joe","name":"Marilyn"} scala> compact(render((json removeField { _ == JField("name", JString("Marilyn")) }) \\ "name")) res3: String = {"name":"Joe"} scala> compact(render(json \ "person" \ "name")) res4: String = "Joe" scala> compact(render(json \ "person" \ "spouse" \ "person" \ "name")) res5: String = "Marilyn" scala> json findField { case JField("name", _) => true case _ => false } res6: Option[org.json4s.JsonAST.JValue] = Some((name,JString(Joe))) scala> json filterField { case JField("name", _) => true case _ => false } res7: List[org.json4s.JsonAST.JField] = List(JField(name,JString(Joe)), JField(name,JString(Marilyn))) scala> json transformField { case JField("name", JString(s)) => ("NAME", JString(s.toUpperCase)) } res8: org.json4s.JsonAST.JValue = JObject(List((person,JObject(List( (NAME,JString(JOE)), (age,JInt(35)), (spouse,JObject(List( (person,JObject(List((NAME,JString(MARILYN)), (age,JInt(33))))))))))))) scala> json.values res8: scala.collection.immutable.Map[String,Any] = Map(person -> Map(name -> Joe, age -> 35, spouse -> Map(person -> Map(name -> Marilyn, age -> 33))))
數組元素能夠經過索引獲取,還能夠獲取指定類型的元素:
scala> val json = parse(""" { "name": "joe", "children": [ { "name": "Mary", "age": 5 }, { "name": "Mazy", "age": 3 } ] } """) scala> (json \ "children")(0) res0: org.json4s.JsonAST.JValue = JObject(List((name,JString(Mary)), (age,JInt(5)))) scala> (json \ "children")(1) \ "name" res1: org.json4s.JsonAST.JValue = JString(Mazy) scala> json \\ classOf[JInt] res2: List[org.json4s.JsonAST.JInt#Values] = List(5, 3) scala> json \ "children" \\ classOf[JString] res3: List[org.json4s.JsonAST.JString#Values] = List(Mary, Mazy)
樣式類也能夠用來從JSON對象中提取值:
scala> import org.json4s._ scala> import org.json4s.jackson.JsonMethods._ scala> implicit val formats = DefaultFormats // Brings in default date formats etc. scala> case class Child(name: String, age: Int, birthdate: Option[java.util.Date]) scala> case class Address(street: String, city: String) scala> case class Person(name: String, address: Address, children: List[Child]) scala> val json = parse(""" { "name": "joe", "address": { "street": "Bulevard", "city": "Helsinki" }, "children": [ { "name": "Mary", "age": 5, "birthdate": "2004-09-04T18:06:22Z" }, { "name": "Mazy", "age": 3 } ] } """) scala> json.extract[Person] res0: Person = Person(joe,Address(Bulevard,Helsinki),List(Child(Mary,5,Some(Sat Sep 04 18:06:22 EEST 2004)), Child(Mazy,3,None)))
默認狀況下構造器參數必須與json的字段匹配,若是json中的字段名在scala不合法,能夠這樣解決:
使用反引號
scala> case class Person(`first-name`: String)
使用transform轉換函數
scala> case class Person(firstname: String) scala> json transformField { case ("first-name", x) => ("firstname", x) }
若是樣式類具備輔助構造函數,提取函數會盡可能匹配最佳的構造函數:
scala> case class Bike(make: String, price: Int) { def this(price: Int) = this("Trek", price) } scala> parse(""" {"price":350} """).extract[Bike] res0: Bike = Bike(Trek,350)
Scala基本類型的值能夠直接從JSON中提取:
scala> (json \ "name").extract[String] res0: String = "joe" scala> ((json \ "children")(0) \ "birthdate").extract[Date] res1: java.util.Date = Sat Sep 04 21:06:22 EEST 2004
日期格式能夠經過重寫DefaultFormats方法實現:
scala> implicit val formats = new DefaultFormats { override def dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") }
JSON對象也能夠轉換成Map[String, _],每一個字段將轉換成鍵值對:
scala> val json = parse(""" { "name": "joe", "addresses": { "address1": { "street": "Bulevard", "city": "Helsinki" }, "address2": { "street": "Soho", "city": "London" } } }""") scala> case class PersonWithAddresses(name: String, addresses: Map[String, Address]) scala> json.extract[PersonWithAddresses] res0: PersonWithAddresses("joe", Map("address1" -> Address("Bulevard", "Helsinki"), "address2" -> Address("Soho", "London")))
樣式類能夠序列化和反序列化:
scala> import org.json4s._ scala> import org.json4s.jackson.Serialization scala> import org.json4s.jackson.Serialization.{read, write} scala> implicit val formats = Serialization.formats(NoTypeHints) scala> val ser = write(Child("Mary", 5, None)) scala> read[Child](ser) res1: Child = Child(Mary,5,None)
序列化支持:
任意深度的樣式類
全部的基本類型,包括BigInt與Symbol
List,Seq,Array,Set以及Map
scala.Option
java.util.Date
Polymorphic Lists
遞歸類型
只含可序列化字段的類
自定義序列化函數