Page 2 of 2 FirstFirst 12
Results 11 to 19 of 19

Thread: ModelType alternatives for JSON stores

  1. #11
    Sencha User
    Join Date
    Jun 2008
    Posts
    17

    Default

    So, I started to play around with the gxt-3.0.0-SNAPSHOT up on oss.sonatype repo but the API was so much different than 2.2.5 that I reverted instead of refactoring everything I had in place already. Anyway, I'll get back to testing that stuff later in a green project.

    So, where I landed tonight was with a combination of AutoBean and BaseModelData. Probably not the most hugely performant code but I think it more or less gets me where I wanted to go w/out much fuss: JSON with nested objects in GXT 2.2.5. I've subclassed JsonLoadResultReader and used AutoBean to populate BaseModelData instances. This at least supports nested objects and requires an interface which if I'm thinking right, my domain models would implement to keep refactorings safe. I know the below implementation currently only supports arrays but that I can address later since this is just a proof of concept.

    Code:
        public class MyJsonLoadResultReader<D, T> extends JsonLoadResultReader<D>{
        private AutoBeanFactory autoBeanFactory;
        private Class<T> autoBeanInterface;
    
    
    
    
        public MyJsonLoadResultReader(AutoBeanFactory autoBeanFactory, Class<T> autoBeanInterface)
        {
            super(null);
            this.autoBeanFactory = autoBeanFactory;
            this.autoBeanInterface = autoBeanInterface;
        }
    
    
    
    
        @Override
        public D read(Object loadConfig, Object data)
        {
            JSONValue jsonRoot = JSONParser.parseStrict((String) data);
            JSONArray root = jsonRoot.isArray();
            ArrayList<ModelData> models = new ArrayList<ModelData>();
            for (int i = 0; i < root.size(); i++)
            {
                JSONObject obj = (JSONObject) root.get(i);
    
    
                AutoBean<T> bean = AutoBeanCodex.decode(autoBeanFactory, autoBeanInterface, obj.toString());
                models.add(new BaseModelData(AutoBeanUtils.getAllProperties(bean)));
            }
    
    
            return (D) createReturnData(loadConfig, models, models.size());
        }
    }

    Now to do something about the ColumnConfigs... I saw 3.0.0 introduced a new strategy for this but I'll need something for 2.5.5 too:

    Code:
            PersonAutoBeanFactory factory = GWT.create(PersonAutoBeanFactory.class);
    
    
            RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, "api/people/list");
            MyJsonLoadResultReader<ListLoadResult<BeanModel>, IPerson> reader =
                    new MyJsonLoadResultReader<ListLoadResult<BeanModel>, IPerson>(factory, IPerson.class);
    
    
            HttpProxy proxy = new HttpProxy(builder);
    
    
            final BaseListLoader<ListLoadResult<ModelData>> loader = new BaseListLoader<ListLoadResult<ModelData>>(proxy, reader);
            loader.load();
    
    
            List<ColumnConfig> columnConfigs = new ArrayList<ColumnConfig>();
            columnConfigs.add(new ColumnConfig("firstName", "First Name", 200));
            columnConfigs.add(new ColumnConfig("lastName", "Last Name", 200));
            columnConfigs.add(new ColumnConfig("dob", "Date of Birth", 200));
            columnConfigs.add(new ColumnConfig("addresses", "Addresses", 200));
    
    
            ColumnModel columnModel = new ColumnModel(columnConfigs);
    
    
            ListStore<ModelData> store = new ListStore<ModelData>(loader);
            Grid<ModelData> peopleGrid = new Grid<ModelData>(store, columnModel);
            peopleGrid.getView().setEmptyText("No people found.");
            peopleGrid.setLoadMask(true);
            peopleGrid.setHeight(400);
    Last edited by rynam0; 4 Nov 2011 at 4:44 AM. Reason: update code examples

  2. #12
    Sencha User
    Join Date
    Jun 2008
    Posts
    17

    Default

    Yeah, I jumped the gun on this one... Nested objects still are not quite right (among other things). Back to the drawing board.

  3. #13
    Sencha User
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,737
    Answers
    109

    Default

    We're shortly going to be heading deeper than you had intended to go with this... but try using the AutoBean.visit command to traverse the object and build out the ModelData instance with nested instances. GXT 3 has several AutoBeanVisitor examples you could take a look at to see how you might do this, and GWT has a few more internally. They aren't exactly light reading - I tend to be bad at reading iterative visitors, but I'm fairly certain its the best way to do what you are after.

    I wouldn't be too terribly concerned about perf for the copying stuff itself - this shouldn't be much worse than O(n) on the size of your objects. A case like BeanModel doesn't do the copying up front, but does a lookup when the sub properties are actually requested, which in the long run can actually be worse.

    I has assumed this was a green project from the introduction of json<=>beans, as the rest of your project probably has another way of solving this issue, or I wouldn't have suggested 3. GXT 3, for all the changes it makes and 2.x compatibility it breaks, still functions in the same basic ways, but is much more interoperable with mainstream GWT.

  4. #14
    Sencha User
    Join Date
    Jun 2008
    Posts
    17

    Default

    Quote Originally Posted by Colin Alworth View Post
    We're shortly going to be heading deeper than you had intended to go with this... but try using the AutoBean.visit command to traverse the object and build out the ModelData instance with nested instances. GXT 3 has several AutoBeanVisitor examples you could take a look at to see how you might do this, and GWT has a few more internally. They aren't exactly light reading - I tend to be bad at reading iterative visitors, but I'm fairly certain its the best way to do what you are after.
    Haha, yeah I can see that. AutoBean.visit sounds like something I should take a glance at but I have to wonder: is there a target release date set for GXT3? Obviously you guys are already working on a more sound AutoBean integration and it would seem I am really just reinventing the wheel where that is concerned (even if it does target 2.2.5 instead).

    Quote Originally Posted by Colin Alworth View Post
    I wouldn't be too terribly concerned about perf for the copying stuff itself - this shouldn't be much worse than O(n) on the size of your objects. A case like BeanModel doesn't do the copying up front, but does a lookup when the sub properties are actually requested, which in the long run can actually be worse.
    Ok, good to know.

    Quote Originally Posted by Colin Alworth View Post
    I has assumed this was a green project from the introduction of json<=>beans, as the rest of your project probably has another way of solving this issue, or I wouldn't have suggested 3. GXT 3, for all the changes it makes and 2.x compatibility it breaks, still functions in the same basic ways, but is much more interoperable with mainstream GWT.
    Yeah, this is really a proof of concept that is helping me decide which way to go for future projects. To give you some background, I'm targeting EJB3 with JPA and want to keep the app as portable as possible and without introducing unecessary dependencies. I don't want to tie the team to a specific JPA provider and I haven't really been too excited about DTOs (when Hibernate is concerned). Gilead just rubs me wrong for a couple of reasons. I thought JAX-RS services might be the way to go forward.
    I had enough UI written already that refactoring for GXT3 was enough of a pain to want to start a GXT3 proof of concept project separately. Package renaming and API changes made it difficult to quickly switch out 2.2.5 for 3.0.0. That being said, would it be worth waiting for 3.0.0 or is this quite a ways out?

    As always, many thanks for your input!

  5. #15
    Sencha User
    Join Date
    Jun 2008
    Posts
    17

    Default

    GXT 3 has several AutoBeanVisitor examples you could take a look at to see how you might do this, and GWT has a few more internally.
    Would you happen to know where I might find these? I'm not having much luck finding docs on AutoBeanVisitor.
    Thanks!

  6. #16
    Sencha User
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,737
    Answers
    109

    Default

    Like most of the really interesting (i.e. not drawing) stuff in GWT, it is almost totally without documentation. If using eclipse, there is a 'Find All References' (cmd-shift-g or ctrl-shift-g usually) command you can use to find all the subclasses. The "Type Hierarchy" view can also be useful for this.

    That said, here are some examples. Warning, difficult reading ahead (assuming 2.4.0):
    com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.PropertyCoderCreator - seems to track which beans have been seen yet, and watch which types need to be encoded/decoded
    com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.PropertyGetter - traverses data and writes it out as json
    com.google.web.bindery.requestfactory.server.Resolver.PropertyResolver - server only class! seems used to copy RequestFactory entities into proxy autobeans
    com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext:879 - anonymous class to turn RF messages into data
    com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext:1006 - anonymous class to clone autobeans - there is no non-deprecated built-in mechanism to do this, and this is designed for RF anyway, so grain of salt
    com.google.web.bindery.autobean.shared.AutoBeanUtils:128 - walks a pair of beans, finding differences and returning them


    GXT 3 contains a few too (taken from DP5), based on my reading of the GWT, and a number of unit tests:
    com.sencha.gxt.data.client.writer.XmlWriter.PropertyGetter - turns an autobean into a xml document using some simple xpath properties
    com.sencha.gxt.data.shared.writer.AutoBeanWriter:64 - used to wrap non-autobeans in an autobean so it can be passed through various autobean-writing mechanisms
    com.sencha.gxt.data.client.writer.UrlEncodingWriter:66 - reads key:value pairs from an autobean and writes it out in a urlencoded fashion

    Heavy reading, good luck .

  7. #17
    Sencha User
    Join Date
    Jun 2008
    Posts
    17

    Default

    Colin: As always, you pointed me to exactly what I needed to make some more progress. The AutoBeanUtils source was what I've based my newest reader on and I've managed to actually end up with nested ModelData instances this way. Now if only there were a way to instead pass AutoBeans or interfaces as type parameters to Stores instead of ModelData I would be able to avoid the Visitor, casting, and string properties in calls like this:

    Code:
    (List<ModelData>) selected.get("addresses")
    Anyway, thanks a million for all of your help. I've included my mostly working implementation below:

    Code:
    public class AutoBeanJsonLoadResultReader<D, T> extends JsonLoadResultReader<D>
    {
    
    
        private AutoBeanFactory autoBeanFactory;
        private Class<T> autoBeanInterface;
    
    
    
    
        public AutoBeanJsonLoadResultReader(AutoBeanFactory autoBeanFactory, Class<T> autoBeanInterface)
        {
            super(null);
            this.autoBeanFactory = autoBeanFactory;
            this.autoBeanInterface = autoBeanInterface;
        }
    
    
    
    
        @Override
        public D read(Object loadConfig, Object data)
        {
            JSONValue jsonRoot = JSONParser.parseStrict((String) data);
            JSONArray root = jsonRoot.isArray();
    
    
            ArrayList<ModelData> models = new ArrayList<ModelData>();
            for (int i = 0; i < root.size(); i++) {
                JSONObject obj = (JSONObject) root.get(i);
    
    
                AutoBean<T> bean = AutoBeanCodex.decode(autoBeanFactory, autoBeanInterface, obj.toString());
                ModelDataAutoBeanVisitor visitor = new ModelDataAutoBeanVisitor();
                bean.accept(visitor);
    
    
                models.add(visitor.getModelData());
            }
    
    
            return (D) createReturnData(loadConfig, models, models.size());
        }
    
    
    
    
    
    
        /** An AutoBeanVisitor for AutoBean to ModelData conversion. */
        private class ModelDataAutoBeanVisitor extends AutoBeanVisitor
        {
            private ModelData modelData;
    
    
            public ModelDataAutoBeanVisitor()
            {
                this.modelData = new BaseModelData();
            }
            public ModelData getModelData()
            {
                return modelData;
            }
    
    
    
    
            @Override
            public boolean visitValueProperty(String propertyName, Object previousValue, AutoBeanVisitor.PropertyContext ctx)
            {
                if (previousValue != null)
                {
                    modelData.set(propertyName, previousValue);
                }
                return false;
            }
    
    
    
    
            @Override
            public boolean visitCollectionProperty(String propertyName, AutoBean<Collection<?>> value, CollectionPropertyContext ctx)
            {
                if (value != null)
                {
                    Collection<?> beans = value.as();
                    Collection<ModelData> models = new ArrayList<ModelData>(beans.size());
                    for (Object element : beans)
                    {
                        AutoBean autoBean = AutoBeanUtils.getAutoBean(element);
    
    
                        ModelDataAutoBeanVisitor newVisitor = new ModelDataAutoBeanVisitor();
                        autoBean.accept(newVisitor);
                        models.add(newVisitor.getModelData());
                    }
    
    
                    modelData.set(propertyName, models);
                }
                return false;
            }
        }
    }

  8. #18
    Sencha User
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,737
    Answers
    109

    Default

    Cool stuff, nice work. Two things:

    > Now if only there were a way to instead pass AutoBeans or interfaces as type parameters to Stores

    Check out 3, that aspect of the api is well settled, but as you've noted, there'll be a few changes to be made.

    And second, it looks as though the root object going over the wire is an array. This can be a dangerous move to make. Two links to start looking into this (briefly, GET calls that return array without custom headers make some browser's vulnerable to handing away your user's data):
    It can be made safe, but I'd offer it isn't worth it. Also limits your upgrade path - AutoBeanCodex.decode cannot/will not support decoding an array - you'll need to do your trick here - json->array, iterate over each item, and item->json, then json->autobean. Ugly. As it appears you've already noticed.

    Best practice is for the root of your data to be an object.

  9. #19
    Sencha User
    Join Date
    Jun 2008
    Posts
    17

    Default

    I've always wondered what the driving force was behind the named root object for collections but never did find a reasonable explanation. The links you provided clarifies this for me now. Much appreciated! This is a trivial change to make on the server side and now that I understand the importance I will make it standard. Thanks again.

    I will certainly play with gxt3 but as we will be starting a new project this week it seems I have some work to do in regards to 2.2.5. You have been most helpful and I look forward to working with you some more!

Page 2 of 2 FirstFirst 12

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •