Scala renderJSON

421 views
Skip to first unread message

Vincent Buzzano

unread,
Jul 15, 2010, 11:52:36 AM7/15/10
to play-framework
Hello,

I get a problem using Scala and trying to do a renderJSON on a list


I get all contacts and then try to render the list as JSON
> var contacts = asScala[Contact].findAll
> renderJSON(contacts)

I get this exception

Execution exception (In /app/controllers/Contacts.scala around line
48)
UnsupportedOperationException occured : Expecting parameterized type,
got class scala.collection.immutable.$colon$colon. Are you missing
the use of TypeToken idiom? See
http://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener

play.exceptions.JavaExecutionException: Expecting parameterized type,
got class scala.collection.immutable.$colon$colon.
Are you missing the use of TypeToken idiom?
See http://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:259)
at Invocation.HTTP Request(Play!)
Caused by: java.lang.UnsupportedOperationException: Expecting
parameterized type, got class scala.collection.immutable.$colon$colon.
Are you missing the use of TypeToken idiom?
See http://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener
at com.google.gson.TypeInfoFactory.getActualType(TypeInfoFactory.java:
97)
at
com.google.gson.TypeInfoFactory.extractRealTypes(TypeInfoFactory.java:
116)
at com.google.gson.TypeInfoFactory.getActualType(TypeInfoFactory.java:
65)
at
com.google.gson.TypeInfoFactory.getTypeInfoForField(TypeInfoFactory.java:
54)
at
com.google.gson.ObjectNavigator.navigateClassFields(ObjectNavigator.java:
148)
at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:123)
at
com.google.gson.JsonSerializationContextDefault.serialize(JsonSerializationContextDefault.java:
56)
at com.google.gson.Gson.toJsonTree(Gson.java:230)
at com.google.gson.Gson.toJson(Gson.java:315)
at com.google.gson.Gson.toJson(Gson.java:270)
at com.google.gson.Gson.toJson(Gson.java:250)
at play.mvc.results.RenderJson.<init>(RenderJson.java:20)
at play.mvc.Controller.renderJSON(Controller.java:280)
at play.mvc.ControllerDelegate.renderJSON(ControllerDelegate.java:58)
at controllers.Contacts$.getContacts(/app/controllers/Contacts.scala:
48)
at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:
374)
at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:
357)
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:162)
... 1 more


it's work when i render only one contact
> var contacts = asScala[Contact].findAll
> renderJSON(contacts(0))


Do you have any idea ?

Liu Yongjian

unread,
Jul 15, 2010, 12:08:17 PM7/15/10
to play-fr...@googlegroups.com
I have the same question about that .

According to the gson document , you need to define a TypeToken  if the object that your are serializing/deserializing is a ParameterizedType (i.e. contains at least one type parameter and may be an array) then you must use the toJson(Object, Type) or fromJson(String, Type) method. 

Here is an example for serializing and deserialing a ParameterizedType picked from gson docs

 Type listType = new TypeToken<List<String>>() {}.getType();
 List<String> target = new LinkedList<String>();
 target.add("blah");

 Gson gson = new Gson();
 String json = gson.toJson(target, listType);
 List<String> target2 = gson.fromJson(json, listType);
But it does not work properly in scala.collection.immutable.List, so you can only use array instead - it seams the array in Scala is the same as the one in Java 



--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To post to this group, send email to play-fr...@googlegroups.com.
To unsubscribe from this group, send email to play-framewor...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.


dirk

unread,
Dec 6, 2010, 2:29:21 PM12/6/10
to play-fr...@googlegroups.com
> UnsupportedOperationException occured : Expecting parameterized type,
> got class scala.collection.immutable.$colon$colon. Are you missing
> the use of TypeToken idiom?

Here's a solution to the problem using a custom serializer:

// Your action
def search(query: String) = Json(MySerializer.toJson(User.find("name like ?", query)))


object MySerializer {
    val builder = new GsonBuilder()
    builder.registerTypeAdapter(classOf[scala.collection.immutable.$colon$colon[_]], new CollectionSerializer)
    val gson = builder.create
   
    def toJson(obj: Any) = gson.toJson(obj)
}

// Custom serializer
// http://sites.google.com/site/gson/gson-user-guide#TOC-Custom-Serialization-and-Deserializ
class CollectionSerializer extends JsonSerializer[scala.collection.immutable.$colon$colon[_]] {
    override def serialize(items: scala.collection.immutable.$colon$colon[_], objType: Type, context: JsonSerializationContext): JsonElement = {
        val json = new JsonArray()
        items.foreach(item => json.add(context.serialize(item)))
        return json
    }
}

Loic

unread,
Apr 12, 2011, 9:09:22 AM4/12/11
to play-fr...@googlegroups.com
I have this  problem too, maybe we should open a bug

Dirk

unread,
Apr 12, 2011, 10:40:08 AM4/12/11
to play-fr...@googlegroups.com, Loic
I'm currently working on getting lift-json working with play/scala:
http://groups.google.com/group/play-framework/browse_thread/thread/dd6544395d213f4f/c84890f7a07db042?lnk=gst&q=json+dirk#c84890f7a07db042
http://groups.google.com/group/liftweb/browse_thread/thread/b6d5a00605dd9e36/c6f3a09c9e5e9d39?pli=1


On Tue, Apr 12, 2011 at 9:09 AM, Loic <loic.d...@gmail.com> wrote:
I have this  problem too, maybe we should open a bug

--

Loic

unread,
Apr 13, 2011, 3:49:23 AM4/13/11
to play-fr...@googlegroups.com, Loic
There is a method for that in the play scala API, no need to use lift-json I think.

According to the Scala module documentation, I guess this should work without error : 

def listByApi = Json(Contacts.findAll())

