欢迎关注Hadoop、Spark、Flink、Hive、Hbase、Flume等大数据资料分享微信公共账号:iteblog_hadoop
  1. 文章总数:978
  2. 浏览总数:11,978,673
  3. 评论:3938
  4. 分类目录:106 个
  5. 注册用户数:6129
  6. 最后更新:2018年12月15日
过往记忆博客公众号iteblog_hadoop
欢迎关注微信公众号:
iteblog_hadoop
大数据技术博客公众号bigdata_ai
大数据猿:
bigdata_ai

play-json处理空值的几种方法

假设我们有个需求,需要解析文件里面的Json数据,我们的Json数据如下:

{"website": "www.iteblog.com", "email": "hadoop@iteblog.com"}

我们使用play-json来解析,首先我们引入相关依赖:

<dependency>
    <groupId>com.typesafe.play</groupId>
    <artifactId>play-json_2.10</artifactId>
    <version>2.4.0-M1</version>
</dependency>

然后我们会定义一个类来存储解析好的数据,方便后面的操作,如下:

case class BlogInfo(website: String, email: String)
play-json formatNullable
如果想及时了解Spark、Hadoop或者Hbase相关的文章,欢迎关注微信公共帐号:iteblog_hadoop

现在我们可以使用下面代码来解析上面的Json数据,并存储到 BlogInfo 类中。每行Json解析后都用 BlogInfo 类表示:

import play.api.libs.functional.syntax._
import play.api.libs.json._

val jsonInfo = """{"website": "www.iteblog.com", "email" : "hadoop@iteblog.com"}"""

implicit val WebSiteFormat: Reads[BlogInfo] = (
  (JsPath \ "website").read[String] and
    (JsPath \ "email").read[String]
  ) (BlogInfo)

println(Json.parse(jsonInfo).as[BlogInfo])
//BlogInfo(www.iteblog.com,hadoop@iteblog.com)

我们运行上面的代码,完美地解析了Json数据。但是现实并不是一帆风顺的,我们的Json数据可能有些字段没有,比如文件里面有一行如下格式的数据:

{"website": "www.iteblog.com"}

没有email字段,如果这时候你运行上面的代码会得到下面的异常:

Exception in thread "main" play.api.libs.json.JsResultException:
 JsResultException(errors:List((/email,List(ValidationError(error.path.missing,WrappedArray())))))
  at play.api.libs.json.JsValue$$anonfun$2.apply(JsValue.scala:67)
  at play.api.libs.json.JsValue$$anonfun$2.apply(JsValue.scala:67)
  at play.api.libs.json.JsResult$class.fold(JsResult.scala:77)
  at play.api.libs.json.JsError.fold(JsResult.scala:13)
  at play.api.libs.json.JsValue$class.as(JsValue.scala:65)
  at play.api.libs.json.JsObject.as(JsValue.scala:166)
  at com.iteblog.JsonTest$.main(Test.scala:22)
  at com.iteblog.JsonTest.main(Test.scala)

这是因为我们解析Json的时候期待 /email 路径,但是我们的Json里面没有,所有会抛出异常。那如何处理呢?本文提供了以下几种解决方案。

使用readNullable解决

既然 email 字段可能不存在,那么我们将 BlogInfo 类中 email 字段设置成 Option[String] 不就可以解决这个问题吗?如下:

case class BlogInfo(website: String, email: Option[String])

这时候我们的WebSiteFormat也需要修改了:

implicit val WebSiteFormat: Reads[BlogInfo] = (
  (JsPath \ "website").read[String] and
    (JsPath \ "email").readNullable[String]
  ) (BlogInfo)

val jsonInfo = """{"website": "www.iteblog.com", "email" : "hadoop@iteblog.com"}"""
println(Json.parse(jsonInfo).as[BlogInfo])

val jsonInfo2 = """{"website": "www.iteblog.com"}"""
println(Json.parse(jsonInfo2).as[BlogInfo])

Output:
BlogInfo(www.iteblog.com,Some(hadoop@iteblog.com))
BlogInfo(www.iteblog.com,None)

