最近在一个项目中使用到Writes即可;而将Json字符串转换成一个类直接写个Reads即可。所有的操作只需要引入play.api.libs.json._即可。
但昨天在将List[(String, String)]类型的对象转换成Json遇到了一点麻烦。如果是List[String]我们可以这么写: val list = List("www", "iteblog", "com")
println(Json.toJson(list).toString())
["www","iteblog","com"]
可以直接转换成一个Json数组,如果List[(String, String)]也那么写行不行呢? val info = List(("web_site", ""), ("weixin", "iteblog_hadoop"))
println(Json.toJson(info))
上面代码还没运行,在编译的时候就出现问题了(这个很不错,错误出现越早越好!): Error:(15, 24) No Json serializer found for type List[(String, String)]. Try to implement an implicit Writes or Format for this type.
println(Json.toJson(info))
^
上面的错误大概的意思就是Play Json框架找不到如何将List[(String, String)]类型的对象序列化成Json,需要我们指定一个Writes或者Format告诉Play Json框架如何将它序列化成Json。我们先来看看Json.toJson方法的原型: def toJson[T](o: T)(implicit tjs: Writes[T]): JsValue
这个方法其实接受两个参数,其中Writes是通过隐式传入的。有些人可能会问:那为什么上面将List[String]转换成Json却不要写Writes呢?原因很简单,因为对于这些基本的类型,Play Json框架已经内置了这些Writes: /**
* Serializer for Int types.
*/
implicit object IntWrites extends Writes[Int] {
def writes(o: Int) = JsNumber(o)
}
/**
* Serializer for Short types.
*/
implicit object ShortWrites extends Writes[Short] {
def writes(o: Short) = JsNumber(o)
}
/**
* Serializer for Long types.
*/
implicit object LongWrites extends Writes[Long] {
def writes(o: Long) = JsNumber(o)
}
/**
* Serializer for Float types.
*/
implicit object FloatWrites extends Writes[Float] {
def writes(o: Float) = JsNumber(o)
}
/**
* Serializer for Double types.
*/
implicit object DoubleWrites extends Writes[Double] {
def writes(o: Double) = JsNumber(o)
}
/**
* Serializer for BigDecimal types.
*/
implicit object BigDecimalWrites extends Writes[BigDecimal] {
def writes(o: BigDecimal) = JsNumber(o)
}
/**
* Serializer for Boolean types.
*/
implicit object BooleanWrites extends Writes[Boolean] {
def writes(o: Boolean) = JsBoolean(o)
}
/**
* Serializer for String types.
*/
implicit object StringWrites extends Writes[String] {
def writes(o: String) = JsString(o)
}
/**
* Serializer for Jackson JsonNode
*/
implicit object JsonNodeWrites extends Writes[JsonNode] {
def writes(o: JsonNode): JsValue = JacksonJson.jsonNodeToJsValue(o)
}
/**
* Serializer for Array[T] types.
*/
implicit def arrayWrites[T: ClassTag](implicit fmt: Writes[T]): Writes[Array[T]] = new Writes[Array[T]] {
def writes(ts: Array[T]) = JsArray((ts.map(t => toJson(t)(fmt))).toList)
}
可以从上面看出,基本类型转换已经内置了,所以我们根本不需要自己定义Writes。但是List[(String,String)]并不是内置的类型,所以我们需要自己定义一个Writes。根据上面内置的Writes启发,我们可以这么定义一个(String,String)类型的Writes如下: implicit object iteblogWrites extends Writes[(String, String)] {
def writes(o: (String, String)) = Json.obj(o._1 -> o._2)
}
然后我们再将上面List[(String,String)]对象转换成Json: val info = List(("web_site", ""), ("weixin", "iteblog_hadoop"))
println(Json.toJson(info))
[{"web_site":""},{"weixin":"iteblog_hadoop"}]
可以看出,经过自定义iteblogWrites已经告诉Play Json框架如何系列化List[(String,String)]类型的对象了!如果List[(String,String)]定义在一个case class中又该如何弄,如下: case class IteblogInfo(blog: String, blogInfo: List[(String, String)])
val iteblogInfo = IteblogInfo("iteblog", info)
println(Json.toJson(iteblogInfo))
运行上面代码也会出错,我们需要告诉Play Json框架如何系列化IteblogInfo类型成Json,如下: implicit val IteblogInfoWrites: Writes[IteblogInfo] = (
(JsPath \ "blog").write[String] and
(JsPath \ "blogInfo").write[List[(String,String)]]
) (unlift(IteblogInfo.unapply))
这样运行上面的代码得到如下的结果: {"blog":"iteblog","blogInfo":[{"web_site":""},{"weixin":"iteblog_hadoop"}]}
当然,定义Writes的方法有很多,上面的例子我们还可以这么定义一个Writes,如下: implicit val IteblogInfoWrites: Writes[IteblogInfo] = (
(JsPath \ "iteblog").write[String] and
(JsPath \ "blogInfo").write[JsArray]
.contramap[List[(String, String)]](list => JsArray(list.map(g => Json.obj(g._1 -> g._2))))
) (unlift(IteblogInfo.unapply))
这样也可以得到正确的结果。在使用Play Json框架发现这个框架真是太方便了,太强大了。。