简体   繁体   中英

Creating a Json array in Scala

I'm trying to create a Json object like so

 { "Field1": [{}, {}, {}, {}], "Field2": [{}, {}, {}], "Field3": {} }

Typically in Java I could achieve this by (specifically using google simple json)

  1. Creating JSON arrays for field1 and field2,
  2. Creating a JSON Object for Field3
  3. Combine the above 3 into another JSONObject

The results for the above are coming from separate processes, I need to return a combined json object

I'm confused how to achieve the same in Scala, I tried multiple libraries and approaches. The latest I'm trying is json4s. I thought I could

  1. Iterate through a list of string and put them in a "json array"
  2. Add the above array into another "json object" with a key
  3. Return the combined json object

I thought this will be as straightforward as it is in Java, am I missing something obvious? (I am Scala newbie).

Any help would be much appreciated.

I use scala module with jackson , JsonUtil.scala:

package json

import com.fasterxml.jackson.databind.{SerializationFeature, DeserializationFeature, ObjectMapper, PropertyNamingStrategy}
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper

object JsonUtil {
  private val ObjectMapper = buildMapper()

  private def buildMapper() = {
    val mapper = new ObjectMapper() with ScalaObjectMapper
    mapper.registerModule(DefaultScalaModule)
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
    mapper.enable(SerializationFeature.INDENT_OUTPUT)
    mapper
  }

  def fromJson[A](json: String, objectType: Class[A]): A = {
    ObjectMapper.readValue(json, objectType)
  }

  def toJson(data: AnyRef): String = {
    ObjectMapper.writeValueAsString(data)
  }
}

Dependencies:

compile 'com.fasterxml.jackson.core:jackson-core:2.6.3'
compile 'com.fasterxml.jackson.module:jackson-module-scala_2.11:2.6.3'

Then, it's easy to use:

case class MyDto(id: Int, name: Option[String])
...
val jsonString = JsonUtil.toJson(MyDto(123, Some("John Doe")))
val dto = JsonUtil.fromJson(jsonString, classOf[MyDto])

case class MyList(items: List[Int])
...
val jsonList = JsonUtil.toJson(MyList(List(1,2,3,4,5)))

Here's one way to do this in circe :

import io.circe.syntax._

val json = Map(
  "Field1" -> List.fill(4)(()).asJson,
  "Field2" -> List.fill(3)(()).asJson,
  "Field3" -> ().asJson
).asJson

And then:

scala> json.noSpaces
res0: String = {"Field1":[{},{},{},{}],"Field2":[{},{},{}],"Field3":{}}

There are a couple of things to note here. The first is that Scala encourages the use of immutable collections, so instead of "putting values into" a list, you use utilities like List.fill(n)(value) , which creates a list of value repeated n times. This means List.fill(3)(()) is exactly the same as List((), (), ()) , which we could also have used above.

The second point is more circe-specific. () is a special value in Scala—it's the only value of type Unit , which just represents an empty thing that carries no information. In circe the JSON encoding of () is {} , so we can create an empty JSON object by calling ().asJson .

circe also knows how to create a JSON array out of a Scala list (or other sequence) of any type that it knows how to encode, so List.fill(3)(()).asJson is a JSON value that's a JSON array of three empty JSON objects.

Lastly, circe also knows how to create a JSON object out of a Scala Map where the keys are strings and the value type is something it knows how to encode. This means we can create a map of type Map[String, io.circe.Json] and call asJson on it to get a JSON value that's a JSON object containing the fields represented by the map.

There are other ways you could do this in circe that might be nicer depending on your exact use case, including the following:

case class Fields(Field1: List[Unit], Field2: List[Unit], Field3: Unit)

And then:

scala> import io.circe.generic.auto._
import io.circe.generic.auto._

scala> Fields(List((), (), (), ()), List((), (), ()), ()).asJson.noSpaces
res0: String = {"Field1":[{},{},{},{}],"Field2":[{},{},{}],"Field3":{}}

This approach (which uses circe's support for generic codec derivation ) is typically more idiomatic than working with JSON values directly.

While a little verbose you can use the play framework . This enables to write case class objects and then directly convert them into JSON objects. The following example is a little loose since you didn't say what exactly your objects look like.

case class ResponseObject(a: Field1, b: Field2, c: Field3)
case class Field1(a: Seq[Field1obj])
case class Field2(a: Seq[Field2obj]))
case class Field3(a: Field3obj)
case class Field1obj(a: String, b: Int)
...

Then you can write an implicit writer using

implicit val responseWrites = Json.writes[ResponseObject]
implicit val fieldWrites = Json.writes[Field1]
...
val resultToSend = ResponseObject(field1, field2, field3)
val json = Json.toJson(resultToSend)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM