Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing a simple array with Spray-json

I'm trying (and failing) to get my head around how spray-json converts json feeds into objects. If I have a simple key -> value json feed then it seems to work ok but the data I want to read comes in a list like this:

[{
    "name": "John",
    "age": "30"
},
{
    "name": "Tom",
    "age": "25"
}]

And my code looks like this:

package jsontest

import spray.json._
import DefaultJsonProtocol._

object JsonFun {

  case class Person(name: String, age: String)
  case class FriendList(items: List[Person])

  object FriendsProtocol extends DefaultJsonProtocol {
    implicit val personFormat = jsonFormat2(Person)
    implicit val friendListFormat = jsonFormat1(FriendList)
  }

  def main(args: Array[String]): Unit = {

    import FriendsProtocol._

    val input = scala.io.Source.fromFile("test.json")("UTF-8").mkString.parseJson

    val friendList = input.convertTo[FriendList]

    println(friendList)
  }

}    

If I change my test file so it just has a single person not in an array and run val friendList = input.convertTo[Person] then it works and everything parses but as soon as I try and parse an array it fails with the error Object expected in field 'items'

Can anyone point me the direction of what I'm doing wrong?

like image 569
pogo Avatar asked Feb 15 '15 19:02

pogo


Video Answer


2 Answers

Well, as is often the way immediately after posting something to StackOverflow after spending hours trying to get something working, I've managed to get this to work.

The correct implementation of FriendsProtocol was:

object FriendsProtocol extends DefaultJsonProtocol {
  implicit val personFormat = jsonFormat2(Person)
  implicit object friendListJsonFormat extends RootJsonFormat[FriendList] {
    def read(value: JsValue) = FriendList(value.convertTo[List[Person]])
    def write(f: FriendList) = ???
  } 
}

Telling Spray how to read / write (just read in my case) the list object is enough to get it working.

Hope that helps someone else!

like image 131
pogo Avatar answered Oct 20 '22 10:10

pogo


To make the Friend array easier to use extend the IndexedSeq[Person]trait by implementing the appropriate apply and length methods. This will allow the Standard Scala Collections API methods like map, filter and sortBy directly on the FriendsArray instance itself without having to access the underlying Array[Person] value that it wraps.

case class Person(name: String, age: String)

// this case class allows special sequence trait in FriendArray class
// this will allow you to use .map .filter etc on FriendArray
case class FriendArray(items: Array[Person]) extends IndexedSeq[Person] {
    def apply(index: Int) = items(index)
    def length = items.length
}

object FriendsProtocol extends DefaultJsonProtocol {
  implicit val personFormat = jsonFormat2(Person)
  implicit object friendListJsonFormat extends RootJsonFormat[FriendArray] {
    def read(value: JsValue) = FriendArray(value.convertTo[Array[Person]])
    def write(f: FriendArray) = ???
  } 
}

import FriendsProtocol._

val input = jsonString.parseJson
val friends = input.convertTo[FriendArray]
friends.map(x => println(x.name))
println(friends.length)

This will then print:

John
Tom
2
like image 2
Rocket Surgeon Avatar answered Oct 20 '22 11:10

Rocket Surgeon