how to automatically bind to a Map<String, String> instead of binding to a pojo...

1,694 views
Skip to first unread message

sas

unread,
Nov 15, 2010, 9:45:42 PM11/15/10
to play-framework
I wonder if it's possible to automatically bind the following:

<input name="client.name">
<input name="client.surname">
<input name="client.age.">

public static void add( Map<String, String> client )

I don't want to bind a pojo Client class, because in fact I'd like to
use it to filter data, that is in age I can have something like
">20" (to query for people older than 20 years), that means I won't
have a valid number int client.age...

thanks a lot

saludos

sas

sas

unread,
Nov 15, 2010, 9:49:11 PM11/15/10
to play-framework
I found this...

http://groups.google.com/group/play-framework/browse_thread/thread/34a388fe3a1b3f87

seems like custom binding is not quite there yet...

GrailsDeveloper

unread,
Nov 16, 2010, 3:59:16 AM11/16/10
to play-framework
I think he don't want a custom binding. I think he searched for
Binder.bind. Please have a look into the code to understand the
method. The javadoc http://www.playframework.org/documentation/api/1.1/play/data/binding/Binder.html
is not very good at the moment.

Niels

On 16 Nov., 03:49, sas <open...@gmail.com> wrote:
> I found this...
>
> http://groups.google.com/group/play-framework/browse_thread/thread/34...

Guillaume Bort

unread,
Nov 16, 2010, 6:02:17 AM11/16/10
to play-fr...@googlegroups.com
Check here:

http://www.playframework.org/documentation/1.1/releasenotes-1.1#binding

> --
> 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

For anything work-related, use g...@zenexity.fr; for everything else,
write guillau...@gmail.com

sas

unread,
Nov 22, 2010, 8:34:57 PM11/22/10
to play-framework
I kept playing with it, and had a look at the source

I did the Point example and everything worked fine

Then I created the following binder:

package lib.binder;
[imports...]
public class MapBinder implements TypeBinder<Map<String, String>> {

@Override
public Object bind(String name, Annotation[] annotations, String
value,
Class actualClass) throws Exception {

return getMapFromParams(name);
}

private Map<String, String> getMapFromParams(String name) {
Map<String, String> map = new LinkedHashMap<String, String>();
String prefix = name + ".";
String[] values;
String key, value;

for(Map.Entry<String, String[]> entry:
play.mvc.Scope.Params.current().all().entrySet()) {
key = entry.getKey().toString();
if (key.startsWith(prefix)) {
values = entry.getValue();
value = join(asList(values), ", ");
map.put(key, value);
}
}
return map;
}
}

The problem is it never gets executed. In Binder.bindInternal, it uses
isComposite to check it it should be binded to a Bean.

isComposite just checks if there is a param starting with that name +
'.' and the param is not empty, then it asumes it has to use a
BeanWrapper...

In BeanWrapper.registerSetters, a NullPointerException shows up when
executing registerSetters(clazz.getSuperclass(), isScala); being clazz
a java.util.Map...

So the code never gets to directBind to catch my own binder...

I think the framework should give me the chance to bind it (it should
at least check @As(binder=xxx) annotation) before assuming it's a
bean...

I can follow and understand the code, but I wouldn't dare to touch
it...

By the way, this is my controller:

