【问题标题】:Deserialization to a Java object using JSON?使用 JSON 反序列化为 Java 对象?
【发布时间】:2010-08-11 18:10:27
【问题描述】:

我必须收到以下 JSON:

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

}

这是另一个例子:

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

}

我需要解析这些 JSON 以便以指定的“args”作为参数调用指定的方法。

(对我而言)最简单的方法是使用JsonParser 来获取JsonElement,然后是JsonObject,然后使用.get() 提取每个值...如下所示:

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();

}

然后我可以使用适当的参数调用指定的方法(使用switch)。

我只是想知道是否有更简单(或更漂亮)的方法来实现这一点。

我可以改用.fromJson() 来检索对象,但我不知道我应该如何创建我的类来执行此操作(类似于 args 数组,并非所有方法都具有相同数量的 args)。

我是 JSON 新手(也是 Java 新手)。

我已经做到了:

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;
    }
}

它似乎工作......但由于我是 Java 新手,我不确定这是正确的方法。你怎么看?

【问题讨论】:

    标签: java json


    【解决方案1】:

    也许 Gson Map 序列化器足够聪明,可以反序列化它?

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

    (没有时间彻底测试它,但我希望它有效!:))

    【讨论】:

    • 这确实有效,Gson 似乎使用了LinkedHashMap,因此也保留了 args 的顺序,这对于调用该方法很重要。
    • @S Macgowan:如果答案解决了您的问题,请随时投票和/或接受它作为答案。 :-)(只是提醒一下,因为我注意到你在这里很新!)
    • @ColinD - 你无法保证 args 每次都以相同的顺序到达。 JSON“对象”中的条目顺序是未定义的,并且可以沿路径的任何位置更改。如果您控制发送内容的格式,则发送数组会更有意义。
    【解决方案2】:

    按照您通常使用 Gson 的方式执行此操作的问题在于 JSON "args" 是一个对象,根据您的示例,它可以具有可变数量的字段(参数及其名称)。如果 "args" 只是该方法的参数 JSON 数组(没有参数名称)会更好,因为无论如何在调用该方法时您将无法使用参数名称。然后你可以写一个简单的类:

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

    可以从 JSON 中解析出来。

    不过,我猜您无法更改收到的格式,因此另一种选择可能是使用我上面列出的相同类,但为它注册一个自定义 JsonDeserializer。这可能看起来像这样:

    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);
    }
    

    然后您可以使用反射来调用该方法。理想情况下,执行此操作的方法是MethodCall 上的方法,因此在获取MethodCall 对象后,您只需编写call.callMethod(obj)obj 是您希望调用该方法的对象。

    编辑

    最容易调用 Java 方法的 JSON 格式如下:

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

    Gson 可以自动将 JSON 数组转换为 List 或数组,然后可以使用该数组通过反射调用方法。

    【讨论】:

    • 实际上,我可以更改接收到的 JSON 的格式。其他团队尚未完成。应如何格式化 JSON 以使用您提供的类?感谢您的帮助!
    • 我已经编辑了答案以包含它。将 args 反序列化为 Map 也将起作用(映射的值可用于调用该方法,因为使用了保留其顺序的映射)。但是,您不会对 args 的名称做任何事情(至少不调用该方法),因此最好避免使用它们。
    • 我设法使用您的解决方案(列表)和 ColinD 的解决方案(地图)使其工作。我实际上很难尝试使用适当的参数从 JSON 调用该方法。现在,我所有的方法(我有两个)都返回一个字符串(但以后可以添加更多方法)。我还想知道如果我必须发送例如“int”作为参数(列表/映射被定义为字符串)会发生什么。非常感谢您的帮助!
    • 哦,如果我使用 ColinD 的解决方案(如果我修改我的 JSON 以使用您的解决方案),我不必执行自定义 JsonDeserializer。
    【解决方案3】:

    您必须将代码反序列化两次,因为它包含在其他数组中

    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) jsonObject.get("args");     
         String arg1 = jsonArgs.get("arg1Name").getAsString();
         String arg2 = jsonArgs.get("arg2Name").getAsString();
    
    }
    

    【讨论】:

      【解决方案4】:

      使用 Java 反射。

      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);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-12-28
        • 2018-06-07
        • 1970-01-01
        • 1970-01-01
        • 2012-09-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多