how to remove outer array "[" of JSON response

1,125 views
Skip to first unread message

mkr...@trialfire.com

unread,
Jun 20, 2013, 6:13:38 PM6/20/13
to reacti...@googlegroups.com
Hi. 

I have the following code in my controller (based loosely on the example from the reactivemongo-demo-app ):


 def index = Action { implicit request =>
    Async {
      
      val cursor: Cursor[JsObject] = collection.find(Json.obj()).cursor[JsObject]

       val futureArticleList: Future[List[JsObject]] = cursor.toList
       val futureArticleListJsonArray: Future[JsArray] = futureArticleList.map { articles =>
        Json.arr(articles)        
      }
      futureArticleListJsonArray.map { articles =>
        Ok(articles)
      }
      
    }
  }


The above code returns a doubly nested JSON array as show below. Notice the doubly nested array within an array. How do I get rid of the outer array? Or rather what is causing it?

[
  • [
    • {
      • _id
        {
        • $oid"51c33bce865173290120d2b7"
        },
      • title"How i became awesome",
      • content"blah blah blah",
      • publisher"Tyler Durden",
      • creationDate
        {
        • $date1371749326516
        },
      • updateDate
        {
        • $date1371749326516
        }
      },
    • {
      • _id
        {
        • $oid"51c34853865173389320d2b8"
        },
      • title"sgf",
      • content"sdfsdf",
      • publisher"sdfsdf",
      • creationDate
        {
        • $date1371752531157
        },
      • updateDate
        {
        • $date1371752531157
        }
      }
    ]
]

Paul Dijou

unread,
Jun 20, 2013, 6:45:02 PM6/20/13
to reacti...@googlegroups.com
Hey there,

If you write Json.arr(articles), you are creating a JsArray with only one element, your list (which will be transformed in an array). So you will have an array of one array.

The solution is to write Json.arr(articles: _*) instead, so you will have only one array of all your elements.

Regards

--
You received this message because you are subscribed to the Google Groups "ReactiveMongo - http://reactivemongo.org" group.
To unsubscribe from this group and stop receiving emails from it, send an email to reactivemong...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

mkr...@trialfire.com

