2017-07-15
概述
json 分为好几种形态.
- 字符串形态, 用于数据交换和描述存储的原始形式.
- Json对象形态, 这是Json引擎内在逻辑,树形结构,抽象语法树AST
- 模型对象形态, 这是用户业务对象
在实际编码中这三种形态经常相互转化.
由于官方文档的示例都非常简单, 所以遇到复杂的结构出了问题很难处理.
本文采用akka-http自带spray json和json4s的json分别处理.
用g8生成模板
zhouhh@/Users/zhouhh/git $ sbt new akka/akka-http-scala-seed.g8
name [My Akka HTTP Project]: TestJsonConvert
scala_version [2.12.2]:
akka_http_version [10.0.9]:
akka_version [2.5.3]:
Template applied in ./testjsonconvert
zhouhh@/Users/zhouhh/git/testjsonconvert $ find .
.
./build.sbt
./project
./project/build.properties
./project/plugins.sbt
./src
./src/main
./src/main/scala
./src/main/scala/com/example
./src/main/scala/com/example/routes
./src/main/scala/com/example/routes/BaseRoutes.scala
./src/main/scala/com/example/routes/SimpleRoutes.scala
./src/main/scala/com/example/WebServer.scala
./src/main/scala/com/example/WebServerHttpApp.scala
./src/test
./src/test/scala
./src/test/scala/com
./src/test/scala/com/example
./src/test/scala/com/example/routes
./src/test/scala/com/example/routes/BaseRoutesSpec.scala
./src/test/scala/com/example/routes/SimpleRoutesSpec.scala
./src/test/scala/com/example/WebServerHttpAppSpec.scala
build.sbt
zhouhh@/Users/zhouhh/git/testjsonconvert $ cat build.sbt
lazy val akkaHttpVersion = "10.0.9"
lazy val akkaVersion = "2.5.3"
lazy val root = (project in file(".")).
settings(
inThisBuild(List(
organization := "com.example",
scalaVersion := "2.12.2"
)),
name := "TestJsonConvert",
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-http" % akkaHttpVersion,
"com.typesafe.akka" %% "akka-http-xml" % akkaHttpVersion,
"com.typesafe.akka" %% "akka-stream" % akkaVersion,
"com.typesafe.akka" %% "akka-http-spray-json" % "10.0.9",
"org.json4s" % "json4s-jackson_2.12" % "3.5.2",
"com.typesafe.akka" %% "akka-http-testkit" % akkaHttpVersion % Test,
"org.scalatest" %% "scalatest" % "3.0.1" % Test
)
)
Spray Json 转换
Json转换代码
zhouhh@/Users/zhouhh/git/testjsonconvert/src/main/scala/com/example $ vi TestSprayJsonConvert.scala
package com.example
import spray.json._
case class Color( name: String, red: Double, green: Int, blue: Int )
case class Colors( colors: List[Color] )
case class Paint( name: String, colors: Colors )
object MyJsonProtocol extends DefaultJsonProtocol {
implicit val colorFormat = jsonFormat4( Color ) //4个属性
implicit val colorsFormat = jsonFormat1( Colors ) //1个属性
implicit val paintFormat = jsonFormat2( Paint ) //2个属性
}
/**
* Author: zhouhh
* Date: 2017.7.15
* 抽象语法树结构(Abstract Syntax Tree (AST)), 是Json对象树, 区别于json字符串和模型对象
* 该代码演示三者之间互转. 但引入复杂对象,浮点和集合
*
*/
object TestSprayJsonConvert {
import MyJsonProtocol._
def main( args: Array[String] ): Unit = {
//object to jsonAst, 军校蓝, 此处故意调整值为浮点
val json = Color( "CadetBlue", 95.2, 158, 160 ).toJson
println( json ) //{"name":"CadetBlue","red":95,"green":158,"blue":160}
//jsonAst to object
val color = json.convertTo[Color]
println( "name:" + color.name + ",red:" + color.red + ",green:" + color.green + ",blue:" + color.blue ) //name:CadetBlue,red:95,green:158,blue:160
val jsonsListStr = "[{\"name\":\"CadetBlue\",\"red\":95.3,\"green\":158,\"blue\":160},{\"name\":\"CadetRed\",\"red\":160.5,\"green\":158,\"blue\":95}]"
//另一种更直观的表示
val jsons = """{"colors":[{"name":"CadetBlue","red":95.3,"green":158,"blue":160},{"name":"CadetRed","red":160.5,"green":158,"blue":95}]}"""
//string -> jsonAst(JsValue) -> object
val colorsObj: Colors = jsons.parseJson.convertTo[Colors]
print( colorsObj )
val colorsList: List[Color] = jsonsListStr.parseJson.convertTo( DefaultJsonProtocol.listFormat[Color] )
//此处故意将colorsObj命名有区别,否则出现两个colors, 后者是前者的成员.
colorsObj.colors.foreach { color =>
println( "name:" + color.name + ",red:" + color.red + ",green:" + color.green + ",blue:" + color.blue )
}
//do the same thing but directly var List
colorsList.foreach { color =>
println( "name:" + color.name + ",red:" + color.red + ",green:" + color.green + ",blue:" + color.blue )
}
//object -> json
// val listjson = colors.colors.toArray.toJson
val listjson: JsValue = colorsObj.toJson
println( listjson )
//复杂结构, json字符串,对象互转.
val paintJsonStr = """ {"name":"mypaint","colors":{"colors":[{"name":"CadetBlue","red":95.3,"green":158,"blue":160},{"name":"CadetRed","red":160.5,"green":158,"blue":95}]}}"""
val paintAST = paintJsonStr.parseJson
val paint: Paint = paintAST.convertTo[Paint]
val paintJsonStrTo = paint.toJson
println( paintAST )
println( paint )
println( paintJsonStrTo )
}
}
运行测试
zhouhh@/Users/zhouhh/git/testjsonconvert $ sbt
> run
[info] Formatting 1 Scala source {file:/Users/zhouhh/git/testjsonconvert/}root(compile) ...
[info] Reformatted 1 Scala source {file:/Users/zhouhh/git/testjsonconvert/}root(compile).
[info] Compiling 1 Scala source to /Users/zhouhh/git/testjsonconvert/target/scala-2.12/classes...
[warn] Multiple main classes detected. Run 'show discoveredMainClasses' to see the list
Multiple main classes detected, select one to run:
[1] com.example.TestSprayJsonConvert
[2] com.example.WebServer
[3] com.example.WebServerHttpApp
Enter number: 1
[info] Running com.example.TestSprayJsonConvert
{"name":"CadetBlue","red":95.2,"green":158,"blue":160}
name:CadetBlue,red:95.2,green:158,blue:160
Colors(List(Color(CadetBlue,95.3,158,160), Color(CadetRed,160.5,158,95)))name:CadetBlue,red:95.3,green:158,blue:160
name:CadetRed,red:160.5,green:158,blue:95
name:CadetBlue,red:95.3,green:158,blue:160
name:CadetRed,red:160.5,green:158,blue:95
{"colors":[{"name":"CadetBlue","red":95.3,"green":158,"blue":160},{"name":"CadetRed","red":160.5,"green":158,"blue":95}]}
{"name":"mypaint","colors":{"colors":[{"name":"CadetBlue","red":95.3,"green":158,"blue":160},{"name":"CadetRed","red":160.5,"green":158,"blue":95}]}}
Paint(mypaint,Colors(List(Color(CadetBlue,95.3,158,160), Color(CadetRed,160.5,158,95))))
{"name":"mypaint","colors":{"colors":[{"name":"CadetBlue","red":95.3,"green":158,"blue":160},{"name":"CadetRed","red":160.5,"green":158,"blue":95}]}}
[success] Total time: 12 s, completed 2017-7-15 9:07:53
json4s转json
zhouhh@/Users/zhouhh/git/testjsonconvert $ vi src/main/scala/com/example/TestJson4s.scala
cat: zhouhh@/Users/zhouhh/git/testjsonconvert: No such file or directory
cat: $: No such file or directory
cat: vi: No such file or directory
package com.example
import org.json4s._
import org.json4s.jackson.JsonMethods._
import org.json4s.JsonDSL._
/**
* 测试json4s.jackson
*/
object TestJson4s {
case class Winner( id: Long, numbers: List[Int] )
case class Lotto( id: Long, winningNumbers: List[Int], winners: List[Winner], drawDate: Option[java.util.Date] )
def mprint( json: String ): Unit = {
print( json )
}
def genJson(): String = {
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 ) )
} ) )
val jsonstr = compact( render( json ) )
println( compact( jsonstr ) )
jsonstr
}
def main( args: Array[String] ) {
mprint( genJson() )
}
}
"org.json4s" % "json4s-jackson_2.11" % "3.5.2",
执行
> run
Multiple main classes detected, select one to run:
[1] com.example.TestJson4s
[2] com.example.TestSprayJsonConvert
[3] com.example.WebServer
[4] com.example.WebServerHttpApp
Enter number: 1
[info] Running com.example.TestJson4s
"{\"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]}]}}"
{"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]}]}}[success] Total time: 28 s, completed 2017-7-15 10:26:50
>
参考
如非注明转载, 均为原创. 本站遵循知识共享CC协议,转载请注明来源