How to convert JSON to a type in Scala

28,065

Solution 1

I personally prefer lift-json, but it's pretty easy to do this with Play's JSON library:

import play.api.libs.json._
import scala.io.Source

case class Trend(name: String, url: String)

implicit object TrendReads extends Reads[Trend] {
  def reads(json: JsValue) = Trend(
    (json \ "name").as[String],
    (json \ "url").as[String]
  )
}

val url = new java.net.URL("https://api.twitter.com/1/trends/1.json")
val content = Source.fromInputStream(url.openStream).getLines.mkString("\n")
val trends = Json.parse(content) match {
  case JsArray(Seq(t)) => Some((t \ "trends").as[Seq[Trend]])
  case _ => None
}

Right now this produces the following:

scala> trends.foreach(_.foreach(println))
Trend(#TrueFactsAboutMe,http://twitter.com/search/?q=%23TrueFactsAboutMe)
Trend(#200mFinal,http://twitter.com/search/?q=%23200mFinal)
Trend(Jamaica 1,2,3,http://twitter.com/search/?q=%22Jamaica%201,2,3%22)
Trend(#DontComeToMyHouse,http://twitter.com/search/?q=%23DontComeToMyHouse)
Trend(Lauren Cheney,http://twitter.com/search/?q=%22Lauren%20Cheney%22)
Trend(Silver & Bronze,http://twitter.com/search/?q=%22Silver%20&%20Bronze%22)
Trend(Jammer Martina,http://twitter.com/search/?q=%22Jammer%20Martina%22)
Trend(Japan 2-0,http://twitter.com/search/?q=%22Japan%202-0%22)
Trend(Prata e Bronze,http://twitter.com/search/?q=%22Prata%20e%20Bronze%22)
Trend(Final 200m,http://twitter.com/search/?q=%22Final%20200m%22)

So yeah, looks about right.

Solution 2

Have a look at Lift-Json. It is part of the Lift web framework, but can be used as a stand-alone library. It can parse json into case classes (and collections of those, e.g., lists and maps), and it does not require you to add annotations. It also supports rendering classes as json, and merging and querying of json.

Here is an example taken from their website:

import net.liftweb.json._
implicit val formats = DefaultFormats // Brings in default date formats etc.

case class Child(name: String, age: Int,
                birthdate: Option[java.util.Date])
case class Address(street: String, city: String)
case class Person(name: String, address: Address,
                 children: List[Child])
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
             }
           ]
         }
       """)

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

Solution 3

Try Jerkson lib: https://github.com/codahale/jerkson/

It is a json library for scala based on Jackson. It is included to play 2: http://www.playframework.org/documentation/2.0.2/ScalaJson

Example:

    case class Person(id: Long, name: String)
    parse[Person]("""{"id":1,"name":"Coda"}""") //=> Person(1,"Coda")

Solution 4

I suggest to use Jackson JSON processor. It works both for Java and Scala. You just add annotations to your classes which describe how you want to map JSON data to your native objects.

An example:

import scala.reflect.BeanProperty
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.annotate._

class User {
  @BeanProperty var gender: String = null
  @BeanProperty var verified: Boolean = false
  @BeanProperty var userImage: Array[Byte] = null
  @BeanProperty var name: Name = null
}

case class Name {
  @BeanProperty var first: String = null;
  @BeanProperty var last: String = null;
}

object Json {
  def main(argv: Array[String]) {
    val input = """{
  "name" : { "first" : "Joe", "last" : "Sixpack" },
  "verified" : false,
  "userImage" : "Rm9vYmFyIQ=="
}""";

    val mapper = new ObjectMapper(); // can reuse, share globally
    val user: User = mapper.readValue(input, classOf[User]);

    print(user.name.first);
  }
}

This solution has a slight hassle that you have to annotate every field with @BeanProperty, but I don't know a simpler way.


Remark: As far as I know, Jackson doesn't use javax.bean.Introspector, it tries to find getters/setters by examining the methods by itself. If it did, things would have been easier, it would be possible to write just

@scala.reflect.BeanInfo
case class Name {
    var first: String;
    var last: String;
}
Share:
28,065
Athiwat Chunlakhan
Author by

Athiwat Chunlakhan

https://www.athiwat.xyz/

Updated on December 12, 2020

Comments

  • Athiwat Chunlakhan
    Athiwat Chunlakhan over 3 years

    My problem is I receive an JSON text from say, twitter. Then I want to convert this text to an native object in scala. Is there a standard method to do this? I'm also using Play 2

    Here is what I have

    import scala.io.Source.{fromInputStream}
    import java.net._
    
    val url = new URL("https://api.twitter.com/1/trends/1.json")
    val content = fromInputStream( url.openStream ).getLines.mkString("\n")
    val json = Json.parse(content)
    val a = (json \ "trends" )
    Ok(a(0))
    

    I want to get all the trends name from the JSON