unread,
Jun 21, 2013, 11:33:00 AM6/21/13
to reacti...@googlegroups.com
Thanks Paul,

  I follow your logic and that totally makes sense. (I'm new a to Scala so excuse my syntactical ignorance) I tried Json.arr(articles: _*) and it won't compile, looks like the Json.arr method expects a Seq[JSValueWrapper] but instead finds a List[JsObject]. So what is Json.arr(articles: _*) actually doing? And how to make this work as you describe in order to have a single array of all my objects?

-Max

Paul Dijou

unread,
Jun 22, 2013, 12:34:28 PM6/22/13
to reacti...@googlegroups.com
Which version of Play are you using? According to last API ( http://www.playframework.com/documentation/api/2.1.1/scala/index.html#play.api.libs.json.Json$ ), Json.arr is expecting an arbitrary number of JsValueWrapper (the syntax with a "*" after the type of the parameter).

Since it doesn't specify how many, if you give it a List[JsObject], by default, it will consider you are giving him only one element (which is a list). By writing Json.arr(articles: _*), you tell him to explode your list in several elements and pass them to Json.arr, so now it will return a real array with the same length as your list.

So it should compile in the last version...Otherwise, you can still do "JsArray(articles)" instead of Json.arr.

Regards.

mkr...@trialfire.com

unread,
Jun 24, 2013, 5:15:12 PM6/24/13
to reacti...@googlegroups.com


I am using the latest. Play 2.1.1

Thanks for the explanation. But I'm afraid I'm still a little lost. The map method on futureArticleList expects a function which takes a List[JsObject], this list is futureArticleList (its wrapped in a future but still its a List[JsObject]). Json.arr expects one or more JsValueWrapper and as I understand _* unpacks a list. But that list is of type  List[JsObject] and not List[JsValueWrapper ] and hence why the compiler is complaining.

So I guess the question is how do I turn a List[JsObject] into List[JsValueWrapper ] ??? (Maybe thats not the right question to ask but I'm a bit confused now)

Any help would be greatly appreciated.

-Max

Paul Dijou

unread,
Jun 24, 2013, 7:27:42 PM6/24/13
to reacti...@googlegroups.com
JsValueWrapper is a bit anything that can be cast to a JsValue. For example, a String can be cast to a JsString which extends JsValue so it can be considered as a JsValueWrapper. I'm pretty sure any JsValue is also a JsValueWrapper.

I tried to write Json.arr(Json.obj("key" -> "value"), Json.obj("key" -> "value")), it compile just fine.

Could you copy/paste your code and the compilation error you got?

mkr...@trialfire.com

unread,
Jun 24, 2013, 11:00:43 PM6/24/13
to reacti...@googlegroups.com
package controllers

import org.joda.time.DateTime
import scala.concurrent.Future

import play.api.Logger
import play.api.Play.current
import play.api.mvc._
import play.modules.reactivemongo.{ MongoController, ReactiveMongoPlugin }

import reactivemongo.api.gridfs.GridFS
import reactivemongo.api.gridfs.Implicits.DefaultReadFileReader
import reactivemongo.api.collections.default.BSONCollection
import reactivemongo.bson._
import reactivemongo.api.Cursor
import play.api.libs.json._
import play.modules.reactivemongo.json.collection.JSONCollection
import models.Article
import models.Article._

object Articles extends Controller with MongoController {
  // get the collection 'articles'
  val collection = db[JSONCollection]("articles")
  // a GridFS store named 'attachments'
  //val gridFS = new GridFS(db, "attachments")
  val gridFS = new GridFS(db)

  // let's build an index on our gridfs chunks collection if none
  gridFS.ensureIndex().onComplete {
    case index =>
      Logger.info(s"Checked index, result is $index")
  }

  def index = Action { implicit request =>
    Async {
      
      val cursor: Cursor[JsObject] = collection.find(Json.obj()).cursor[JsObject]

       val futureArticleList: Future[List[JsObject]] = cursor.toList
       val futureArticleListJsonArray: Future[JsArray] = futureArticleList.map { articles =>
        Json.arr(articles: _*);        
      }
      futureArticleListJsonArray.map { articles =>
        Ok(articles)
      }
      
    }
  }
  
}
Error at line 44: "Json.arr(articles: _*);": type mismatch;  found   : List[play.api.libs.json.JsObject]  required: Seq[play.api.libs.json.Json.JsValueWrapper] Application.scala /reactivemongo-demo-app/app/controllers line 41 Scala Problem

Paul Dijou

unread,
Jun 25, 2013, 4:59:26 AM6/25/13
to reacti...@googlegroups.com
Okay, I've worked a bit more on the problem and I'm quite frustrated.

Json.arr(Json.obj("key" -> "value"), Json.obj("key" -> "value")) is fine.
Json.arr(List(Json.obj("key"->"value"), Json.obj("key"->"value")): _*) fails to compile where it should be exactly the same.

It looks like you have reach one limit of the compiler. JsValueWrapper is a mechanism to have implicit conversion from "primitive" types (like String or Boolean) to JsValue so you can write Json.arr("test", true). But going with a List of JsValue seems to crash it.

So go for plan B: replace Json.arr(articles: _*) with JsArray(articles), it should works fine.

mkr...@trialfire.com

unread,
Jun 25, 2013, 3:38:20 PM6/25/13
to reacti...@googlegroups.com
Paul, thanks for your help and patience. That works and gives me the result I desire. 

There still some things about the Play Json libs that I find a bit confusing. I still don't fully comprehend the difference between JsValue and JsObject and when to use which... but i guess that is a discussion for the play group
Reply all
Reply to author
Forward
0 new messages