如何在Scala / Lift中构建和解析JSON字符串

55

我想使用JSON在浏览器和我的应用程序之间传输数据。

我尝试使用Lift 1.0来创建和解析JSON字符串,但是不知何故,我无法解析刚刚构造的JSON:

scala>import scala.util.parsing.json.JSON._
import scala.util.parsing.json.JSON._

scala> import net.liftweb.http.js._
import net.liftweb.http.js._

scala> import net.liftweb.http.js.JE._
import net.liftweb.http.js.JE._

scala> val json = JsObj(("foo", 4), ("bar", "baz")).toJsCmd
json: String = {'foo': 4, 'bar': 'baz'}

scala>  parseFull(json)  
res3: Option[Any] = None

我该如何在Scala/Lift中以编程方式构建一个有效的JSON消息,并能够再次解析?

2个回答

90
你正在使用Lift 1.0的JsCmd,它生成带有单引号的JSON,并尝试使用scala的解析器对其进行解析,而scala的解析器仅支持双引号字符串。重要的是要意识到JSON有多个定义。单引号字符串在JSON中有效吗?根据ECMAScript 5th Ed,它们是有效的。但是根据Crockford最初的RFC 4627,它们无效。Lift和Scala提供了许多解析JSON的方法,有时版本之间的行为会有所不同。这些解析器接受的字符串并不相等。以下是一些关于生成和解析JSON字符串的各种方法的注释和示例。

使用lift-json库DSL生成JSON

  • 推荐
  • 尽管名称如此,但这是一个独立的项目,与Lift的其他部分无关

示例:

scala> import net.liftweb.json.JsonAST
import net.liftweb.json.JsonAST

scala> import net.liftweb.json.JsonDSL._
import net.liftweb.json.JsonDSL._

scala> import net.liftweb.json.Printer._
import net.liftweb.json.Printer._

scala> val json1 = ("foo" -> 4) ~ ("bar" -> "baz")
json1: net.liftweb.json.JsonAST.JObject = JObject(List(JField(foo,JInt(4)), JField(bar,JString(baz))))

scala> compact(JsonAST.render(json1))
res0: String = {"foo":4,"bar":"baz"}

scala> val json2 = List(1,2,3)
json2: List[Int] = List(1, 2, 3)

scala> compact(JsonAST.render(json2))
res1: String = [1,2,3]

scala> val json3 = ("foo", 4) ~ ("bar", List(1,2,3))
json3: net.liftweb.json.JsonAST.JObject = JObject(List(JField(foo,JInt(4)), JField(bar,JArray(List(JInt(1), JInt(2), JInt(3))))))

scala> compact(JsonAST.render(json3))
res2: String = {"foo":4,"bar":[1,2,3]}

使用lift-json库解析JSON

  • 推荐使用
  • 提供scala case类的隐式映射
  • 目前不支持在控制台定义的case类(会抛出com.thoughtworks.paranamer.ParameterNamesNotFoundException: Unable to get class bytes异常)
  • 下面的示例使用预定义的scala case类PublicID,以便可以在scala控制台上运行。

示例:

scala> import scala.xml.dtd.PublicID
import scala.xml.dtd.PublicID

scala> import net.liftweb.json._
import net.liftweb.json._

scala> import net.liftweb.json.JsonAST._
import net.liftweb.json.JsonAST._

scala> import net.liftweb.json.JsonDSL._
import net.liftweb.json.JsonDSL._

scala> implicit val formats = DefaultFormats 
formats: net.liftweb.json.DefaultFormats.type = net.liftweb.json.DefaultFormats$@7fa27edd

scala> val jsonAst = ("publicId1" -> "idString") ~ ("systemId" -> "systemIdStr")
jsonAst: net.liftweb.json.JsonAST.JObject = JObject(List(JField(publicId,JString(idString)), JField(systemId,JString(systemIdStr))))

scala> jsonAst.extract[PublicID]
res0: scala.xml.dtd.PublicID = PUBLIC "idString" "systemIdStr"

在scala 2.7.7和2.8.1中解析JSON

示例:

scala>import scala.util.parsing.json.JSON._
import scala.util.parsing.json.JSON._

scala>  parseFull("{\"foo\" : 4 }")        
res1: Option[Any] = Some(Map(foo -> 4.0))

scala> parseFull("[ 1,2,3 ]")
res2: Option[Any] = Some(List(1.0, 2.0, 3.0))

scala>  parseFull("{'foo' : 4 }")  
res3: Option[Any] = None

在Lift 2.0和2.2中使用util.JSONParser解析JSON

  • 中立建议
  • Lift的util.JSONParser可以解析单引号或双引号的JSON字符串:

示例:

scala> import net.liftweb.util.JSONParser._
import net.liftweb.util.JSONParser._

scala> parse("{\"foo\" : 4 }")    
res1: net.liftweb.common.Box[Any] = Full(Map(foo -> 4.0))

scala> parse("[ 1,2,3 ]")
res2: net.liftweb.common.Box[Any] = Full(List(1.0, 2.0, 3.0))

scala> parse("{'foo' : 4}")           
res3: net.liftweb.common.Box[Any] = Full(Map(foo -> 4.0))

使用json.JsonParser在Lift 2.0和2.2中解析JSON

  • 中立建议
  • Lift的json.JsonParser无法解析单引号的JSON字符串:

