Problem decoding complex AutoBean

438 views
Skip to first unread message

Thad Humphries

unread,
Aug 19, 2013, 10:37:30 PM8/19/13
to google-we...@googlegroups.com
I've managed a simple AutoBean with a list. Now I've a more complex one, and I don't understand what's wrong. My get methods are returning null though the debugger shows the object has data.

The JSON looks like this:

{
  "mquery":
    {
      "screenname":"Index Card",
      "fields":
        {
          "field":[
            {"title":"Name",  "name":"odname",   "value":"*TIFF*"},
            {"title":"Date",  "name":"oddate",   "value":""},
            {"title":"Ref #", "name":"odrefnum", "value":""}
          ]
        }
    }


I have three interfaces:

public interface Field {
  String getTitle();
  void setTitle(String title);
  String getName();
  void setName(String name);
  String getValue();
  void setValue(String value);
}

public interface FieldList {
  List<Field> getField();
  void setField(List<Field> field);
}

public interface MQuery {
  String getScreenname();
  void setScreenname(String screenname);
  FieldList getFields();
  void setFields(FieldList fields);
}

In my bean factory, I have

public interface BeanFactory extends AutoBeanFactory {
  ...
  AutoBean<Field> field();
  AutoBean<FieldList> fields();
  AutoBean<MQuery> mquery();
}

In my onResponseReceived() method, I call

  AutoBean<MQuery> mqueryBean = 
      AutoBeanCodex.decode(clientFactory.getBeanFactory(), 
          MQuery.class, response.getText());
  MQuery mQuery = mqueryBean.as();
  logger.log(Level.INFO, mQuery.getScreenname());
  screenName = mQuery.getScreenname();
  fieldList = mQuery.getFields().getField();
  view.renderFields(fieldList);

When I look at the debugger in Chrome, screenname, etc are populated (in mQuery:this$0:data_0 there is mquery with screenename set and 3 field objects in fields). However mQuery.getScreenname() returns null as does mQuery.getFields().

What am I not seeing here? Could the presence of other AutoBean<>s in my factory (beans that work) be messing me up (do I need a separate factory)?

Thomas Broyer

unread,
Aug 20, 2013, 5:34:03 AM8/20/13
to google-we...@googlegroups.com
You don't seem to have an AutoBean interface with a "MQuery getMquery()" method.
The object you're parsing has only a "mquery" property, so if you look at it as if it were an MQuery object, getScreenname and getFields would expectedly be null.

Thad Humphries

unread,
Aug 20, 2013, 7:47:58 AM8/20/13
to google-we...@googlegroups.com
Thank you, Thomas. That worked.

I added the interface

public interface MQueryWrapper {
  MQuery getMquery();
}

In BeanFactory, I changed "AutoBean<MQuery> mquery();" for "AutoBean<MQueryWrapper> mquery();"

My onResponseReceived() now looks like

  AutoBean<MQueryWrapper> bean = 
      AutoBeanCodex.decode(clientFactory.getBeanFactory(), MQueryWrapper.class, 
          response.getText());
  MQuery mQuery = bean.as().getMquery();
  logger.log(Level.INFO, mQuery.getScreenname());
  screenName = mQuery.getScreenname();
  fieldList = mQuery.getFields().getField();
  view.renderFields(fieldList);

Client-side this beats overlay types because it can handle nested objects (so long as I figure out nesting). I've not tried it server-side yet, but that looks very handy. However it's probably wise at this early stage to come up with better tags than "field" and "fields" for the XML that my JSON originates from. As more structures are added, I may have conflicts. (I wonder if JSON's XML.toJSONObject() does some magic with namespaces...)

Thomas Broyer

unread,
Aug 20, 2013, 8:40:47 AM8/20/13
to google-we...@googlegroups.com


On Tuesday, August 20, 2013 1:47:58 PM UTC+2, Thad Humphries wrote:
Thank you, Thomas. That worked.

I added the interface

public interface MQueryWrapper {
  MQuery getMquery();
}

In BeanFactory, I changed "AutoBean<MQuery> mquery();" for "AutoBean<MQueryWrapper> mquery();"

My onResponseReceived() now looks like

  AutoBean<MQueryWrapper> bean = 
      AutoBeanCodex.decode(clientFactory.getBeanFactory(), MQueryWrapper.class, 
          response.getText());
  MQuery mQuery = bean.as().getMquery();
  logger.log(Level.INFO, mQuery.getScreenname());
  screenName = mQuery.getScreenname();
  fieldList = mQuery.getFields().getField();
  view.renderFields(fieldList);

Client-side this beats overlay types because it can handle nested objects (so long as I figure out nesting).

Why wouldn't you be able to do it with JSOs?
 
I've not tried it server-side yet, but that looks very handy.

Yup, I'm using it to serialize some data that I output in the host page and load on the client-side at startup, works great and you're sure that your JSON will be understood by the end and don't fear forgetting to update the code on one side and not the other.

Thad Humphries

unread,
Aug 20, 2013, 12:37:08 PM8/20/13
to google-we...@googlegroups.com
On Tuesday, August 20, 2013 8:40:47 AM UTC-4, Thomas Broyer wrote:


On Tuesday, August 20, 2013 1:47:58 PM UTC+2, Thad Humphries wrote:
Thank you, Thomas. That worked.

I added the interface

public interface MQueryWrapper {
  MQuery getMquery();
}

In BeanFactory, I changed "AutoBean<MQuery> mquery();" for "AutoBean<MQueryWrapper> mquery();"

My onResponseReceived() now looks like

  AutoBean<MQueryWrapper> bean = 
      AutoBeanCodex.decode(clientFactory.getBeanFactory(), MQueryWrapper.class, 
          response.getText());
  MQuery mQuery = bean.as().getMquery();
  logger.log(Level.INFO, mQuery.getScreenname());
  screenName = mQuery.getScreenname();
  fieldList = mQuery.getFields().getField();
  view.renderFields(fieldList);

Client-side this beats overlay types because it can handle nested objects (so long as I figure out nesting).

Why wouldn't you be able to do it with JSOs?

Ah, thank you for that little programming problem. I was missing something when I previously looked at overlay types. Yes, they can handle nested objects.

Assuming the same JSON structure as above, these two objects manage it:

public class MQueryField extends JavaScriptObject {
  protected MQueryField() {}
  public final native String getTitle() /*-{ return this.title; }-*/;
  public final native String getName() /*-{ return this.name; }-*/;
  public final native String getValue() /*-{ return this.value; }-*/;
}

public class MQueryRoot extends JavaScriptObject {
  protected MQueryRoot() {}
  public final native String getScreenName() /*-{ return this.screenname; }-*/;
  public final native JsArray<MQueryField> getFields() 
      /*-{ return this.fields.field; }-*/;
}

Now reading in that same string:

JSONObject jobj = new JSONObject(JsonUtils.safeEval(response.getText()));
MQueryRoot root = jobj.get("mquery").isObject().getJavaScriptObject().cast();
logger.log(Level.INFO, root.getScreenName());
// prints "Index Card"
logger.log(Level.INFO, "fields["+root.getFields().length()+"]");
// prints "fields[3]"

Hm… I think I prefer AutoBeans, but this is nice to know. 

 
I've not tried it server-side yet, but that looks very handy.

Yup, I'm using it to serialize some data that I output in the host page and load on the client-side at startup, works great and you're sure that your JSON will be understood by the end and don't fear forgetting to update the code on one side and not the other.

Thank you for these examples.
 

Jens

unread,
Aug 20, 2013, 1:50:36 PM8/20/13
to google-we...@googlegroups.com

JSONObject jobj = new JSONObject(JsonUtils.safeEval(response.getText()));
MQueryRoot root = jobj.get("mquery").isObject().getJavaScriptObject().cast();

If you want that code a bit easier to read you could use a MQueryWrapper JSO like in the AutoBean example and then write

MQueryWrapper wrapper = JsonUtils.safeEval(response.getText());
MQueryRoot root = wrapper.getMQuery();


So at the end its like: calling .as() vs. implementing JSO's by hand/via code generation. 

-- J.

Akshay Kumar

unread,
Jul 10, 2020, 4:23:35 PM7/10/20
to GWT Users
Can anyone help me?
My json string is - 
{
"themeId": 1,
"name": "rpc",
"enabled": true,
"propertiesList": [
{
"id": 1,
"themeConfigurationId": 1,
"chatScreen": "chatScreen",
"component": "header",
"property": "text",
"value": "HELLO"
},
{
"id": 3,
"themeConfigurationId": 1,
"chatScreen": "chatScreen",
"component": "background",
"property": "color",
"value": "rgba(0,191,255,0.5)"
}
]
}

Interfaces are -

public interface IWebchatThemeConfigurationBean { public Integer getThemeId(); public String getName(); public Boolean getEnabled(); public List<IWebchatThemeConfigurationPropertyBean> getPropertiesList(); }
public interface IWebchatThemeConfigurationPropertyBean { public Integer getId(); public Integer getThemeConfigurationId(); public String getChatScreen(); public String getComponent(); public String getProperty(); public String getValue(); }

public interface ChatFactory extends AutoBeanFactory {
        AutoBean<IWebchatThemeConfigurationBean> theme();

        AutoBean<IWebchatThemeConfigurationPropertyBean> properties();
    }
    ChatFactory factory;
factory = GWT.create(ChatFactory.class);

IWebchatThemeConfigurationBean deserializeFromJson(String json) { AutoBean<IWebchatThemeConfigurationBean> bean = AutoBeanCodex.decode(factory, IWebchatThemeConfigurationBean.class, json); return bean.as(); }

in this returned bean I'm getting null propertiesList.
name, enabled, themeId are fine.

Colin Alworth

unread,
Jul 10, 2020, 4:56:57 PM7/10/20
to GWT Users
I'm not seeing any issue with that code and sample JSON. Here's a quick entrypoint that I made:

    @Override
    public void onModuleLoad() {
        IWebchatThemeConfigurationBean bean = deserializeFromJson("{\n" +
                "    \"themeId\": 1,\n" +
                "    \"name\": \"rpc\",\n" +
                "    \"enabled\": true,\n" +
                "    \"propertiesList\": [\n" +
                "        {\n" +
                "            \"id\": 1,\n" +
                "            \"themeConfigurationId\": 1,\n" +
                "            \"chatScreen\": \"chatScreen\",\n" +
                "            \"component\": \"header\",\n" +
                "            \"property\": \"text\",\n" +
                "            \"value\": \"HELLO\"\n" +
                "        },\n" +
                "        {\n" +
                "            \"id\": 3,\n" +
                "            \"themeConfigurationId\": 1,\n" +
                "            \"chatScreen\": \"chatScreen\",\n" +
                "            \"component\": \"background\",\n" +
                "            \"property\": \"color\",\n" +
                "            \"value\": \"rgba(0,191,255,0.5)\"\n" +
                "        }\n" +
                "    ]\n" +
                "}");
        DomGlobal.console.log(bean.getEnabled());
        DomGlobal.console.log(bean.getThemeId() + "");
        DomGlobal.console.log(bean.getName());
        DomGlobal.console.log(String.valueOf(bean.getPropertiesList()));
        DomGlobal.console.log(bean.getPropertiesList().size() + "");
        DomGlobal.console.log(bean.getPropertiesList().stream().map(IWebchatThemeConfigurationPropertyBean::getValue).collect(Collectors.joining(", ")));
    }


Notice that after running your parse method, it dumps the output to the JS console - here is the output:
true
1
rpc
[com.vertispan.draw.connected.client.FlowChartEntryPoint_IWebchatThemeConfigurationPropertyBeanAutoBean$2@1b, com.vertispan.draw.connected.client.FlowChartEntryPoint_IWebchatThemeConfigurationPropertyBeanAutoBean$2@1c]
2
HELLO, rgba(0,191,255,0.5)

It seems to be working?

One note: if you do not actually call the properties() method, it is not needed in the factory, since the IWebchatThemeConfigurationPropertyBean type is already reachable from IWebchatThemeConfigurationBean.

Using GWT 2.8.2 for this test.
Reply all
Reply to author
Forward
0 new messages