public class CustomBinder extends Controller {

public static void index(
String puntoManual,
@As(binder=PuntoBinder.class) Punto puntoAutomatico,
@As(binder=MapBinder.class) Map<String, String> map ) {

renderArgs.put("puntoManual", parsePunto(puntoManual));

//Map<String, String> map = getMapFromParams("map");

renderTemplate("CustomBinder/index.html", puntoAutomatico, map);
}
...

The PuntoBinder class works fine, and so does the commented code...








On 16 nov, 08:02, Guillaume Bort <guillaume.b...@gmail.com> wrote:
> Check here:
>
> http://www.playframework.org/documentation/1.1/releasenotes-1.1#binding
>
> On Tue, Nov 16, 2010 at 9:59 AM, GrailsDeveloper
>
>
>
>
>
> <opensourc...@googlemail.com> wrote:
> > I think he don't want a custom binding. I think he searched for
> > Binder.bind. Please have a look into the code to understand the
> > method. The javadochttp://www.playframework.org/documentation/api/1.1/play/data/binding/...
> Guillaume Bort,http://guillaume.bort.fr
>
> For anything work-related, use g...@zenexity.fr; for everything else,
> write guillaume.b...@gmail.com

Guillaume Bort

unread,
Nov 23, 2010, 5:35:17 AM11/23/10
to play-fr...@googlegroups.com
Ah yes,

A custom binder is supposed to translate a single HTTP parameter to a
class instance (like binding an URL, Point, BigDecimal, etc.). So it
can't operate an multiple parameters.

Currently you can provide your custom binding mechanism via a plugin,
by overriding this method:

public Object bind(String name, Class clazz, Type type, Annotation[]
annotations, Map<String, String[]> params);

But we should probably offer an easier way to define it. You should
report the issue.

> For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.
>
>

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

For anything work-related, use g...@zenexity.fr; for everything else,

write guillau...@gmail.com

sas

unread,
Nov 23, 2010, 11:46:23 PM11/23/10
to play-framework

sas

unread,
Nov 24, 2010, 9:11:32 PM11/24/10
to play-framework
I decided to implement it as a plugin... and it was damn easy...

this is the MapBinderPlugin

--

package lib.plugins.binders;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Map;

import play.PlayPlugin;

import lib.utils.*;

public class MapBinderPlugin extends PlayPlugin {

@Override
public Object bind(String name, Class clazz, Type type,
Annotation[] annotations, Map<String, String[]> params) {

if (clazz.getName().equals(Map.class.getName())) {
return HttpUtils.filterParam(name, params);
}
return null;
}
}

--

and then in /conf/play.plugins (create it if it's not there) (wouldn't
it be better in application.conf???)

50:lib.plugins.binders.MapBinderPlugin

that's it...

and the filterParam function

package lib.utils;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

import play.mvc.Scope;
import play.templates.JavaExtensions;

public class HttpUtils {

public static Map<String, String> filterParam(String name,
Map<String, String[]> params) {
Map<String, String> map = new LinkedHashMap<String, String>();
String prefix = name + ".";
String key, value;

for(Map.Entry<String, String[]> entry: params.entrySet()) {
key = entry.getKey().toString();
if (key.startsWith(prefix)) {
value = JavaExtensions.join(Arrays.asList(entry.getValue()), ",
");
map.put(key, value);
}
}
return map;
}

public static Map<String, String> filterParam(String name,
Scope.Params params) {
return filterParam(name, params.all());
}
}

saludos

sas


On 24 nov, 01:46, sas <open...@gmail.com> wrote:
> done
>
> http://play.lighthouseapp.com/projects/57987-play-framework/tickets/4...
>
> saludos
>
> sas
>
> On 23 nov, 07:35, Guillaume Bort <guillaume.b...@gmail.com> wrote:
>
>
>
> > Ah yes,
>
> > A custom binder is supposed to translate a single HTTP parameter to a
> > class instance (like binding an URL, Point, BigDecimal, etc.). So it
> > can't operate an multiple parameters.
>
> > Currently you can provide your custom binding mechanism via a plugin,
> > by overriding this method:
>
> > public Object bind(String name, Class clazz, Type type, Annotation[]
> > annotations,Map<String, String[]> params);
>
> > But we should probably offer an easier way to define it. You should
> > report the issue.
>
> > On Tue, Nov 23, 2010 at 2:34 AM, sas <open...@gmail.com> wrote:
> > > I kept playing with it, and had a look at the source
>
> > > I did the Point example and everything worked fine
>
> > > Then I created the following binder:
>
> > > package lib.binder;
> > > [imports...]
> > > public class MapBinder implements TypeBinder<Map<String, String>> {
>
> > >        @Override
> > >        public Object bind(String name, Annotation[] annotations, String
> > > value,
> > >                        Class actualClass) throws Exception {
>
> > >                return getMapFromParams(name);
> > >        }
>
> > >        privateMap<String, String> getMapFromParams(String name) {
> > >                Map<String, String>map= new LinkedHashMap<String, String>();
> > >                //Map<String, String>map= getMapFromParams("map");
>
> > >                renderTemplate("CustomBinder/index.html", puntoAutomatico,map);
> > >        }
> > > ...
>
> > > The PuntoBinder class works fine, and so does the commented code...
>
> > > On 16 nov, 08:02, Guillaume Bort <guillaume.b...@gmail.com> wrote:
> > >> Check here:
>
> > >>http://www.playframework.org/documentation/1.1/releasenotes-1.1#binding
>
> > >> On Tue, Nov 16, 2010 at 9:59 AM, GrailsDeveloper
>
> > >> <opensourc...@googlemail.com> wrote:
> > >> > I think he don't want a custom binding. I think he searched for
> > >> > Binder.bind. Please have a look into the code to understand the
> > >> > method. The javadochttp://www.playframework.org/documentation/api/1.1/play/data/binding/...
> > >> > is not very good at the moment.
>
> > >> > Niels
>
> > >> > On 16 Nov., 03:49, sas <open...@gmail.com> wrote:
> > >> >> I found this...
>
> > >> >>http://groups.google.com/group/play-framework/browse_thread/thread/34...
>
> > >> >> seems like custom binding is not quite there yet...
>
> > >> >> On 15 nov, 23:45, sas <open...@gmail.com> wrote:
>
> > >> >> > I wonder if it's possible to automatically bind the following:
>
> > >> >> > <input name="client.name">
> > >> >> > <input name="client.surname">
> > >> >> > <input name="client.age.">
>
> > >> >> > public static void add(Map<String, String> client )

Guillaume Bort

unread,
Nov 25, 2010, 5:23:54 AM11/25/10
to play-fr...@googlegroups.com
Btw I think we could have this binder natively in Play. Feel free to
add a bug and send us a pull request for that.

> For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.
>
>

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

For anything work-related, use g...@zenexity.fr; for everything else,

write guillau...@gmail.com

sas

unread,
Nov 25, 2010, 10:44:08 PM11/25/10
to play-framework
yes, that would be great...

I'll see what I can do

I'd just need a little help setting up a development environment with
eclipse (that is, mess around with the framework, test it with a play
app, debug the whole thing with eclipse, and run all the test to avoid
breaking stuff)

so far now I'm compiling with ant, and manually restarting the app on
every change...

(having a look at the code, I would add this feature to
play.data.binding.Binder.bindInternal, just before checking for a map,
and I would move the branWrapper.bind after the Collection types...)

see: http://play.lighthouseapp.com/projects/57987-play-framework/tickets/443

saludos

sas

Guillaume Bort

unread,
Nov 26, 2010, 1:47:24 PM11/26/10
to play-fr...@googlegroups.com
> so far now I'm compiling with ant, and manually restarting the app on
> every change...

Yes, unfortunately there is no other way...

> For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.
>
>

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

For anything work-related, use g...@zenexity.fr; for everything else,

write guillau...@gmail.com

sas

unread,
Nov 29, 2010, 8:13:19 AM11/29/10
to play-framework
well, here goes my first patch to play ;-))))