现在这个程序完美解决 Json 数据了,不管Json数据里面是否存在 email 字段。如果存在的话返回 Some(xxx);如果不存在,直接返回 None

使用formatNullable

除了使用 readNullable 函数,我们还可以使用 formatNullable 函数,而且 formatNullable 函数功能更强大,我们当需要的字段不存在,我们可以设置默认值。而且我们还可以存成 Option[Type] 或者直接是 Type 类型的。,使用如下:

/////////////////////////////////////////////////////////////////////
 User: 过往记忆
 Date: 2017年08月02日 
 Time: 21:57:32
 bolg: https://www.iteblog.com
 本文地址:https://www.iteblog.com/archives/2222
 过往记忆博客,专注于hadoop、hive、spark、shark、flume的技术博客,大量的干货
 过往记忆博客微信公共帐号:iteblog_hadoop
/////////////////////////////////////////////////////////////////////

case class BlogInfo(website: String, email: Option[String])

implicit val WebSiteFormat: Reads[BlogInfo] = (
  (JsPath \ "website").read[String] and
    (JsPath \ "email").formatNullable[String].inmap[Option[String]](a => Some(a.getOrElse("No Email")), b => b)
  ) (BlogInfo)

val jsonInfo = """{"website": "www.iteblog.com", "email" : "hadoop@iteblog.com"}"""
println(Json.parse(jsonInfo).as[BlogInfo])

val jsonInfo2 = """{"website": "www.iteblog.com"}"""
println(Json.parse(jsonInfo2).as[BlogInfo])

Output:
BlogInfo(www.iteblog.com,Some(hadoop@iteblog.com))
BlogInfo(www.iteblog.com,Some(No Email))

#####################################################

case class BlogInfo(website: String, email: String)

implicit val WebSiteFormat: Reads[BlogInfo] = (
  (JsPath \ "website").read[String] and
    (JsPath \ "email").formatNullable[String].inmap[String](_.getOrElse("No Email!"), Some(_))
  ) (BlogInfo)

val jsonInfo = """{"website": "www.iteblog.com", "email" : "hadoop@iteblog.com"}"""
println(Json.parse(jsonInfo).as[BlogInfo])

val jsonInfo2 = """{"website": "www.iteblog.com"}"""
println(Json.parse(jsonInfo2).as[BlogInfo])

Output:
BlogInfo(www.iteblog.com,hadoop@iteblog.com)
BlogInfo(www.iteblog.com,No Email!)

使用Reads.pure

另外一种办法是使用 Reads.pure,当 read 失败的时候会使用 Reads.pure 函数,我们可以通过它指定一个默认值,具体使用如下:

case class BlogInfo(website: String, email: String)

implicit val WebSiteFormat: Reads[BlogInfo] = (
  (JsPath \ "website").read[String] and
    ((JsPath \ "email").read[String] or Reads.pure("No Email!"))
  ) (BlogInfo)

val jsonInfo = """{"website": "www.iteblog.com", "email" : "hadoop@iteblog.com"}"""
println(Json.parse(jsonInfo).as[BlogInfo])

val jsonInfo2 = """{"website": "www.iteblog.com"}"""
println(Json.parse(jsonInfo2).as[BlogInfo])

Output:
BlogInfo(www.iteblog.com,hadoop@iteblog.com)
BlogInfo(www.iteblog.com,No Email!)

从上面看出,Play 的 Json 库提供的功能真是强大,可以解决很多麻烦的问题,更多关于 play-json 的使用请参见官方文档了,这里就不再介绍了。

本博客文章除特别声明,全部都是原创!
转载本文请加上:转载自过往记忆(https://www.iteblog.com/)
本文链接: 【play-json处理空值的几种方法】(https://www.iteblog.com/archives/2222.html)
喜欢 (13)
分享 (0)
发表我的评论
取消评论

表情
本博客评论系统带有自动识别垃圾评论功能,请写一些有意义的评论,谢谢!