- 遇到的問題
因為要把spark從es讀出來的json數據轉換為對象,開始想用case class定義類型,通過fastjson做轉換。如下
case class Book (author: String, content: String, id: String, time: Long, title: String) val json = "{\"author\":\"hll\",\"content\":\"ES即etamsports\",\"id\":\"693\",\"time\":1490165237200,\"title\":\"百度百科\"}" val mapper: ObjectMapper = new ObjectMapper() val book: Book = mapper.readValue(json, classOf[Book])
結果拋出了異常:com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class JsonTest$Book]
換成fastjson也會有相似的異常。
恍然大悟,case class沒有空參構造函數,跟fastjson這些庫不太兼容。
- 解決辦法
然而又不想就java class,然后就找到了json4s-jackson,可以完美兼容scala的case class。
pom依賴:
<dependency> <groupId>org.json4s</groupId> <artifactId>json4s-jackson_2.10</artifactId> <version>3.2.10</version> </dependency>
使用的樣例代碼:
//隱式轉換必須要導入
import org.json4s._ import org.json4s.jackson.JsonMethods._ class Book(val author: String,val content: String,val id: String, val time: Long, val title: String) object JsonTest { def main(args: Array[String]) { val json = "{\"author\":\"hll\",\"content\":\"ES即etamsports\",\"id\":\"693\",\"time\":1490165237200,\"title\":\"百度百科\"}"
//導入隱式值 implicit val formats = DefaultFormats val book: Book = parse(json).extract[Book] println(book.content) } }
- 實際使用與思考
spark程序中的應用:
1
2
|
implicit val formats = DefaultFormats
esRDD.map(_._2).map(parse(_).extract[Book]).sortBy(_.time, false).take(10).foreach(println)
|
spark里面解析json數據有一個經典的問題,ObjectMapper對象的創建很重。一般使用mapPartition來對一個分區復用ObjectMapper對象。
我們來看一下parse方法的源碼:
private[this] lazy val _defaultMapper = {
val m = new ObjectMapper()
m.registerModule(new Json4sScalaModule)
m
}
def mapper = _defaultMapper
def parse(in: JsonInput, useBigDecimalForDouble: Boolean = false): JValue = {
mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, useBigDecimalForDouble)
in match {
case StringInput(s) => mapper.readValue(s, classOf[JValue])
case ReaderInput(rdr) => mapper.readValue(rdr, classOf[JValue])
case StreamInput(stream) => mapper.readValue(stream, classOf[JValue])
case FileInput(file) => mapper.readValue(file, classOf[JValue])
}
}
實際使用的ObjectMapper對象是lazy初始化的而且是復用的,避免了ObjectMapper對象的重復創建,很nice。