https://github.com/playframework/play/pull/45#files_bucket

BTW, at first I configured just-test-cases project to work with play
source code (I removed play.jar and added the reference to the
project), it worked fine a couple of times but then the project
wouldn't start on first request... with this error:

Internal Error (check logs)

[...]

10:09:34,369 TRACE ~ 0ms to apply play.db.DBPlugin@13c6a22 to
models.threeLevels.Address
10:09:34,490 TRACE ~ 120ms to apply play.db.jpa.JPAPlugin@165a3c2 to
models.threeLevels.Address
10:09:34,504 TRACE ~ serve500: begin
10:09:34,549 TRACE ~ 43ms to load template /app/views/errors/500.html
from cache
10:09:34,639 ERROR ~

@64j6ckbnj
Internal Server Error (500) for request GET /favicon.ico

Oops: DuplicateMemberException
An unexpected error occured caused by exception
DuplicateMemberException: duplicate method: count in
models.threeLevels.Address

play.exceptions.UnexpectedException: While applying
play.db.jpa.JPAPlugin@15c07d8 on models.threeLevels.Address
at play.classloading.ApplicationClasses
$ApplicationClass.enhance(ApplicationClasses.java:215)
at
play.classloading.ApplicationClassloader.loadApplicationClass(ApplicationClassloader.java:
142)
at
play.classloading.ApplicationClassloader.getAllClasses(ApplicationClassloader.java:
423)
at play.Play.start(Play.java:418)
at play.Play.detectChanges(Play.java:532)
at play.Invoker$Invocation.init(Invoker.java:164)
at Invocation.HTTP Request(Play!)
Caused by: javassist.bytecode.DuplicateMemberException: duplicate
method: count in models.threeLevels.Address
at javassist.bytecode.ClassFile.testExistingMethod(ClassFile.java:
593)
at javassist.bytecode.ClassFile.addMethod(ClassFile.java:577)
at javassist.CtClassType.addMethod(CtClassType.java:1235)
at play.db.jpa.JPAEnhancer.enhanceThisClass(JPAEnhancer.java:51)
at play.db.jpa.JPAPlugin.enhance(JPAPlugin.java:95)
at play.classloading.ApplicationClasses
$ApplicationClass.enhance(ApplicationClasses.java:212)
... 6 more
10:09:34,640 ERROR ~