例如:

scala> import net.liftweb.json._
import net.liftweb.json._

scala> import net.liftweb.json.JsonParser._
import net.liftweb.json.JsonParser._

scala> parse("{\"foo\" : 4 }")
res1: net.liftweb.json.JsonAST.JValue = JObject(List(JField(foo,JInt(4))))

scala> parse("[ 1,2,3 ]")
res2: net.liftweb.json.JsonAST.JValue = JArray(List(JInt(1), JInt(2), JInt(3)))

scala> parse("{'foo' : 4}")    
net.liftweb.json.JsonParser$ParseException: unknown token '
Near: {'foo' : 4}
    at net.liftweb.json.JsonParser$Parser.fail(JsonParser.scala:216)
    at net.liftweb.json.JsonParser$Parser.nextToken(JsonParser.scala:308)
    at net.liftweb.json.JsonParser$$anonfun$1.apply(JsonParser.scala:172)
    at net.liftweb.json.JsonParser$$anonfun$1.apply(JsonParser.scala:129)
    at net.liftweb.json.JsonParse...

使用Lift 1.0 JsCmd生成JSON

  • 不推荐-输出结果并非所有JSON解析器都支持
  • 请注意字符串周围的单引号:

示例:

scala> import net.liftweb.http.js._
import net.liftweb.http.js._

scala> import net.liftweb.http.js.JE._
import net.liftweb.http.js.JE._

scala> JsObj(("foo", 4), ("bar", "baz")).toJsCmd
res0: String = {'foo': 4, 'bar': 'baz'}

scala> JsArray(1,2,3).toJsCmd
res1: String = 
[1, 2, 3]

scala>  JsObj(("foo", 4), ("bar", JsArray(1,2,3))).toJsCmd
res2: String = 
{'foo': 4, 'bar': [1, 2, 3]
}

使用Lift 2.0 JsCmd生成JSON

  • 中立建议
  • 注意字符串周围的双引号:

示例:

scala> import net.liftweb.http.js._
import net.liftweb.http.js._

scala> import net.liftweb.http.js.JE._
import net.liftweb.http.js.JE._

scala> JsObj(("foo", 4), ("bar", "baz")).toJsCmd
res0: String = {"foo": 4, "bar": "baz"}

scala> JsArray(1,2,3).toJsCmd
res1: String = 
[1, 2, 3]

scala> JsObj(("foo", 4), ("bar", JsArray(1,2,3))).toJsCmd
res3: String = 
{"foo": 4, "bar": [1, 2, 3]
}

在Scala中生成JSON(已测试2.10版本)

示例:

scala> import scala.util.parsing.json._
import scala.util.parsing.json._

scala> JSONObject (Map ("foo" -> 4, "bar" -> JSONArray (1 :: 2 :: 3 :: Nil))) .toString()
res0: String = {"foo" : 4, "bar" : [1, 2, 3]}

3
这是一个很好的回答,但现在有点过时了。然而,我不知道在StackOverflow是否接受对条目进行重大编辑的做法。 对我来说,主要问题是应该提到Twitter JSON库(基于scala-json)和Jerkson(使用Jackson)库的参考文献。此外,对内置Scala支持的引用没有提及Scala 2.9.x。 - sroebuck
3
SO最初的目标是为了避免在线论坛的问题 - 帖子会永久存在,但最终变得陈旧,而且没有人会将它们废弃或标记为不再相关。鉴于这一点,再加上这是一个社区维基回答,我建议你进行更改! - Daniel Yankowsky
3
你在声称ECMA第5版规范中允许JSON字符串使用单引号时是错误的。在第202页,规范明确指出应该使用双引号。因此,你的关于多种标准的说法也无效。Crockford定义了一个JSON标准,并随后被包含在ECMA第5版规范中,这就是唯一的JSON标准。生成或验证带有单引号的JSON字符串的实现是无效的。 - Chris Stivers
1
我正在尝试使用lift-json库将一个map转换为json。这是我的代码:var x = compact(JsonAST.render(y)),其中y是一个map。我遇到了以下错误:类型不匹配; [error] found : scala.collection.immutable.Map[String,List[String]] [error] required: net.liftweb.json.JsonAST.JValue。我该怎么办?我甚至找不到一种将Map更改为所需格式的方法。 - Ravi Ranjan

0

看看Circe。它非常好用,利用了一些来自ShapelessCats的新工具。此外,你还可以从编译为Javascript的Scala中使用它。

摘自Circe自述文件

scala> import io.circe., io.circe.generic.auto., io.circe.parser., io.circe.syntax. import io.circe._ import io.circe.generic.auto._ import io.circe.parser._ import io.circe.syntax._

scala> sealed trait Foo defined trait Foo

scala> case class Bar(xs: List[String]) extends Foo defined class Bar

scala> case class Qux(i: Int, d: Option[Double]) extends Foo defined class Qux

scala> val foo: Foo = Qux(13, Some(14.0)) foo: Foo = Qux(13,Some(14.0))

scala> foo.asJson.noSpaces res0: String = {"Qux":{"d":14.0,"i":13}}

scala> decodeFoo res1: cats.data.Xor[io.circe.Error,Foo] = Right(Qux(13,Some(14.0)))


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接