Genson Caching of Bean Accessors

35 views
Skip to first unread message

MDD

unread,
Nov 16, 2015, 1:13:37 PM11/16/15
to Genson user group

Hi, I have already posted the question at SO (http://stackoverflow.com/questions/33735036/genson-caching-of-bean-accessors) but have not got an answer yet. This is fairly urgent so it would be excellent if we could find a solution to this soon.


I have recently upgraded to Genson 1.3 and I am not 100% sure if this issue is new or not as previously I patched the 0.98 version to make it work.


Context

We are using our own implementation of the BeanMutatorAccessorResolver. This is so that we can dynamically decide whether a property should be serialized or not. Basically we have integrated Genson into our generic jersey REST API interface. Genson does all the serializing and deserializing. When doing a GET requests it is possible for a user to pass fields in the URL in order to filter those he specifically needs (especially for large objects this is necessary where you only need 3 fields or so for displaying a table overview). For example: ?fields=field1, field2, field3. We then know in our implementation of BeanMutatorAccessorResolver exactly which fields to serialize and which ones to ignore. This is mainly intended for speeding up requests and parsing as we are then working with less data.


Problem

Unfortunately it seems that once Genson has read in all the fields via reflection or whatever, it caches that. This would be no problem if we were always requesting the same fields. Unfortunately on some occasions we need more fields then before, but because Genson does not visit our BeanMutatorAccessorResolver a second time it only returns the few fields that it has already cached.


Is there anyway around this?

Eugen Cepoi

unread,
Nov 16, 2015, 1:36:15 PM11/16/15
to gen...@googlegroups.com
Hi,

Mh, so it was working with the old 0.98 but not with latest? Or it never worked? Anyway this is not the best way to do this filtering.
As actually what you want is have everything be resolved once and at runtime just exclude some fields, you don't want to do the resolution of properties again, which as you say would hurt the performances a lot.

I think the right solution would be to provide a custom BeanDescriptor implementation and do the filtering in there at runtime during ser/de. You can store the list of properties to exclude/include in the current context and then use them in the BeanDescriptor. To provide your own instance of BeanDescriptor you just need to override create method.

Let me know if you need more help with that.

Cheers,
Eugen

--
You received this message because you are subscribed to the Google Groups "Genson user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to genson+un...@googlegroups.com.
To post to this group, send email to gen...@googlegroups.com.
Visit this group at http://groups.google.com/group/genson.
To view this discussion on the web visit https://groups.google.com/d/msgid/genson/6a3a965b-c679-418a-b05a-cb7012e0bbe2%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

MDD

unread,
Nov 16, 2015, 1:58:46 PM11/16/15
to Genson user group
Thanks, I will have a look. I think the patch I made at the time was something completely different. That actually works now in genson 1.3. Actually I think the problem before may have been more of a Jersey problem. For some reason Genson was behaving differently when calling the serialize method directly as opposed to via Jersey, which I think just had something to do with the parameters being passed to the serialize method - especiallly the generic type parameter. After creating my own Jersey AbstractResourceMethod and RequestDispatcher implementations it worked better. But anyway that was another issue :-). I will have a look at your suggestions below now. A custom BeanDescriptor implementation sounds like a good solution.  I'll let you know if it helped.

Thanks,
Michael

MDD

unread,
Nov 16, 2015, 3:34:51 PM11/16/15
to Genson user group
Hi Eugen,

I have now implemented my own BeanDescriptorProvider and BeanDescriptor and have removed unnecessary code in my custom MutatorAccessorResolver so that runtime property filtering is not done too early. This sounds like a really nice solution. The only thing is that I have not fully understood where to register my custom BeanDescriptorProvider yet. Do I add it to new GensonBuilder().withConverters(...) ?

Thanks for your help!

Michael

Eugen Cepoi

unread,
Nov 16, 2015, 4:29:43 PM11/16/15
to gen...@googlegroups.com
Hey Michael,

To register one you have to provide a GensonBundle like the one for scala, have a look here.
Basically you extend GensonBundle and implement configure method and override createBeanDescriptorProvider, which is the important part. This allows you to have access to all the things the povider needs to work properly.
Once this is done you can register your bundle to GensonBuilder using withBundle method.

MDD

unread,
Nov 16, 2015, 4:56:34 PM11/16/15
to Genson user group
Thanks very much. I have almost completed the Bundle class. I am just wondering - do I need to implement the configure method or will the values be taken from the builder if not imeplemented? If I do ... should I just do it exactly like in the scala version?

Thanks!

Eugen Cepoi