But it reponds an error :

{
    type:   'play.exceptions.JavaExecutionException',
    message: 'Expecting parameterized type, got class scala.collection.immutable.$colon$colon.
 Are you missing the use of TypeToken idiom?
 See http://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener'
}

Guillaume Bort

unread,
Apr 13, 2011, 3:54:24 AM4/13/11
to play-fr...@googlegroups.com
That why we need Lift-JSON. GSON is not able to serialize correctly scala types.

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To post to this group, send email to play-fr...@googlegroups.com.
To unsubscribe from this group, send email to play-framewor...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.



--
Guillaume Bort, http://guillaume.bort.fr

v6ak

unread,
Apr 13, 2011, 4:23:57 PM4/13/11
to play-framework
It's simple:

import net.liftweb.json.ParameterNameReader
import java.lang.reflect.Constructor
import play.classloading.enhancers.LocalvariablesNamesEnhancer
import scala.collection.JavaConversions._

object PlayParameterReader extends ParameterNameReader{
def lookupParameterNames(constructor: Constructor[_]) =
LocalvariablesNamesEnhancer.lookupParameterNames(constructor)
}

Then you need to use this paranamer instead of the default one in
Formats. There is one way that allows it:
https://gist.github.com/891235

Regards
Vít Šesták 'v6ak'

On Apr 12, 4:40 pm, Dirk <dirkm...@gmail.com> wrote:
> I'm currently working on getting lift-json working with play/scala:http://groups.google.com/group/play-framework/browse_thread/thread/dd...http://groups.google.com/group/liftweb/browse_thread/thread/b6d5a0060...

Guillaume Bort

unread,
Apr 14, 2011, 4:44:50 AM4/14/11
to play-fr...@googlegroups.com
We really need Lift-JSON working with Play Scala as GSON won't work for most Scala specific structure. If you have a working version, please send us a pull request.

Dirk

unread,
Apr 15, 2011, 12:33:10 PM4/15/11
to play-fr...@googlegroups.com
I'm almost there, just trying to iron out a few remaining issues with lift-json. For example it wasn't able to correctly serialize a model object until yesterday:
http://groups.google.com/group/liftweb/browse_thread/thread/96d3bd870b731f3b/a5d5542da7952320

v6ak

unread,
Apr 16, 2011, 12:49:06 PM4/16/11
to play-fr...@googlegroups.com
In fact, I don't have integrated it well. I just have solved the issue with loading parameter names according to a dicsussion thread. The problem was probably that Lift JSON expects serialized classes to be on classpath (unless you provide a custom ParameterNameReader), but Play! uses a different classloading mechanism. (This is possibly not true in the production mode.)

I'm not sure about the right Play integration. Like any advanced (de)serializer, Lift suports some configuration. (In fact, you can handle polymorphism, of you configure it.) The configuration is described in an instance of net.liftweb.json.Formats.

I've some ideas: https://gist.github.com/923233 (simple by default, complex and robust on demand)

* Constructor parameter vs. method: Constructor parameter seems to be a cleaner way for me.
* It is possible to use constructor overloading for convenience. So, Controller can have constructors that accepts both of variants (either net.liftweb.json.Formats or something like play.json.JsonAdapter).
* Combining availbility of constructor parameter and protected method seems to be odd for me. Parameters could be overriden in such case by a trait. A similar API design fail have been done in java.lang.Thread, which implements java.lang.Runnable. Although there is a different issue (you can accidentaly call run() instead of start()), both of these designs are crazy.
* Should Play! "defensively" force an instance of PlayParameterReader? There are some advantages and disadvantages on both sides:
* * If Play! does not force the PlayParameterReader (or PlayParameterNameReader), it can lead to some serialization issues (the result of the serialization is "{}") if the user speciffies some non-default Formats and does not choose to use PlayParameterReader.
* * If Play! forces the PlayParameterReader, it can lead to similar iussues if user uses a specific ParameterNameReader on purpose. Fortunately, it is an edge case. However, this seems to be a magic for me.
* * It is also possible to use one of these ParameterNameReader-s by default and the other as a fallback, but there is still some magic.
* * It is also possible to allow user to switch overriding ParameterNameReader off, which can be the best way.

What do you think about it?

Regards
Vít Šesták 'v6ak'

Dirk

unread,
Apr 16, 2011, 9:15:30 PM4/16/11
to play-fr...@googlegroups.com
The reason they added the configuration option to use another parameter name reader is because I asked for it so that lift-json could be used with play:
http://groups.google.com/group/liftweb/browse_thread/thread/b6d5a00605dd9e36/c6f3a09c9e5e9d39?pli=1
So we simply define a play ParameterNameReader that uses play's classloading mechanism. I'll do this within the scala plugin itself so that play users don't need to worry about it.
I'm currently working on it, but as I mentioned in my last post I'm just waiting for them iron out a couple of problems with the serialization:
http://groups.google.com/group/liftweb/browse_thread/thread/96d3bd870b731f3b/a5d5542da7952320


--

v6ak

unread,
Apr 17, 2011, 8:06:59 AM4/17/11
to play-fr...@googlegroups.com

RI understand that Play users don't need to worry about it in simple cases. However, one may need a custom Formats (for typeHinth etc.). In such case, the behavior is a tradeoff between simplicity and magic.

Regards,
Vít Šesták 'v6ak'

Dirk

unread,
Apr 18, 2011, 11:13:00 AM4/18/11
to play-fr...@googlegroups.com
Yes you're right the developer should be allowed to override the default behaviour. I will make the Play ParameterNameReader object public so that it can be re-used. In the case that the developer wants to use a completely different library, he can produce the required json as a string and pass that to Json()


Regards,
Vít Šesták 'v6ak'

Reply all
Reply to author
Forward
0 new messages