17

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

Wrench
  • 4,070
  • 4
  • 34
  • 46
Athiwat Chunlakhan
  • 7,589
  • 14
  • 44
  • 72
  • 1
    possible duplicate of [How can I construct and parse a JSON string in Scala / Lift](http://stackoverflow.com/questions/927983/how-can-i-construct-and-parse-a-json-string-in-scala-lift) – om-nom-nom Aug 09 '12 at 19:42
  • there is also Jerkson and some others less known libraries – om-nom-nom Aug 09 '12 at 19:48
  • 1
    @om-nom-nom: I don't think this counts as a duplicate—the other question is specifically about Lift, not Play (and is fairly ancient, anyway). – Travis Brown Aug 09 '12 at 20:22
  • @TravisBrown it contains example for pure scala 2.8.1 solution and mentions sjson, Jerkson and Twitter JSON library. While question is not exact duplicate, the answer fits this particular question (what can I do if I have scala and play), I don't think it is good to have a bunch of near-duplicate questions each them with a list of all fancy scala libraries for json. – om-nom-nom Aug 09 '12 at 20:33
  • But the question here asks how to do this in Play 2.0, and the code given uses Play's JSON library. It's maybe unfortunate that people are giving non-Play answers, but I don't think that's the question's fault. – Travis Brown Aug 09 '12 at 21:03
  • @om-nom-nom, TravisBrown is correct I already know how to do this in pure Scala, using Jerkson. The question is directed at the JSON library that is provided by the Play 2.0 framework. – Athiwat Chunlakhan Aug 10 '12 at 09:47

5 Answers5

8

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.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
6

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)))
 */
Malte Schwerhoff
  • 12,684
  • 4
  • 41
  • 71
3

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;
}
Petr
  • 62,528
  • 13
  • 153
  • 317
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")
user1411778
  • 461
  • 1
  • 5
  • 12
0

convert to jsvalue by using Json.parse(string) then add operator as[T] to extract the value