unread,
Nov 16, 2015, 5:01:37 PM11/16/15
to gen...@googlegroups.com
You can leave it empty. The configure method is there so you can group a bunch of genson customizations together in a single place and enable all of them at once. This is how Genson provides extensions for other apis like JodaTime, Jsonp etc.

MDD

unread,
Nov 16, 2015, 5:29:53 PM11/16/15
to Genson user group
Hi Eugen,

I have implemented the whole flow now and Genson is using my classes. What an awesome way to extend Genson :-). I think I may have made a mistake though. Basically I added the filtering to the BeanDescriptorProvider and now it seems that Genson is only calling  that method once. 

 protected <T> BeanDescriptor<T> create(Class<T> forClass, Type ofType, BeanCreator creator,
 
List<PropertyAccessor> accessors, Map<String, PropertyMutator> mutators, Genson genson)
 
{
 
return new DefaultBeanDescriptor<T>(forClass, getRawClass(ofType), filterPropertyAccessors(accessors), mutators, creator, genson.failOnMissingProperty());

 
}



I realize now though that you metioned earlier to "store the list of properties to exclude/include in the current context and then use them in the BeanDescriptor". Could you explain this a bit further? I assume you mean that I override the serialize() method in my custom BeanDescriptor class. What I do not understand yet - and I think this is the missing key to solving this - is how and where do I add these properties to the context. If it is just a case of updating the context object and then passing it the the super-method serialize(), then which method do I need to call on the context-object to filter the property accessors that I do not want? There was no method like "ignore" or something like that, so I am not sure what to call.

MDD

unread,
Nov 16, 2015, 5:51:20 PM11/16/15
to Genson user group
Hi Eugen,

The answer is in BeanDescriptor.serialize() ... I should have had a look at the super-method before ... thanks!

Best regards,
Michael

Eugen Cepoi

unread,
Nov 16, 2015, 6:01:46 PM11/16/15
to gen...@googlegroups.com
Yeah the create method is called once at the begining to create the beandescriptor for that specific type, so it is not the place to have the filtering logic.

Basically you need to override the serialize and deserialize methods of BeanDescriptor and implement them your self (though this is a bit ugly as you will duplicate some logic). Then you code there your logic of filtering. Now you will wonder how do I get the field names from the query inside the serialize method? So to do this, one way is to instantiate your self the Context and store in there the field names and then invoke genson to do the ser/de using this context. You maybe already do this if you have a custom integration with Jersey.

I have opened this issue as I think that some parts of the code that you need to write could be part of Genson. If you feel to contribute, this could be a great feature. First you would need to define some interface or abstract class named RuntimePropertyFilter and add it to the BeanDescriptorProvider so it then passes it to the BeanDescriptor. Then in the BeanDescriptor you would call shouldSkip method of that filter to know if you must or not skip the current property. Then users would provide their own implementation of that filter if they want to do some fancy stuff. You could even contribute your filtering logic based on url parameters in the jersey extension package.



MDD

unread,
Nov 16, 2015, 6:11:20 PM11/16/15
to Genson user group
Hi Eugen,

Here is my final anwer of the day. It works perfectly just as you described. I have simply added an if-condition before the property is serialized:

 if (!isIgnore(accessor.getName()))
     accessor
.serialize(obj, writer, ctx);


It is easy when you know how :-). It is fairly rare that a library lets you make so many adaptions to suit your needs! Thanks for the nice work and your help today!

Best regards,
Michael

Eugen Cepoi

unread,
Nov 16, 2015, 6:15:05 PM11/16/15
to gen...@googlegroups.com
Great, I am glad it worked well for you!
I will probably add some stuff to make it easier for others to do the same.
How are you passing the field names to your implementation of BeanDesciptor? Is it through the context or using a threadlocal, or something else?

Cheers,
Eugem

MDD

unread,
Nov 17, 2015, 3:37:12 AM11/17/15
to Genson user group
Hi Eugen,

Currently I am solving this with a ThreadLocal variable which contains includes and excludes, as I already had this with the previous solution.

Thanks very much for your time!

Best regards,
Michael

eugen

unread,
Dec 25, 2015, 4:10:52 AM12/25/15
to Genson user group
Hey Michael,

I just pushed some improvements to Genson that handle this use case. This is available on master branch, here is an example on how to use it with jersey.

UrlQueryParamFilter filter = new UrlQueryParamFilter();
Genson genson = new GensonBuilder().useRuntimePropertyFilter(filter).create();
new ResourceConfig()
 
.register(new GensonJaxRSFeature().use(genson))
 
.register(filter);

Please let me know if it works well for your use case as basically this is what you have described above.

Cheers,
Eugen
Reply all
Reply to author
Forward
0 new messages