@64j6ckbnm
Error during the 500 response generation

Template not found (In /app/views/errors/500.html around line 9)
The template tags/500.html or tags/500.tag does not exist.

play.exceptions.TemplateNotFoundException: Template not found : tags/
500.html or tags/500.tag
at play.templates.GroovyTemplate
$ExecutableTemplate.invokeTag(GroovyTemplate.java:308)
at /app/views/errors/500.html.(line:9)
at play.templates.GroovyTemplate.render(GroovyTemplate.java:203)
at play.server.PlayHandler.serve500(PlayHandler.java:602)
at Invocation.HTTP Request(Play!)
10:09:34,667 TRACE ~ serve500: end
10:09:34,668 TRACE ~ run: end

any idea???

So I just work with the play.jar, and added the ant task from the Run,
External Tools menu on Eclipse...

saludos

sas
> >> >                        Annotation[] annotations,Map<String, String[]> params) {
>
> >> >                if (clazz.getName().equals(Map.class.getName())) {
> >> >                        return HttpUtils.filterParam(name, params);
> >> >                }
> >> >                return null;
> >> >        }
> >> > }
>
> >> > --
>
> >> > and then in /conf/play.plugins (create it if it's not there) (wouldn't
> >> > it be better in application.conf???)
>
> >> > 50:lib.plugins.binders.MapBinderPlugin
>
> >> > that's it...
>
> >> > and the filterParam function
>
> >> > package lib.utils;
>
> >> > import java.util.Arrays;
> >> > import java.util.LinkedHashMap;
> >> > import java.util.Map;
>
> >> > import play.mvc.Scope;
> >> > import play.templates.JavaExtensions;
>
> >> > public class HttpUtils {
>
> >> >        public staticMap<String, String> filterParam(String name,
> >> >Map<String, String[]> params) {
> >> >                Map<String, String>map= new LinkedHashMap<String, String>();
> >> >                String prefix = name + ".";
> >> >                String key, value;
>
> >> >                for(Map.Entry<String, String[]> entry: params.entrySet()) {
> >> >                        key = entry.getKey().toString();
> >> >                        if (key.startsWith(prefix)) {
> >> >                                value = JavaExtensions.join(Arrays.asList(entry.getValue()), ",
> >> > ");
> >> >                                map.put(key, value);
> >> >                        }
> >> >                }
> >> >                returnmap;
> >> >        }
>
> >> >        public staticMap<String, String> filterParam(String name,
> ...
>
> leer más »

Guillaume Bort

unread,
Nov 30, 2010, 4:49:57 AM11/30/10
to play-fr...@googlegroups.com
Thank you I'll check that.

Reply all
Reply to author
Forward
0 new messages