Output parsed JSON to template in lift - json

Using Lift's json parser, how can I output parsed json objects into a template?
The datatypes that net.liftweb.json.JsonParser provides are not
standard lists.
package rem.lift_client
package snippet
import net.liftweb._
import util._
import Helpers._
import net.liftweb.json.JsonParser._
class SearchResults {
def render() = {
val json_raw = "[ {\"userName\":\"John\"}, {\"userName\":\"Michael\"} ]"
val json_parsed = parse(input)
"li *" #> json_parsed.toString <---- NOT CORRECT
}
}
In the above example, I wanted to output a list of users as:
John
Michael
How do I interpret the parsed object? Any ideas are welcome, thanks.
NOTE: In addition to the accepted answer, lift-json has an excellent documentation on this subject.

One way is to extract the data with case classes.
implicit val formats = DefaultFormats
case class User(userName: String)
json_parsed.extract[List[User]]

Related

Extract a Json from an array inside a json in spark

I have a complicated JSON column whose structure is :
story{
cards: [{story-elements: [{...}{...}{...}}]}
The length of the story-elements is variable. I need to extract a particular JSON block from the story-elements array. For this, I first need to extract the story-elements.
Here is the code which I have tried, but it is giving error:
import org.json4s.{DefaultFormats, MappingException}
import org.json4s.jackson.JsonMethods._
import org.apache.spark.sql.functions._
def getJsonContent(jsonstring: String): (String) = {
implicit val formats = DefaultFormats
val parsedJson = parse(jsonstring)
val value1 = (parsedJson\"cards"\"story-elements").extract[String]
value1
}
val getJsonContentUDF = udf((jsonstring: String) =>
getJsonContent(jsonstring))
input.withColumn("cards",getJsonContentUDF(input("storyDataFrame")))
According to json you provided, story-elements is a an array of json objects, but you trying to extract array as a string ((parsedJson\"cards"\"story-elements").extract[String]).
You can create case class representing on story (like case class Story(description: String, pageUrl: String, ...)) and then instead of extract[String], try extract[List[Story]] or extract[Array[Story]]
If you need just one piece of data from story (e.g. descrition), then you can use xpath-like syntax to get that and then extract List[String]

Rename JSON fields with circe

I want to have different names of fields in my case classes and in my JSON, therefore I need a comfortable way of renaming in both, encoding and decoding.
Does someone have a good solution ?
You can use Custom key mappings via annotations. The most generic way is the JsonKey annotation from io.circe.generic.extras._. Example from the docs:
import io.circe.generic.extras._, io.circe.syntax._
implicit val config: Configuration = Configuration.default
#ConfiguredJsonCodec case class Bar(#JsonKey("my-int") i: Int, s: String)
Bar(13, "Qux").asJson
// res5: io.circe.Json = JObject(object[my-int -> 13,s -> "Qux"])
This requires the package circe-generic-extras.
Here's a code sample for Decoder (bit verbose since it won't remove the old field):
val pimpedDecoder = deriveDecoder[PimpClass].prepare {
_.withFocus {
_.mapObject { x =>
val value = x("old-field")
value.map(x.add("new-field", _)).getOrElse(x)
}
}
}
implicit val decodeFieldType: Decoder[FieldType] =
Decoder.forProduct5("nth", "isVLEncoded", "isSerialized", "isSigningField", "type")
(FieldType.apply)
This is a simple way if you have lots of different field names.
https://circe.github.io/circe/codecs/custom-codecs.html
You can use the mapJson function on Encoder to derive an encoder from the generic one and remap your field name.
And you can use the prepare function on Decoder to transform the JSON passed to a generic Decoder.
You could also write both from scratch, but it may be a ton of boilerplate, those solutions should both be a handful of lines max each.
The following function can be used to rename a circe's JSON field:
import io.circe._
object CirceUtil {
def renameField(json: Json, fieldToRename: String, newName: String): Json =
(for {
value <- json.hcursor.downField(fieldToRename).focus
newJson <- json.mapObject(_.add(newName, value)).hcursor.downField(fieldToRename).delete.top
} yield newJson).getOrElse(json)
}
You can use it in an Encoder like so:
implicit val circeEncoder: Encoder[YourCaseClass] = deriveEncoder[YourCaseClass].mapJson(
CirceUtil.renameField(_, "old_field_name", "new_field_name")
)
Extra
Unit tests
import io.circe.parser._
import org.specs2.mutable.Specification
class CirceUtilSpec extends Specification {
"CirceUtil" should {
"renameField" should {
"correctly rename field" in {
val json = parse("""{ "oldFieldName": 1 }""").toOption.get
val resultJson = CirceUtil.renameField(json, "oldFieldName", "newFieldName")
resultJson.hcursor.downField("oldFieldName").focus must beNone
resultJson.hcursor.downField("newFieldName").focus must beSome
}
"return unchanged json if field is not found" in {
val json = parse("""{ "oldFieldName": 1 }""").toOption.get
val resultJson = CirceUtil.renameField(json, "nonExistentField", "newFieldName")
resultJson must be equalTo json
}
}
}
}

How to create a JSON object in Scala?

