How do you create Json object with values of different types ?
I'm using spray-json
Here is the code
val images : List[JsObject] = fetchImageUrls(url).map((url: String) => {
JsObject(List(
"link_path" -> JsString(url),
"display_name" -> JsString("image"),
"size" -> JsString(""),
"modified" -> JsString(""),
"thumbnail" -> JsString(url),
"filename" -> JsString("image"),
"is_dir" -> JsBoolean(x = false),
"thumb_exists" -> JsBoolean(x = true)) )
})
val jsonAst: JsObject = JsObject(List(
"client" -> JsString("urlimages"),
"view" -> JsString("thumbnails"),
"contents" -> JsArray(images)
))
It works but looks really heavy. Is there a way to define json with code like this ?
val images : List[List[(String, Any)]] = fetchImageUrls(url).map((url: String) => {
List(
"link_path" -> url,
"display_name" -> "image",
"size" -> "",
"modified" -> "",
"thumbnail" -> url,
"filename" -> "image",
"is_dir" -> false,
"thumb_exists" -> true)
})
val jsonAst = List(
"client" -> "urlimages",
"view" -> "thumbnails",
"contents" -> images
).toJson
It doesn't work saying that
Cannot find JsonWriter or JsonFormat type class for List[(String, Object)]
).toJson
^
Which I get, type of each field is not defined at compile time. But why wouldn't it work if serializer does pattern matching anyway ?
Thanks!
I agree with @alex23 that a case class based approach will be better. Using spray-json, you would first define your case class structure as well as an extension of DefaultJsonProtocol to describe the case classes you want to be able to serialize. That would look like this:
case class Image(link_path:String, display_name:String, size:Option[String],
modified:Option[String], thumbnail:String, filename:String, is_dir:Boolean, thumb_exists:Boolean)
object Image
case class UrlImages(client:String, view:String, contents:List[Image])
object UrlImages
object MyJsonProtocol extends DefaultJsonProtocol {
implicit val imageFormat = jsonFormat8(Image.apply)
implicit val urlImagesFormat = jsonFormat3(UrlImages.apply)
}
Then, a modified version of your example would look like this:
import MyJsonProtocol._
val images : List[Image] = fetchImageUrls(url).map((url: String) => {
Image(url, "image", None, None, url, "image", false, true)
})
val jsonAst = UrlImages("urlimages", "thumbnails", images).toJson
The reason why you were seeing that error is that spray-json does not know how to serialize the Lists of tuples you are creating. If you really want to use that structure and not go the case class route, then you could look into providing a custom serializer for List[(String,Any)]. Check out the section in the spray-json docs titled "Providing JsonFormats for other Types". If you want to go this route and need more help, let me know.
You are going for the wrong approach here. For consistency purposes I would strongly encourage you to use a case class
.
Say you have this
case class Image(
url: String,
size: Double,
properties: Map[String][String]
optionalProperty: Option[String]
// etc.
);
And then you use parse
and decompose
to deal with this.
val image = parse(jsonString).extract[Image]; // extracts an Image from JSON.
val jsonForImage: JValue = decompose(image); // serializes an Image to JSON.
And if you want to serialize a List[Image]
to JSON:
def serialize(images: List[Image]) : JValue = {
for (image <- images)
yield decompose(image);
};
To parse a list of images from JSON:
val images: List[Image] = parse(jsonString).extract[List[Image]];
Using Option[SomeType]
in the Image
case class
will deal with missing/optional parameters automatically.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With