tags:

views:

104

answers:

3

Hello,

I must receive the following JSON:

{
   "method":"methodName1",
   "args":{
      "arg1Name":"arg1Value",
      "arg2Name":"arg2Value"
   }

}

Here's another example:

{
   "method":"methodName2",
   "args":{
      "arg1Name":"arg1Value",
      "arg2Name":"arg2Value"
      "arg3Name":"arg3Value"
      "arg4Name":"arg4Value"
   }

}

I need to parse those JSON in order to call the specified method with the specified "args" as parameters.

The easy way (for me) would be to use a JsonParser to get a JsonElement then a JsonObject and then extract each value using .get()... something like this:

JsonParser jsonParser = new JsonParser();
JsonElement jsonElement = jsonParser.parse(jsonString);
if (jsonElement.isJsonObject()) {
     JsonObject jsonObject = jsonElement.getAsJsonObject();
     String method = jsonObject.get("method").getAsString();
     JsonObject jsonArgs = jsonObject.getAsJsonObject("args");
     String arg1 = jsonArgs.get("arg1Name").getAsString();
     String arg2 = jsonArgs.get("arg2Name").getAsString();

}

And then I could call the specified method with the appropriate arguments (using a "switch").

I was just wondering if there would be an easier (or prettier) way to achieve this. I could use .fromJson() to retrieve an object instead but I don't know how should I create my class to do so (there's something like an array or args and not all methods have the same number or args).

Sorry if this is an easy question, I'm just new to JSON (and to Java as well). Can someone helps a little? Or give advices?

Thanks a lot!

EDIT
I managed to do this:
import lotus.domino.; import com.google.gson.; import java.io.*; import java.lang.reflect.Method;

public class JavaAgent extends AgentBase {
    public void NotesMain() {
        try {
            String jsonReceived = "{\"method\":\"methodName\",\"args\":{\"arg1Name\":\"arg1Value\",\"arg2Name\":\"arg2Value\"}}";
            Gson gson = new Gson();
            MethodCall mc = gson.fromJson(jsonReceived, MethodCall.class);

            JavaAgent ja = new JavaAgent();
            Method method = getClass().getMethod(mc.getMethod(), MethodCall.class);
            String s = (String)method.invoke(ja, mc);

            PrintWriter pw = getAgentOutput();
            pw.println(s);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public String methodName(MethodCall mc) {
        String s = mc.getArgs().get("arg1Name") + " " + mc.getArgs().get("arg2Name");
        return s;
    }
}

import java.util.*;
public class MethodCall {
    private String method;
    private Map<String, String> args;

    public String getMethod() {
        return method;
    }

    public Map<String, String> getArgs() {
        return args;
    }
}

It seems to work... but since I'm new to Java, I'm not sure that's the proper way to do it. What do you think? Thanks again!

A: 

Use Java reflection.

List<String> args = new ArrayList<String>();
// add the args to the list here
Method method = myClass.getClass().getMethod(method, List.class);
Object o = method.invoke(myClass, args);
bluedevil2k
Doesn't answer the question...
Steven Schlansker
A: 

The problem with doing this the way you normally would using Gson is that the JSON "args" is an object that, based on your examples, can have a variable number of fields (the arguments and their names). It would be better if "args" were just a JSON array of arguments to the method (with no argument names) since you won't be able to use the argument names when calling the method anyway. Then you could write a simple class like:

class MethodCall {
   private String method;
   private List<String> args;
   ...
}

which could be parsed from the JSON.

I'm guessing you can't change the format you're receiving, though, so another option might be to use the same class I listed above, but register a custom JsonDeserializer for it. That might look something like this:

public MethodCall fromJson(JsonElement json, Type typeOfT, 
                           JsonDeserializationContext context)
      throws JsonParseException {
  JsonObject obj = json.getAsJsonObject();
  String method = obj.get("method").getAsString();
  JsonObject argsObj = obj.getAsJsonObject("args");
  List<String> args = new ArrayList<String>();
  for(Map.Entry<String,JsonElement> entry : argsObj.entrySet()) {
    args.add(entry.getValue().getAsString());
  }
  return new MethodCall(method, args);
}

You could then use reflection to call the method. Ideally the method that does that would be a method on MethodCall, so after getting the MethodCall object you'd just write call.callMethod(obj) with obj being the object you want the method called on.

Edit

The JSON format that would be easiest for calling Java methods would be something like:

{
  "method":"methodName2",
  "args": [
    "arg1Value",
    "arg2Value",
    "arg3Value",
    "arg4Value"
  ]
}

Gson can automatically convert the JSON array into a List or array which could then be used to invoke the method via reflection.

ColinD
Actually, I can change the format of the JSON I'm receiving. It's not completed yet by the other team. How should the JSON be formatted to use the class you supplied? Thanks for your help!
S Macgowan
I've edited the answer to include that. Deserializing the args as a `Map` would also work (the values of the map could be used to invoke the method since a map that preserves their order is used). However, you wouldn't be doing anything with the names of the args (at least not to invoke the method) so it might be preferable to avoid having them.
ColinD
I managed to make it works using your solution (List) and ColinD's solution (Map). I'm actually having a hard time trying to call the method from the JSON with the appropriate args. Right now, all my methods (I have two of them) return a String (but more methods could be added later). I was also wondering what would happen if I had to send, for example, an "int" as a parameter (the List/Map are defined as String). Thanks a lot for your help!
S Macgowan
Oh, I didn't have to do a custom JsonDeserializer if I use ColinD's solution of if I modify my JSON to use your solution.
S Macgowan
I edited my main post, could you look at it please?
S Macgowan
+1  A: 

Perhaps the Gson Map serializer is smart enough to deserialize that?

public class MethodCall {
    private String method;
    private Map<String, String> args;
}

(Don't have the moment to test it thoroughly, but I hope it works! :) )

Steven Schlansker
This does work and Gson seems to use a `LinkedHashMap` so the order of the args is preserved as well, which is important for invoking the method.
ColinD
Thanks for the answer... I tried it and it works perfectly!
S Macgowan
@S Macgowan: if the answer fixes your problem, feel free to upvote and/or accept it as the answer. :-) (Just a reminder since I notice you're newish here!)
Steven Schlansker