¿Cómo puedo construir y analizar una cadena JSON en Scala/Lift?
Pregunta
Estoy intentando utilizar JSON para enviar datos entre el navegador y mi aplicación.
Estoy intentando utilizar Lift 1.0 para crear y analizar cadenas JSON, pero por alguna razón no puedo analizar el JSON que acabo de construir:
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
¿Cómo construyo mediante programación un mensaje JSON válido en Scala/Lift que también se puede analizar nuevamente?
Solución
Estás usando Lift 1.0 JsCmd
, que produce JSON con cadenas entre comillas simples e intenta analizarlo con el analizador de Scala, que solo admite cadenas entre comillas dobles.
Es importante darse cuenta de que existen múltiples definiciones para JSON.
¿Son válidas las cadenas entre comillas simples en JSON?
- son de acuerdo a ECMAScript 5ª Ed.
- No son según el original de Crockford. RFC 4627
Lift y Scala proporcionan muchas formas de analizar JSON, a veces con comportamientos diferentes entre versiones.
Las cadenas aceptadas por estos analizadores no son equivalentes.
A continuación se muestran algunos comentarios y ejemplos de los distintos métodos para producir y analizar cadenas JSON.
Producir JSON con el levantar-json biblioteca DSL
- Recomendado
- A pesar de su nombre, se trata de un proyecto independiente que no depende del resto de Lift.
ejemplo:
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]}
Analizando JSON con el levantar-json biblioteca
- Recomendado
- Proporciona mapeo implícito hacia/desde clases de casos de Scala
- Las clases de casos definidas en la consola no son compatibles actualmente (arrojará un
com.thoughtworks.paranamer.ParameterNamesNotFoundException: Unable to get class bytes
) - El siguiente ejemplo utiliza
ID pública
, una clase de caso de Scala preexistente para que funcione en la consola de Scala.
ejemplo:
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"
Análisis de JSON en Scala 2.7.7 y 2.8.1
- No recomendado - "Ya no es realmente compatible"
- El analizador de Scala 2.7.7 no analizará JSON entre comillas simples
- Este método de análisis utilizado en la pregunta.
ejemplo:
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
Analizando JSON en Lift 2.0 y 2.2 con util.JSONParser
- Recomendación neutral
- util.JSONParser de Lift analizará cadenas JSON entre comillas simples o dobles:
ejemplo:
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))
Analizando JSON en Lift 2.0 y 2.2 con json.JsonParser
- Recomendación neutral
- Json.JsonParser de Lift no analizará cadenas JSON entre comillas simples:
ejemplo:
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...
Produciendo JSON con Lift 1.0 JsCmd
- No recomendado: la salida no es válida para todos los analizadores JSON
- Tenga en cuenta las comillas simples alrededor de las cadenas:
ejemplo:
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]
}
Produciendo JSON con Lift 2.0 JsCmd
- Recomendación neutral
- Tenga en cuenta las comillas dobles alrededor de las cadenas:
ejemplo:
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]
}
Produciendo JSON en Scala (probado con 2.10)
- "Ya no es realmente compatible", pero funciona y está ahí.
ejemplo:
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]}
Otros consejos
Tome un vistazo a Circe . Es muy agradable de usar y aprovecha algunas de las nuevas herramientas de Sin forma y Gatos . Además, se puede usar desde Scala compilado a Javascript.
Scala> io.circe importación. , io.circe.generic.auto. , io.circe.parser. , io.circe.syntax. io.circe._ importación io.circe.generic.auto._ importación importar importar io.circe.parser._ io.circe.syntax ._
Scala> rasgo sellado Foo rasgo definido Foo
Scala> caso Bar clase (xs: Lista [String]) se extiende Foo clase definida Bar
Scala> Clase caso Qux (i: Int, d: Opción [Doble]) se extiende Foo definido clase Qux
Scala> val foo: Foo = Qux (13, algunos (14.0)) foo: Foo = Qux (13, algunos (14.0))
Scala> foo.asJson.noSpaces res0: String = { "Qux": { "d": 14,0, "i": 13}}
Scala> decodeFoo res1: cats.data.Xor [io.circe.Error, Foo] = Derecho (Qux (13, algunos (14.0)))