Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to downcast a GWT AutoBean?

I've been using AutoBeans to map JSON data coming from a non GWT-RPC Java based web service. Everything has been working so far except for one mapping.

On the server side, the Class has a property of type Map where MyAbstractParentObject is the parent class of about 15 different child classes.

When I map that to a corresponding AutoBean interface on the client I'm not able to downcast MyAbstractParentObject to its child type after it's been decoded. I looked all over the GWT docs and 'the Googles' to see if AutoBeans even has polymorphic support but couldn't get an answer either way. Interceptors and Categories don't seem to be able handle this, just methods they want to exist in the interface that aren't getters/setters.

I was attempting to do a workaround using the type field in the JSON data to create an instance of the child class but the AutoBean does not give me access to the raw JSON, even though in the debugger I can see it as a protected field called 'data'. If I try to decode the original bean it will only have the fields in the MyAbstractParentObject.

The only alternatives I can see are to:

  1. Extend or create my own AutoBeanCodex that can properly handle the children of MyAbstractParentObject when it decodes the JSON.
  2. Find a way to get to the raw JSON in the MyAbstractParentObject AutoBean and use that to create and instance of the child class on the fly.
  3. Switch to some other JSON-GWT Serialization framework like GWTProJSONSerializer or piriti.

Any help would be appreciated.

like image 557
timmyonline Avatar asked Oct 04 '11 21:10

timmyonline


1 Answers

I know this was asked a long time ago, but I struggled to find an answer too. I realized that the AutoBeans, since they're basically just fancy wrappers for the JSON, still contain all the data for the fields of the child object you want to downcast it to. So I wrote a method like this:

public <A, B> B cast( A sourceObject, Class<B> targetClass )
{
    AutoBean<A> sourceBean = AutoBeanUtils.getAutoBean( sourceObject ); // Get the corresponding AutoBean.
    HasSplittable splittableBean = ( HasSplittable ) sourceBean;       // Implementation (if still AbstractAutoBean) supports this interface ;)
    Splittable splittable = splittableBean.getSplittable().deepCopy(); // If you don't copy it, decode() tries to be clever and returns
                                                                       // the original bean!
    AutoBean<B> targetBean = AutoBeanCodex.decode( typeFactory, targetClass, splittable ); // Create new AutoBean of
                                                                                           // the target type.
    return targetBean.as(); // Get the proxy for the outside world.
}

--Where typeFactory extends AutoBeanFactory, as you can see.

It's worked well enough for me. The trickiest bit was the cast to HasSplittable, since AutoBean doesn't extend that interface, but AbstractAutoBean (which implements AutoBean) does -- and a subclass of that is what's returned by calls to getAutoBean().

You also need to copy the Splittable, otherwise AutoBeanCodex thinks, "hey, I already have an AutoBean for that Splittable! Here you go!" -- and just gives you the original. ;)

Anyway, you can cast downwards, upwards...sideways! :P

Late edit: Stumbling upon this again months later, I figured I'd add a small caveat about something Jonathan mentioned below. The method I've described here is designed to be used on an AutoBean that hasn't been modified since it was deserialized. That's because (AFAIK) there's no guarantee that any setters you call will actually update the JSON (needed for the casting). This probably isn't a big deal, since typically you'll use this when you have an incoming DTO and you want to cast it to its real type ASAP, before doing anything else with it. In our case, none of our AutoBeans even had setters, so it wasn't really an issue. ;)

After you've cast it, you can do whatever you want with the resulting bean, which is fresh out of the factory after all!

like image 135
Dark_Eternal Avatar answered Oct 18 '22 16:10

Dark_Eternal