2

I've tried everything but the solution is so ugly, I really want a straight forward answer on if this can be improved (and that means if I need to use a different implementation).

The problem lies in Map of Maps with GSON:

Gives me this response according to Firebug:

{"id": 2, "result": {"FirstWorld": {"FirstValue": 5, ... }, "SecondWorld":{"FirstValue": 5, ....}}, "error":null }

There are around 200 "Values", but only two "Worlds". This is what I have so far to parse it in my ControlService class:

public void RegisterValues( String [] Names, AsyncCallback<Map<String,RegisterValues>> callback);

public class RegisterValues
    {
        int FirstValue;
        int SecondValue;
        ... And so on 200 times !!!

So I access the data like so:

service_.RegisterValues( Names, new AsyncCallback<ControlService.RegisterValues>()
                {
                public void onSuccess( GlibControlService.RegisterValues result )
                    {
                        String test = "FirstValue";

                        String message="Result for channel 1 is ";
                        for( String Name : result.keySet() ) message+=Name+"="+result.get(Name);

But as you can see, this is going to be really long. The other problem is some of the "Values" have ampersands in them, which means I can't use them in this method e.g;

@SerializedName("One&Two") // Ampersand not allowed in name
int OneTwo; //gives Invalid JSON response apparently

Does anyone have a better method of doing this?

EDIT Code that works:

private ControlService service_;

service_.connectedNames( new String[0], new AsyncCallback<String[]>() {

            public void onSuccess( String[] result) 
            {   
                List_.removeItem(0);
                Names = result;
                for( String Name : result ) {

                    List_.addItem(Name);
                }
                List_.setEnabled( true );
            }
            public void onFailure(Throwable why)
            {
                List_.removeItem(0);
                List_.addItem( "Server error!" );
            }
        });

Then in my ControlService Class, I have this:

@RpcImpl(version=RpcImpl.Version.V2_0,transport=RpcImpl.Transport.HTTP_POST)
public interface ControlService extends RemoteJsonService
{

    public void connectedNames( String [] Names, AsyncCallback<String[]> callback );

This works perfectly.

I tried doing it a very similar way by adding this in my ControlService:

public void RegisterValues( String [] Names, AsyncCallback<Map< String,Map<String, Integer>> callback); 

And so on, making sure the Map<String, Map<String, Integer>> was also in the onSucess() part of the structure too. This however caused my program to crash. It seemed to me that it doesn’t like nested maps. However GSON will automatically parse in objects if the member name matches the JSON field. So I used that RegisterValues to automatically force GSON to parse this.

Stack trace:

   [ERROR] Errors in 'generated://AF9BA58B045D92E7896CD657C9CC5FAF/example/client/ControlService_JsonProxy.java'
      [ERROR] Line 18: INSTANCE cannot be resolved or is not a field
      See snapshot: /var/folders/pf/56b3mznn35gg741rlsqq424m0000gp/T/example.client.ControlService_JsonProxy2948240672238331252.java

This is why I think GSON can't automatically parse Nested Maps using AsyncCallback. It may be better to do my HTTP call more in line with what you suggested below.

fiz
  • 906
  • 3
  • 14
  • 38

1 Answers1

4

The best way is to wrap the response you posted into one class.

{"id": 2, "result": {"FirstWorld": {"FirstValue": 5, ... }, "SecondWorld":{"FirstValue": 5, ....}}, "error":null }

import java.util.Map;

public class Response {

    private int id;
    private Map<String, Map<String, Integer>> result;
    private String error;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Map<String, Map<String, Integer>> getResult() {
        return result;
    }

    public void setResult(Map<String, Map<String, Integer>> result) {
        this.result = result;
    }

    public String getError() {
        return error;
    }

    public void setError(String error) {
        this.error = error;
    }

}

I've tested with this and it runs fine.

static String json = "{\"id\":2,\"result\":{\"FirstWorld\":{\"FirstValue\":5,\"SecondValue\":6},\"SecondWorld\":{\"FirstValue\":5,\"SecondValue\":6}},\"error\":null}";

    public static void main(String[] args) {


        Gson g= new Gson();
        Response b=g.fromJson(json, Response.class );
        System.out.println(g.toJson(b));
    }

If you need to have the RegisterValues class declared, you only need to replace the Map<String, Integer> of result with this class, probably extending Map.

rpax
  • 4,468
  • 7
  • 33
  • 57
  • Thankyou very much for your reply. I'll try it now - but I'm struggling to see how I'll fit this into my AsyncCallback method – fiz Mar 28 '14 at 10:09
  • @fiz Maybe extending the `AsyncCallback` class – rpax Mar 28 '14 at 10:37
  • I'll give this a go now. I've only just started learning Java. If this works and you have a "buy me a beer" on paypall, I'll happily donate – fiz Mar 28 '14 at 10:44
  • The problem is the response you parsed is the response I get back from doing an AysncCallback to the server. So I'm trying to figure out a way to slot this in – fiz Mar 28 '14 at 10:49
  • @fiz thank you very much. I don't care about the paypal _buy me a beer_ thing, I'm happy just helping people. The problem is I don't understand very well the `service_.RegisterValues` stuff. If you can post a little more code, it would be great – rpax Mar 28 '14 at 10:49
  • is that enough information I edited in above? The RegisterValues class forces GSON to parse it according to the FieldNames provided. If there's a better way of making AsyncCallback via HTTP requests in GSON, I haven't worked it out yet – fiz Mar 28 '14 at 11:10
  • Maybe the problem here is I need to do a different method of HTTP post request instead of using the AynscCallback() – fiz Mar 28 '14 at 11:17
  • Managed to get around this by using RestyGWT and then using your method to deserialise when the server responds. Thanks ! – fiz Apr 01 '14 at 10:35