First, I searched a lot on Google and StackOverflow for questions like that, but I didn't find any useful answers (to my big surprise).
I saw something about Play Framework, how to create JSON array in Java and how to create JSON objects in Java, but I don't want to use Play Framework and I don't know if the creation of JSON objects differ from Scala to Java.
Following is the JSON I want to create. Later I'll convert the object into a string to send it via a POST request (through an API call).
{
"start_relative": {
"value": "5",
"unit": "years"
},
"metrics": [
{
"name": "DP_391366" # S-Temperature - Celsius
},
{
"name": "DP_812682" # Sensor-A4 Luminosity
}
]
}
How can I do something like that in Scala?
You should use a library that handles serialization/deserialization.
I would consider choosing between Spray Json and Play Json.
I will explain to you how the process works with Play first, and it's very similar to that in Spray.
Let's say you have a class, and an object with an instance and a json as string:
case class MyClass(id: Int,
name: String,
description: String)
object Data {
val obj: MyClass = MyClass(1, "me", "awesome")
val str: String =
"""
|{
| "id": 1,
| "name": "me",
| "description": "awesome"
|}
""".stripMargin
}
For MyClass to be serialized/deserialized, you will need an implicit formatter, specific for this, so you will create an object that contains this formatter using Play.
trait MyClassPlayProtocol {
implicit val formatAbility = Json.format[Ability]
}
object MyClassPlayProtocol extends MyClassPlayProtocol
The serialization/deserialization will look something like this:
object PlayData {
import play.api.libs.json.JsValue
import play.api.libs.json.Json
import MyClassPlayProtocol._
import General._
val str2Json: JsValue = Json.parse(str)
val obj2Json: JsValue = Json.toJson(obj)
val json2Str: String = Json.stringify(str2Json)
val json2Obj: MyClass = obj2Json.as[MyClass]
}
In Spray, the protocol will look like this:
trait MyClassSprayProtocol extends DefaultJsonProtocol {
implicit val myClassFormat = jsonFormat3(MyClass)
}
object MyClassSprayProtocol extends MyClassSprayProtocol
and the serialization/deserialization:
object SprayData {
import spray.json._
import MyClassSprayProtocol._
import General._
val str2Json: JsValue = str.parseJson
val obj2Json: JsValue = obj.toJson
val json2Str: String = str2Json.compactPrint
val json2Obj: MyClass = obj2Json.convertTo[MyClass]
}
As you can see, it's mostly a matter of choice between this two. Both are still improved and probably will be in the near future.
Depending on the benchmark, you will find that one is better than the other by a few miliseconds (usually Spray).
I for one am using Spray at work and Play in some personal projects, and I can't say I found something fundamentally different from one to another.
EDIT:
And to finally answer your question, to go from MyClass to String (serialization), you will do something like this:
PLAY: Json.stringify(Json.toJson(myClass))
SPRAY: myClass.toJson.compactPrint
And the deserialization:
PLAY: Json.parse(string).as[MyClass]
SPRAY: myClass.parseJson.convertTo[MyClass]
You need to use a library if you dont want it to do by yourself there are serveral:
Spray Json - https://github.com/spray/spray-json
Lift Json - https://github.com/lift/lift/tree/master/framework/lift-base/lift-json/
Jerkson - https://github.com/codahale/jerkson
Jackson - You can use Jackson with the scala Module https://github.com/FasterXML/jackson-module-scala
Note: The cool Java Gson LIbrary looses a lot of Magic if you want to use it with Scala, because they dont know Collections. So if you want to use this library you have to convert the "Scala List" to java.util.List and after that use Gson.

Convert any Scala object to JSON

I am using latest version of Play Framework and it's JSON lib like this Json.toJson(obj). But toJson is not capable of converting any Scala object to JSON, because the structure of data is unknown. Someone suggested using case convert, but here my Scala knowledge falls short. The data comes from database, but the structure of table is not known.
Where should I look further to create convert such unknown data structure to JSON?
Given that there is only a limited number of types you want to serialize to JSON, this should work:
object MyWriter {
implicit val anyValWriter = Writes[Any] (a => a match {
case v:String => Json.toJson(v)
case v:Int => Json.toJson(v)
case v:Any => Json.toJson(v.toString)
// or, if you don't care about the value
case _ => throw new RuntimeException("unserializeable type")
})
}
You can use it by then by importing the implicit value at the point where you want to serialize your Any:
import MyWriter.anyValWriter
val a: Any = "Foo"
Json.toJson(a)
Using json4s, you can import the package:
import org.json4s.DefaultFormats
import org.json4s.native.Serialization.write
Then create an implicit variable inside your trait:
implicit val formats: DefaultFormats = DefaultFormats
And finally, in your method, use it:
write(myObject)

Can I use Lift's FieldSerializer to change a field on serialization?

I'm trying to html-escape all strings in my objects when serializing them with Lift's json library. I thought I could do this by passing an escape (partial) function to the objects' FieldSerializer, but this seems to add fields to the json-object - not replace them. You can see what I mean by running the following simple example and note that it outputs
{"y":"test","x":"test"} while I wanted {"y":"test"}:
import net.liftweb.json.Serialization
import net.liftweb.json.FieldSerializer
import net.liftweb.json.FieldSerializer._
import net.liftweb.json.DefaultFormats
case class Simple(x: String)
implicit val formats = DefaultFormats +
FieldSerializer[Simple](renameTo("x","y"),renameFrom("y", "x"))
Serialization.write(Simple("test"))
Is there a way I can tell FieldSerializer to replace the field matched by my partial function?
Try CoustomeSerializer:
implicit val formats = DefaultFormats +
new CustomSerializer[Simple](ser => ( {
case JObject(JField("y", JString(x)) :: Nil) => Simple(x)
}, {
case simple: Simple => JObject(JField("y", JString(simple.x)) :: Nil)
}))
ps.I asked this on lift's google group,and got this answer,checkout https://groups.google.com/d/msg/liftweb/ShRrGNrsu6Y/sNw4JGdSU6sJ