Is there a way to downcast a GWT AutoBean?
Asked Answered
R

2

6

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.

Repugnance answered 4/10, 2011 at 21:34 Comment(0)
S
4

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!

Synclastic answered 14/6, 2013 at 14:5 Comment(4)
I've had to do similar things with AutoBeans, but I do a full encode to get the Splittable from an AutoBean Splittable split = AutoBeanCodex.encode(AutoBeanUtils.getAutoBean(sourceBean));Harvey
@Harvey I tried AutoBeanCodex.encode(); it works for upcasting, but not for downcasting -- a shame as it'd be an easy way to do it. :) encode() uses the visitor pattern to traverse the bean's properties to generate the new Splittable. Sadly, the source JSON has properties that aren't accessible through that bean, but which are needed to 'downcast' it... and the visitor doesn't see them, so the JSON from encode()'s Splittable doesn't have them. :( ...Hence weirdly getting the Splittable directly and copying it; it decodes correctly since all the information is still there.Synclastic
I see what you're getting at. It now looks to me that both techniques have their use cases. I've been using the method I stated above because most of the time when I'm transforming one autobean into another, I've made changes before doing so. So in that case, I need the full visitor traversal in order to get all of the correct reified info. Slippery slope here since using HasSplittable can be great if you've made no changes, and you KNOW that no changes have been made. Otherwise, you're only choice is to perform the full encode. Unless I'm missing something .....Harvey
Gah, I only just noticed someone had replied. :( I must admit, I don't know how making changes would affect the underlying data. We've never had any problems with that, since the first thing we do when given a DTO that we want to downcast is to work out what the 'real' type is (that is, which subclass), and cast it down to that. After that we don't really need to worry. :)Synclastic
E
0

I'm not very familiar with AutoBean but you probably can use the serializer/deserializer from RestyGWT. It supports polymorphism by making use of annotation.

link to the documentation: http://restygwt.fusesource.org/documentation/restygwt-user-guide.html#Polymorphic_Sub_Types

Empty answered 26/1, 2012 at 14:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.