Working with JSON in Scala using the json4s library (Part one).

In this very brilliant article, you can find a comparison between Scala libraries in terms of parsing speed. One of the best result was given by the json4s library. In the first part I will describe the library and it’s main functions, while in the second part I’ll go in deep showing some more detailed examples. As usual let’s create a Maven Scala project with Eclipse, adding the following dependency to the Maven pom.xml file:

org.json4s
json4s-native_${scala.version}
3.2.10

Substitute ${$scala.version} with your version of Scala (2.10 for example). If you don’t know how to create a Maven project with Scala in Eclipse follow this article (just the first part in which it is showed how to setup/install Eclipse with the Scala plugin). At the time of writing, I’ve found some problem with the 3.2.11 version (which is the last one), but the previous one was working smoothly. Now let’s create a Scala object with a main function to run:

package com.nosqlnocry.test
import org.json4s._
import org.json4s.JsonDSL._
import org.json4s.jackson.JsonMethods._
object Json4sTest {  
  def main(arg: Array[String]) {
    ...
  }
}

Before starting, we have to take a look at how the library json4s is modelling JSONs. Looking in the box below, we can see that it is using a syntax tree AST (Abstract Syntax Tree). All starts from an abstract class JValue that have to be initialized with different classes/objects depending on what that “value” is representing.

sealed abstract class JValue
case object JNothing extends JValue // 'zero' for JValue
case object JNull extends JValue
case class JString(s: String) extends JValue
case class JDouble(num: Double) extends JValue
case class JDecimal(num: BigDecimal) extends JValue
case class JInt(num: BigInt) extends JValue
case class JBool(value: Boolean) extends JValue
case class JObject(obj: List[JField]) extends JValue
case class JArray(arr: List[JValue]) extends JValue

type JField = (String, JValue)

In the end there is a functor definition that represents the classic key-value couple. In order to create a JSON, you have to use functions offered by the library that can “parse” or “produce” a JValue starting from a String or Tuples (couples in Scala). Basically you can use the “parse(jsonString: String)” function (to parse the JSON  from a string) or the DSL (Domain-Specific Language) that implement operators to create a JSON from tuples. In the picture below you have a schema where in the center you can find the Json AST (simply: a JSON represented with JValue), sorrounded by all the possible operations you can do with the json4s library. Now let’s have some example starting from the top of this diagram: how to transform String in JsonAST and how to use the DSL to create a JSON. Say we want to create this JSON here

{"name":"luca", "id": "1q2w3e4r5t", "age": 26, "url":"http://www.nosqlnocry.wordpress.com"}

this is what we are going to add inside the main method:

// DSL example
var json = ("name","luca") ~ ("id","1q2w3e4r5t") ~ ("age",26) ~ ("url","http://www.nosqlnocry.wordpress.com")
// tuples can be defined also like this: ("id" -> "1q2w3e4r5t")

As an alternative we can use the parse function that creates the JSON from a string

// parse from string example
var json = parse("""{"name":"luca", "id": "1q2w3e4r5t", "age": 26, "url":"http://www.nosqlnocry.wordpress.com"}""")

In both cases, the val json will be:

println(json)

JObject(List((name,JString(luca)), (id,JString(1q2w3e4r5t)), (age,JInt(26)), (url,JString(http://www.nosqlnocry.wordpress.com))))

The operator “~” in the first example transforms tuples in JFields combining them in a list to create a  org.json4s.JsonAST.JObject, while using the parse function, it will return a org.json4s.JValue. Now that we know how to load a JSON in Scala, let’s give few simple example about how to manipulate it. In the second part of this article we will focus more on advanced examples. Let’s see how:

  1. to add a new field
  2. to update a field
  3. to remove a field
  4. to retrieve a value

1. If you are using the ~ operator, you can add a new value reassigning the variable in this way

json = json ~ ("height" -> 175)

because that operator works just with object of the JsonDSL package. In case you used the parse function, you have to exploit the merge function to merge the old json with a new JValue created with the render method (you need to use render because a single Tuple in Scala is not automatically converted in a JValue within a merge operation):

json = json merge render("height",175)
println(pretty(json)) // use 'pretty' or 'compact'

The result printed will be:

{
  “name” : “luca”,
  “id” : “1q2w3e4r5t”,
  “age” : 26,
  “url” : “http://www.nosqlnocry.wordpress.com”,
  “heigth” : 176
}

2. When you want to update a field, you can use the transformField function:

json = json transformField {
    case JField("name", _) => ("NAME", JString("Luca"))
    case JField("age", JInt(age)) => ("age", JInt(age+1))
}
println(pretty(json))

{
  “NAME” : “Luca”,
  “id” : “1q2w3e4r5t”,
  “age” : 27,
  “url” : “http://www.nosqlnocry.wordpress.com”,
  “height” : 175
}

As you can see you can update either the name or the value.

3. To remove a field you can use either a filter function or a remove function (depending on how you want to delete it)

json = json removeField {
    case JField("NAME", _) => true
    case _ => false
}
// alternative
json = json filterField {
    case JField("NAME", _) => false
    case _ => true
println(pretty(json)) 

{
  “id” : “1q2w3e4r5t”,
  “age” : 27,
  “url” : “http://www.nosqlnocry.wordpress.com”,
  “height” : 175
}

4. To retrieve a value in short you can use different approach:

println(compact(json \\ "age")) // 27
println(compact(json \ "age"))  // 27
println(compact(json.children(1))) // 27

In the second article we will go in deep, testing how this library and its functions are working also with nested JSON structure.

Please write a comment if you have any problem,
and don’t forget to like the blog!
PEACE! 😀

meniluca

8 comments

  1. Thanks for the examples 🙂
    I’m trying the removeField from your example using a variable, but it doesn’t seems to be working well:

    var nameString:String = “NAME”

    json = json removeField {
    case JField(nameString, _) => true
    case _ => false
    }

    It will just remove all the fields…
    Am I missing something here?

    Like

    1. It’s a caveat with pattern matching:in
      case JField(nameString, _) => true

      nameString does NOT refer to the variable you previously defined. It is giving a name ot the field name in the pattern; It is just a way to
      refer to the name of the field in the expression on the right of the =>; Since it has the same name as the variable, it takes precedence over it, and since you are not specifying any condifion on the name of the field, it is deleting everything;
      Essentially, your code would work exactly the same if you had

      case JField(_, _) => true

      Something like

      case JField(theFieldName, _) if theFieldName==nameString => true

      or

      case JField(theFieldName, _) => theFieldName==nameString

      or

      case JField(“NAME”, _) => true

      should do the trick.

      Like

  2. Reblogged this on Ashish Tomer and commented:
    This is not my blogpost!! I am reading this because I want to know about json4s. json4s is used in my project and I must know it.

    Like

  3. Thanks for this article. I worked on a project using Json4s. I used the transformField to transform a List[JObject] from the same library. I noticed that transformField returns a JValue, and so after transforming fields for each JObject in my list, I needed to cast List[JValue] using asInstanceOf(List[JObject]). I think this cast is safe, but sometimes I worry about casting objects. Is this due to type erasure? Do you have any opinion regarding this?

    Like

Leave a comment