How do you create Json object with values of different types?

我怕爱的太早我们不能终老 提交于 2019-12-22 05:54:51

问题


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!


回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/16313902/how-do-you-create-json-object-with-values-of-different-types

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!