简体   繁体   English

Scala 2.10 + Json 序列化和反序列化

[英]Scala 2.10 + Json serialization and deserialization

Scala 2.10 seems to have broken some of the old libraries (at least for the time being) like Jerkson and lift-json. Scala 2.10 似乎已经破坏了一些旧库(至少目前是这样),例如 Jerkson 和lift-json。

The target usability is as follows:目标可用性如下:

case class Person(name: String, height: String, attributes: Map[String, String], friends: List[String])

//to serialize
val person = Person("Name", ....)
val json = serialize(person)

//to deserialize
val sameperson = deserialize[Person](json)

But I'm having trouble finding good existing ways of generating and deserializing Json that work with Scala 2.10.但是我无法找到生成和反序列化适用于 Scala 2.10 的 Json 的良好现有方法。

Are there best practice ways of doing this in Scala 2.10?在 Scala 2.10 中有这样做的最佳实践方法吗?

Jackson is a Java library to process JSON fast. Jackson是一个用于快速处理 JSON 的 Java 库。 The Jerkson project wraps Jackson, but appears to be abandoned.杰克森项目包装了杰克逊,但似乎被放弃了。 I've switched to Jackson's Scala Module for serialization and deserialization to native Scala data structures.我已经切换到 Jackson 的Scala 模块,用于对原生 Scala 数据结构进行序列化和反序列化。

To get it, include the following in your build.sbt :要获得它,请在build.sbt包含以下build.sbt

libraryDependencies ++= Seq(
  "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.1.3",
   ...
)

Then your examples will work verbatim with the following Jackson wrapper (I extracted it from jackson-module-scala test files):然后,您的示例将使用以下 Jackson 包装器逐字运行(我从 jackson-module-scala 测试文件中提取了它):

import java.lang.reflect.{Type, ParameterizedType}
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.`type`.TypeReference;

object JacksonWrapper {
  val mapper = new ObjectMapper()
  mapper.registerModule(DefaultScalaModule)
  
  def serialize(value: Any): String = {
    import java.io.StringWriter
    val writer = new StringWriter()
    mapper.writeValue(writer, value)
    writer.toString
  }

  def deserialize[T: Manifest](value: String) : T =
    mapper.readValue(value, typeReference[T])

  private [this] def typeReference[T: Manifest] = new TypeReference[T] {
    override def getType = typeFromManifest(manifest[T])
  }

  private [this] def typeFromManifest(m: Manifest[_]): Type = {
    if (m.typeArguments.isEmpty) { m.runtimeClass }
    else new ParameterizedType {
      def getRawType = m.runtimeClass
      def getActualTypeArguments = m.typeArguments.map(typeFromManifest).toArray
      def getOwnerType = null
    }
  }
}

Other Scala 2.10 JSON options include Twitter's scala-json based on the Programming Scala book--it's simple, at the cost of performance.其他 Scala 2.10 JSON 选项包括 Twitter 的基于 Scala 编程书籍的scala-json——它很简单,但以性能为代价。 There is also spray-json , which uses parboiled for parsing.还有Spray-json ,它使用parboiled进行解析。 Finally, Play's JSON handling looks nice, but it does not easily decouple from the Play project.最后, Play 的 JSON 处理看起来不错,但它并不容易与 Play 项目分离。

Mentioning json4s that wraps jackson, lift-json or its own native implementation as a long term solution:提到包装 jackson、lift-json 或其自己的本机实现的json4s作为长期解决方案:

I can heartily recommend argonaut for json support in scala.我可以衷心推荐argonaut在 Scala 中提供 json 支持。 All you need to configure it to serialize your Customer object is one line:配置它以序列化 Customer 对象所需的全部内容是一行:

implicit lazy val CodecCustomer: CodecJson[Customer] =
casecodec6(Customer.apply, Customer.unapply)("id","name","address","city","state","user_id")

That will pimp your class to give it an .asJson method which turns it into a string.这将拉皮条你的班级给它一个.asJson方法,将它变成一个字符串。 It will also pimp the string class to give it a method .decodeOption[List[Customer]] to parse strings.它还将对字符串类进行拉皮条,为其提供一个方法.decodeOption[List[Customer]]来解析字符串。 It handles the options in your class fine.它可以很好地处理您班级中的选项。 Here is a working class with a passing test and a running main method which you can drop into a git clone of argonaut to see it all working fine:这是一个通过测试和正在运行的 main 方法的工作类,您可以将其放入 argonaut 的 git clone 中以查看它是否正常工作:

package argonaut.example

import org.specs2.{ScalaCheck, Specification}
import argonaut.CodecJson
import argonaut.Argonaut._

case class Customer(id: Int, name: String, address: Option[String],
                    city: Option[String], state: Option[String], user_id: Int)

class CustomerExample extends Specification with ScalaCheck {

  import CustomerExample.CodecCustomer
  import CustomerExample.customers

  def is = "Stackoverflow question 12591457 example" ^
    "round trip customers to and from json strings " ! {
      customers.asJson.as[List[Customer]].toOption must beSome(customers)
    }
}

object CustomerExample {

  implicit lazy val CodecCustomer: CodecJson[Customer] =
    casecodec6(Customer.apply, Customer.unapply)("id","name","address","city","state","user_id")

  val customers = List(
    Customer(1,"one",Some("one street"),Some("one city"),Some("one state"),1)
    , Customer(2,"two",None,Some("two city"),Some("two state"),2)
    , Customer(3,"three",Some("three address"),None,Some("three state"),3)
    , Customer(4,"four",Some("four address"),Some("four city"),None,4)
  )

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

    println(s"Customers converted into json string:\n ${customers.asJson}")

    val jsonString =
      """[
        |   {"city":"one city","name":"one","state":"one state","user_id":1,"id":1,"address":"one street"}
        |   ,{"city":"two city","name":"two","state":"two state","user_id":2,"id":2}
        |   ,{"name":"three","state":"three state","user_id":3,"id":3,"address":"three address"}
        |   ,{"city":"four city","name":"four","user_id":4,"id":4,"address":"four address"}
        |]""".stripMargin


    var parsed: Option[List[Customer]] = jsonString.decodeOption[List[Customer]]

    println(s"Json string turned back into customers:\n ${parsed.get}")

  }
}

The developers are also helpful and responsive to folks getting started.开发人员也乐于助人,并且对人们的入门做出回应。

现在在https://github.com/randhindi/jerkson有一个支持 Scala 2.10 的 Jerkson 分支。

So, based on the absence of an error message and the incorrect sample code, I'm suspecting this is more of an issue of just not understanding how the lift-json extraction works.因此,基于没有错误消息和不正确的示例代码,我怀疑这更多是一个不了解 Lift-json 提取如何工作的问题。 If I've misunderstood, do comment and let me know.如果我误解了,请发表评论并让我知道。 So, if I'm right then here's what you need.所以,如果我是对的,那么这就是你需要的。

To serialize:序列化:

import net.liftweb.json._
  import Extraction._

implicit val formats = DefaultFormats

case class Person(...)
val person = Person(...)
val personJson = decompose(person) // Results in a JValue

Then to reverse the process you'd do something like:然后要反转该过程,您可以执行以下操作:

// Person Json is a JValue here.
personJson.extract[Person]

If that's not the part you're having trouble with, then do let me know and I can try to revise my answer to be more helpful.如果这不是您遇到问题的部分,请告诉我,我可以尝试修改我的答案以提供更多帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM