3

I have dynamically computed collection of jsObject.

val collection: Enumerator[JsObject] = ???

I want to transform my collection to a JsArray and send the JsArray using the chunked transfer encoding.

import play.api.libs.iteratee.{Iteratee, Enumerator}
import play.api.libs.json._
import play.api.mvc.{Action, Controller}

import scala.concurrent.{Future, ExecutionContext}
import scala.concurrent.ExecutionContext.Implicits.global

def f(i: Int): JsObject = JsObject(Seq("foo" -> JsNumber(i)))

val collection: Enumerator[JsObject] =
  Enumerator.enumerate(List(1,2,3).map(f))

/**
 * Creates a JSON array from a key value iteratee
 */
def jsonArrayCreator(implicit ctx: ExecutionContext): Iteratee[JsObject, JsArray] =
  Iteratee.getChunks.map(values => new JsArray(values))

def toJsArray(e: Enumerator[JsObject]): Enumerator[String] = {
  var i = 0
  def _counter = { i += 1; i }

  Enumerator("[") >>> e.map {
    x => if (_counter == 1) Json.stringify(x) else s",${Json.stringify(x)}"
  } >>> Enumerator("]")
}

object Bar extends Controller {
  // solution 1
  def getChunked = Action {
    Ok.chunked(collection)
    // res: {"foo":1}{"foo":2}{"foo":3}
  }

  // solution 2
  def getJsArray = Action.async {
    val fJsArray: Future[JsArray] = collection |>>> jsonArrayCreator
    fJsArray.map(Ok(_))
    // res: [{"foo":1},{"foo":2},{"foo":3}]
  }

  // solution 3
  def getJsArrayChunked = Action {
    Ok.chunked(toJsArray(collection))
        .withHeaders("Content-type" -> "application/json)")
    //res: [{"foo":1},{"foo":2},{"foo":3}]
  }
}

Solution 1 is unvalid, as the result isn't a valid Json.

Solution 2 doesn't use chunked transfer encoding.

Solution 3 works, but I don't think it the proper way of doing what I want. Is there a cleaner solution?

ulric260
  • 364
  • 4
  • 15
  • 1
    See my answer to a similar question here: http://stackoverflow.com/a/33822051/4432837 – cbley Nov 20 '15 at 08:41

0 Answers0