Object

play.extras.iteratees

JsonBodyParser

Related Doc: package iteratees

Permalink

object JsonBodyParser

Body parser for reactively parsing JSON. Used with no arguments, it just parses a JsValue into memory. However, you can do more powerful things, for example, let's say you have a bulk load function that takes a possibly very long JSON input containing the bulk data to load, including some meta data, like the following:

{
  "exportId": 12345,
  "exportDate": "17/10/2012",
  "exportUser": "bob"
  "items": [
    { "id": 1, ... },
    ...
    ]
}

You could parse this, without loading all the items in memory, but rather saving them to a database as they arrive, like this:

import JsonBodyParser._
import JsonIteratees._
import JsonEnumeratees._

// case class that we will fold the result of the parsing into
case class Errors(id: Int = 0, errors: List[String] = Nil)

// Map function that ignores the input, and returns an identity function to leave errors alone
def ignore[A]: A => Errors => Errors = (_) => identity[Errors]

// The parser
val bodyParser = parser(
  // A JSON object enumerator, expecting keys, using the specified parsers for the values of each.
  // Each value gets mapped to a function, that will be used later to fold them into our Errors result.
  jsObject(
    // Handle the exportId as a number, and map it to a function that stores the id in Errors
    "exportId" -> jsNumber.map(id => (e: Errors) => Errors(id.value.toInt, e.errors)),
    "exportDate" -> jsNullOr(jsString).map(ignore),
    "exportUser" -> jsNullOr(jsString).map(ignore),
    // Handle the items as an array, parsing the values as objects, then using enumeratee composition,
    // parse the item, import the item, and finally collect the errors and map them to the function
    // for folding into the Errors result
    "items" -> (jsArray(jsValues(jsSimpleObject)) ><> parseItem ><> importItem
      &>> Iteratee.getChunks[String].map(errorList => (e: Errors) => Errors(e.id, errorList)))
  // Fold the error functions into an Errors result
  ) &>> Iteratee.fold[Errors => Errors, Errors](Errors())((e, f) => f(e))
)

// The items we want to import
case class Item(id: Int, name: String, description: String)

// Enumeratee that parses a JsObject into an item.  Uses a simple mapping Enumeratee.
def parseItem: Enumeratee[JsObject, Option[Item]] = Enumeratee.map {obj =>
  for {
    id <- (obj \ "id").asOpt[Int]
    name <- (obj \ "name").asOpt[String]
    description <- (obj \ "description").asOpt[String]
  } yield Item(id, name, description)
}

// Enumeratee that imports items.  Uses an input mapping enumeratee, and only passes a result
// along if there is an error
def importItem: Enumeratee[Option[Item], String] = Enumeratee.mapInput(_ match {
  case Input.El(Some(item)) =>
    println(item)
    Input.Empty
  case Input.El(None) => Input.El("An error")
  case other => other.map(_ => "")
})

// Our action that uses the body parser
def bulkImport = Action(bodyParser) {
  request =>
    Ok("Imported export id " + request.body.id +
      " with the following errors:\n" + request.body.errors.mkString("\n"))
}
Linear Supertypes
AnyRef, Any
Ordering
  1. Alphabetic
  2. By Inheritance
Inherited
  1. JsonBodyParser
  2. AnyRef
  3. Any
  1. Hide All
  2. Show All
Visibility
  1. Public
  2. All

Value Members

  1. final def !=(arg0: Any): Boolean

    Permalink
    Definition Classes
    AnyRef → Any
  2. final def ##(): Int

    Permalink
    Definition Classes
    AnyRef → Any
  3. final def ==(arg0: Any): Boolean

    Permalink
    Definition Classes
    AnyRef → Any
  4. final def asInstanceOf[T0]: T0

    Permalink
    Definition Classes
    Any
  5. def clone(): AnyRef

    Permalink
    Attributes
    protected[java.lang]
    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  6. final def eq(arg0: AnyRef): Boolean

    Permalink
    Definition Classes
    AnyRef
  7. def equals(arg0: Any): Boolean

    Permalink
    Definition Classes
    AnyRef → Any
  8. def finalize(): Unit

    Permalink
    Attributes
    protected[java.lang]
    Definition Classes
    AnyRef
    Annotations
    @throws( classOf[java.lang.Throwable] )
  9. final def getClass(): Class[_]

    Permalink
    Definition Classes
    AnyRef → Any
  10. def hashCode(): Int

    Permalink
    Definition Classes
    AnyRef → Any
  11. final def isInstanceOf[T0]: Boolean

    Permalink
    Definition Classes
    Any
  12. final def ne(arg0: AnyRef): Boolean

    Permalink
    Definition Classes
    AnyRef
  13. final def notify(): Unit

    Permalink
    Definition Classes
    AnyRef
  14. final def notifyAll(): Unit

    Permalink
    Definition Classes
    AnyRef
  15. def parser[A](handler: Iteratee[CharString, A] = jsonValue): BodyParser[A] { def apply(rh: play.api.mvc.RequestHeader): play.api.libs.streams.Accumulator[akka.util.ByteString,scala.util.Right[Nothing,A]] }

    Permalink

    Create a parser

    Create a parser

    handler

    An iteratee to handle the JSON. By default parses them into memory as a JsValue

  16. final def synchronized[T0](arg0: ⇒ T0): T0

    Permalink
    Definition Classes
    AnyRef
  17. def toString(): String

    Permalink
    Definition Classes
    AnyRef → Any
  18. final def wait(): Unit

    Permalink
    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  19. final def wait(arg0: Long, arg1: Int): Unit

    Permalink
    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  20. final def wait(arg0: Long): Unit

    Permalink
    Definition Classes
    AnyRef
    Annotations
    @throws( ... )

Inherited from AnyRef

Inherited from Any

Ungrouped