Version 5, last updated by hat at 20 Jan 03:18 UTC

JSON (JavaScript Object Notation) is a lightweight, text-based, language-independent data interchange format described at json.org; for more involved description please take a look of RFC 4627.

Why would I need JSON?

JSON’s small and simple representation is suitable for storing and transmitting structured data (for example over a network connection). JSON is most commonly used to transfer data from a web service to a browser-based JavaScript application.

As one of the JSON’s design goals was to be a subset of JavaScript it is also really handy way to manipulate all kinds of data in Javascript-heavy application.

JSON is a subset of javascript as JSON is derived from the object literals of JavaScript; it can represent four primitive types (strings, numbers, booleans, and null) and two structured types (objects and arrays); “object” and “array” are based on the conventions of JavaScript.

lift-json library

In Lift, parsing and formatting utilities for JSON are provided by lift-json library.

A central concept in lift-json library is JSON AST (Abstract Syntax Tree) which models the structure of a JSON document as a syntax tree; all features are implemented in terms of AST. It provides a case class for each of the above mentioned JSON primitive and structured type, and provides functions used to transform the AST itself, or to transform the AST between different formats.

Following graph summarizes common transformations:

Further introduction and detailed examples: Please refer to the lift-json readme file

Tips

Case Classes

lift-json can serialize case classes and can extract them from JSON strings. For example:

scala> import net.liftweb.json._
scala> import net.liftweb.json.JsonParser._
scala> implicit val formats = DefaultFormats // Brings in default date formats etc.
scala> case class Child(name: String, age: Int, birthdate: Option[java.util.Date])
scala> case class Address(street: String, city: String)
scala> case class Person(name: String, address: Address, children: List[Child])
scala> val json = parse("""
         { "name": "joe",
           "address": {
             "street": "Bulevard",
             "city": "Helsinki"
           },
           "children": [
             {
               "name": "Mary",
               "age": 5
               "birthdate": "2004-09-04T18:06:22Z"
             },
             {
               "name": "Mazy",
               "age": 3
             }
           ]
         }
       """)

scala> json.extract[Person] 
res0: Person = Person(joe,Address(Bulevard,Helsinki),List(Child(Mary,5,Some(Sat Sep 04 18:06:22 EEST 2004)), Child(Mazy,3,None)))

Note: If you are trying this in the Scala REPL it will fail. Unfortunately case classes must be defined outside the REPL session in order for extraction to succeed.

Prior to Lift 2.3, if you had a list of people as your root JSON object you would need to manually extract each person:

scala> val json = parse("""[
         { "name": "joe",
           "address": {
             "street": "Bulevard",
             "city": "Helsinki"
           },
           "children": [
             {
               "name": "Mary",
               "age": 5
               "birthdate": "2004-09-04T18:06:22Z"
             },
             {
               "name": "Mazy",
               "age": 3
             }
           ]
         },
         { "name": "john",
           "address": {
             "street": "Bulevard",
             "city": "Helsinki"
           },
           "children": [
             {
               "name": "Mary",
               "age": 5
               "birthdate": "2004-09-04T18:06:22Z"
             },
             {
               "name": "Mazy",
               "age": 3
             }
           ]
         },
       ]""")
scala> val people: List[Person] = json.children.map(_.extract[Person])

Optional Parameters

You can wrap your case class parameters in Options and then their presence won’t be required in the JSON string being parsed:

scala> case class Person(name: String, address: Option[Address], children: List[Child])
scala> val json = parse("""{ "name": "joe"}""")
scala> json.extract[Person]
res0: Person = Person(joe,None,List())

You’ll notice that Lists can also be empty. The same is true for Arrays and Sets but not (yet) for Maps.

Empty collection parameters will be included when a case class instance is serialized but not Option-al ones:

import net.liftweb.json.Serialization.write
scala> case class Person(name: String, address: Option[Address], children: List[Child])
scala> val joe = Person("joe", None, Nil)
scala> write(joe)
res0: String = {"name":"Joe","children":[]}