【问题标题】:How to deserialize this using GSON? (get rid of List<StringMap>)如何使用 GSON 反序列化? (摆脱 List<StringMap>)
【发布时间】:2014-01-21 16:19:52
【问题描述】:

我有像这样的json:

{ 
    "id":  "AnId",
    "type": "Button",
    "parameter": {
        "text" : "TestText"
    }
}

参数可以是任何结构,也可以包含更多的 json 对象或数组,例如:

{ 
    "id":  "AnId",
    "type": "RadioGroup",
    "parameter": {
         "groupname": "AGroupName",
         "selected": "r2",
         "radioboxes": [
              {
                   "id": "r1",
                   "label": "radio1"
              },
              {
                   "id": "r2",
                   "label": "radio2"
              }
         ]
    }
}

但是,根据类型,可以知道参数是什么样的,例如

public class RadioGroup {
    private String groupName;
    // ..other primitive attributes here..
    private Map<String, RadioBox> radioBoxes;
    // ... getters/setters
}

public class RadioBox {
  private String id;

  private String label;
  // ... getter/setters
}

但是在反序列化之前我不知道类型!

我想知道反序列化输入的最佳方法是什么?我不知道我是否获得按钮或无线电组 json?

到目前为止,我只是将给定的输入(按钮或复选框,尚不知道)反序列化为Map&lt;String, Object&gt;,如果它实际上是按钮,它工作正常,但如果我提供无线电组作为输入,我将获得 gson 的内部类型ArrayList&lt;StringMap&gt; 用于单选框。像 groupName 这样的原始参数很好 (String.class)。但是我该怎么做才能得到List&lt;Map&lt;String,Radiobox&gt;&gt; 而不是ArrayList&lt;StringMap&gt; 作为radioboxes 参数?

【问题讨论】:

    标签: java json deserialization gson


    【解决方案1】:

    您需要设置您正在使用的自定义类的类型。这是为了序列化(只是一个例子):

        Gson gson = new GsonBuilder().create()
        Type aType = new TypeToken</ClassName/>() {}.getType();     
        String json = gson.toJson(this, aType);
    

    gson 然后将正确转换变量类型。如果您使用 自定义类 作为变量,则需要编写自定义类型适配器并将类型适配器注册到 GsonBuilder() 实例。

    要反序列化,您需要相同的过程。

    编辑

        Class<?> klass = null;
        try {
            klass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new JsonParseException(e.getMessage());
        }
    

    【讨论】:

    • 好吧,在反序列化输入之前我不知道 TypeToken,因为我不知道我是否有 button-json 或 radiobox-json。这就是我的问题所在:我如何向 gson 提供如何反序列化的信息?我当然可以解析类型,然后进行巨大的切换,但这很难维护,我希望有一种更优雅的方式来实现我的目标。
    • Hnnngh,我会实现你自己的getClass() 来避免反射(在每个类中)?做不到这一点,反思。人们似乎讨厌它,但我觉得它是抽象类型序列化/反序列化的合理解决方案。
    • 我仍然不明白这如何解决我的问题。我试图更明确地说明我的问题是什么。
    • 啊,抱歉,我没有正确阅读我的代码。您需要创建一个类型适配器并将类名写入 json 输出。当您将其读回(即反序列化)时,您可以基于该字符串创建类(在类型适配器中)。查看JsonElement serialize()JsonElement deserialize() - 您需要使用反射来确定类类型。用反射示例更新了上面的答案。
    • 那么你肯定需要一个自定义类型适配器。找出您正在阅读的 JSON,查看它是否与 RadioGroup、Box 或其他任何内容匹配,然后创建该类类型。也许使用 case 语句而不是多个 if 块。
    【解决方案2】:

    您可以使用 GSON JsonDeserializer 解析任意 json。我已经为 RadioGroup 编写了如下模型类,但您可以根据需要进行更改。

    public class RadioGroup {
      private String id;
      private String type;
      // You can put groupName and selected properties into other class, 
      private String groupname;
      private String selected;
      // for button param parsing into Map
      private Map<String,String> btnParam;
      // for radion Btn just parsing into List
      private List<RadioBox> radioboxes;
    }
    
    public class CustomDeserializer implements JsonDeserializer<RadioGroup> {
    
            @Override
            public RadioGroup deserialize(JsonElement json, Type arg1,
                    JsonDeserializationContext arg2) throws JsonParseException {
    
                final JsonObject jsonObject = json.getAsJsonObject();
                final String id= jsonObject.get("id").getAsString();
                final String type= jsonObject.get("type").getAsString();
                final RadioGroup radioGroup = new RadioGroup();
    
                radioGroup.setId(id);
                radioGroup.setType(type);
    
                JsonElement jsonElementForParam = jsonObject.get("parameter");
                JsonElement jsonElementForText = jsonElementForParam.getAsJsonObject().get("text");
    
                if(null==jsonElementForText){
                    String groupname = jsonElementForParam.getAsJsonObject().get("groupname").getAsString();
                    String selected = jsonElementForParam.getAsJsonObject().get("selected").getAsString();
                    JsonElement jsonElementForRadioboxes = jsonElementForParam.getAsJsonObject().get("radioboxes");
    
                    radioGroup.setSelected(selected);
                    radioGroup.setGroupname(groupname);
    
                    Gson gson = new Gson();
                    Type typeOf = new TypeToken<List<RadioBox>>(){}.getType();  
                    List<RadioBox> radioboxes =gson.fromJson(jsonElementForRadioboxes, typeOf);
                    radioGroup.setRadioboxes(radioboxes);
    
    
                } else if (null!=jsonElementForText) {
                    Gson gson = new Gson();
                    Type typeOf = new TypeToken<Map<String,String>>(){}.getType();  
                    Map<String,String> map = gson.fromJson(jsonElementForParam, typeOf);
                    radioGroup.setBtnParam(map);
    
                }
    
                return radioGroup;
            }
          }
    

    终于解析了

    GsonBuilder gsonBuilder = new GsonBuilder();
       gsonBuilder.registerTypeAdapter(RadioGroup.class, new CustomDeserializer());
       Gson gson = gsonBuilder.create();
       RadioGroup radioGroup = gson.fromJson(buttonJson, RadioGroup.class);
    

    【讨论】:

    • 如果输入的是 button.json 会失败,因为按钮既没有“radioboxes”也没有“groupname”也没有“selected”键,而是“text”。输入是任意的!我现在不像我在问题中写的那样在序列化之前的类型。
    • 感谢您的努力,但此解决方案无法扩展!想想 100 种不同的元素类型,未来的需求可能需要更多。你会把所有这些都放在一个班级里吗?没门!但是,我编写了一个自定义 Deserializer 来反序列化该类型,并且根据它,我可以将反序列化委托给每种类型的特定于类型的反序列化器。我只是想知道是否有更好的“内置”方式,所以我会提出问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-21
    • 2013-02-01
    • 2020-04-02
    • 2011-05-11
    • 1970-01-01
    • 1970-01-01
    • 2018-07-20
    相关资源
    最近更新 更多