欢迎关注大数据技术架构与案例微信公众号:过往记忆大数据
过往记忆博客公众号iteblog_hadoop
欢迎关注微信公众号:
过往记忆大数据

在 json4s 中自定义 CustomSerializer

到目前为止,Scala 环境下至少存在6种 Json 解析的类库,这里面不包括 Java 语言实现的 Json 类库。所有这些库都有一个非常相似的抽象语法树(AST)。而 json4s 项目旨在提供一个单一的 AST 树供其他 Scala 类库来使用。

在 json4s 中自定义 CustomSerializer
如果想及时了解Spark、Hadoop或者HBase相关的文章,欢迎关注微信公众号:iteblog_hadoop

json4s 的使用非常的简单,它可以将类直接转换成 json 格式输出,也支持将 json 格式的数据转换成 class 对象。对于 Scala 和 Java 常见的类型(比如String、Int、java.lang.Integer、java.lang.Long、java.lang.Boolean 等)都提供了响应的转换函数。比如下面我们直接将一个类转换成 json:

import org.json4s.JsonAST.{JNull, JString}
import org.json4s.{DefaultFormats, Extraction, Formats}
import org.json4s.jackson.JsonMethods.render
import org.json4s.jackson.JsonMethods.pretty

case class Person(name: String, age: Int)
implicit val formats: Formats = DefaultFormats

val person = Person("iteblog", 110)
val jvalue = Extraction.decompose(person)

println(pretty(render(jvalue)))

输出
{
  "name" : "iteblog",
  "age" : 110
}

同时我们也可以直接将一个 json 对象转换成类对象:

val person =  Extraction.extract[Person](jvalue)
println(person)

输出
Person(iteblog,110)

我们可以看到上面的例子使用起来都很简单的。但是如果碰到元素的类型在 json4s 中没有事先定义,结果会怎么样呢?比如在 json4s 中并没有定义对 java.sql.Date 类型的解析,那如果我们用到了这个类型,会出现什么问题呢?具体如下:

case class Person(name: String, age: Int, birthday: Date)
implicit val formats: Formats = DefaultFormats

val person = Person("iteblog", 110, Date.valueOf("2019-07-01"))
val jvalue = Extraction.decompose(person)

println(pretty(render(jvalue)))

输出

{
  "name" : "iteblog",
  "age" : 110,
  "birthday" : { }
}

可以从上面的结果看出,json4s 并没有正确的解析出 birthday 字段的值。 如果我们将 json 解析到类中,会出现什么问题呢?

val jvalue1 = parse("""{"name" : "iteblog", "age" : 110, "birthday" : "2019-07-01"}""")
val person = Extraction.extract[Person](jvalue1)
println(person)

结果

Exception in thread "main" org.json4s.package$MappingException: No usable value for birthday
Parsed JSON values do not match with class constructor
args=
arg types=
executable=Executable(Constructor(public java.sql.Date(int,int,int)))
cause=wrong number of arguments
types comparison result=MISSING(int),MISSING(int),MISSING(int)
	at org.json4s.reflect.package$.fail(package.scala:95)
	at org.json4s.Extraction$ClassInstanceBuilder.org$json4s$Extraction$ClassInstanceBuilder$$buildCtorArg(Extraction.scala:526)
	at org.json4s.Extraction$ClassInstanceBuilder$$anonfun$15.apply(Extraction.scala:546)
	at org.json4s.Extraction$ClassInstanceBuilder$$anonfun$15.apply(Extraction.scala:546)
	at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
	at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
	at scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:59)
	at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48)
	at scala.collection.TraversableLike$class.map(TraversableLike.scala:234)
	at scala.collection.AbstractTraversable.map(Traversable.scala:104)
	at org.json4s.Extraction$ClassInstanceBuilder.instantiate(Extraction.scala:546)
	at org.json4s.Extraction$ClassInstanceBuilder.result(Extraction.scala:597)
	at org.json4s.Extraction$$anonfun$extract$6.apply(Extraction.scala:400)
	at org.json4s.Extraction$$anonfun$extract$6.apply(Extraction.scala:392)
	at org.json4s.Extraction$.customOrElse(Extraction.scala:606)
	at org.json4s.Extraction$.extract(Extraction.scala:392)
	at org.json4s.Extraction$.extract(Extraction.scala:39)
	at com.iteblog.Test$.main(Test.scala:32)
	at com.iteblog.Test.main(Test.scala)
Caused by: org.json4s.package$MappingException: Parsed JSON values do not match with class constructor
args=
arg types=
executable=Executable(Constructor(public java.sql.Date(int,int,int)))
cause=wrong number of arguments
types comparison result=MISSING(int),MISSING(int),MISSING(int)
	at org.json4s.reflect.package$.fail(package.scala:95)
	at org.json4s.Extraction$ClassInstanceBuilder.instantiate(Extraction.scala:573)
	at org.json4s.Extraction$ClassInstanceBuilder.result(Extraction.scala:597)
	at org.json4s.Extraction$$anonfun$extract$6.apply(Extraction.scala:400)
	at org.json4s.Extraction$$anonfun$extract$6.apply(Extraction.scala:392)
	at org.json4s.Extraction$.customOrElse(Extraction.scala:606)
	at org.json4s.Extraction$.extract(Extraction.scala:392)
	at org.json4s.Extraction$ClassInstanceBuilder.org$json4s$Extraction$ClassInstanceBuilder$$buildCtorArg(Extraction.scala:514)
	... 17 more

可以看出,出现无法解析出 birthday 字段,因为 json4s 并没有提供将字符串解析到 java.sql.Date。那怎么办呢?json4s 为我们提供了自定义解析类型的方法,那就是 CustomSerializer,我们只需要继承这个类,并实现对自定义类型的序列化和反序列化的方法即可。那对我们的例子可以实现如下:

package com.iteblog

import java.sql.Date

import org.json4s.JsonAST.{JNull, JString}
import org.json4s.{CustomSerializer, DefaultFormats, Extraction, Formats}
import org.json4s.jackson.JsonMethods.render
import org.json4s.jackson.JsonMethods.pretty
import org.json4s.jackson.JsonMethods.parse

object Iteblog {

  case class Person(name: String, age: Int, birthday: Date)

  case object DateSerializer extends CustomSerializer[Date](_ => ( {
    case JString(s) => Date.valueOf(s)
    case JNull => null
  }, {
    case d: Date => JString(d.toString)
  }))

  def main(args: Array[String]): Unit = {

    implicit val formats: Formats = DefaultFormats + DateSerializer

    val person = Person("iteblog", 110, Date.valueOf("2019-07-01"))
    val jvalue = Extraction.decompose(person)

    println(pretty(render(jvalue)))

    val jvalue1 = parse("""{"name" : "iteblog", "age" : 110, "birthday" : "2019-07-01"}""")
    val r = Extraction.extract[Person](jvalue1)
    println(r)


  }
}

输出

{
  "name" : "iteblog",
  "age" : 110,
  "birthday" : "2019-07-01"
}
Person(iteblog,110,2019-07-01)

可见,通过自定义的 DateSerializer 我们可以解析 java.sql.Date 类型了。

本博客文章除特别声明,全部都是原创!
原创文章版权归过往记忆大数据(过往记忆)所有,未经许可不得转载。
本文链接: 【在 json4s 中自定义 CustomSerializer】(https://www.iteblog.com/archives/2451.html)
喜欢 (4)
分享 (0)
发表我的评论
取消评论

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