如何为类的所有字段编写通用的自定义GSON反序列化器?

时间:2016-09-23 12:56:25

标签: java json gson deserialization json-deserialization

我有一个MySimpleObject类,它有各种成员字段。给定一个json,它将相应地填充该字段。但是,如果json被声明为" nil",我计划将其设置为null而不是string" nil"。

以下示例应该是MySimpleObject,其所有字段都为null,以及subItemList的0长度列表。 myObj1应该等于myObj2

@Test
public void myTestFunction() {
    String myJson1 = "{\"item1\":\"nil\",\"item2\":\"nil\",\"subItemList\":[{\"subItem1\":\"nil\",\"subItem2\":\"nil\"}]}";
    String myJson2 = "{\"subItemList\":[]}";

    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(new TypeToken<List<MySubItems>>(){ }.getType(), new MyOwnListDeserializer());
    gsonBuilder.registerTypeAdapter(String.class, new MyOwnStringDeserializer());
    Gson gson = gsonBuilder.create();

    MySimpleObject myObj1 = gson.fromJson(myJson1, MySimpleObject.class);
    MySimpleObject myObj2 = gson.fromJson(myJson2, MySimpleObject.class);

    assertThat(myObj1.equals((myObj2))).isTrue();
}

class MySimpleObject implements Serializable {
    String item1 = null;
    String item2 = null;
    List<MySubItems> subItemList;

    @Override
    public int hashCode() {
        int hash = 17;
        hash = 31*hash + ((item1 == null)? 0 :item1.hashCode());
        hash = 31*hash + ((item2 == null)? 0 :item2.hashCode());
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof MySimpleObject) {
            return this.hashCode() == obj.hashCode();
        }
        return super.equals(obj);
    }
}

class MySubItems implements Serializable {
    String subItem1 = null;
    String subItem2 = null;

    @Override
    public int hashCode() {
        int hash = 17;
        hash = 31*hash + ((subItem1 == null)? 0 :subItem1.hashCode());
        hash = 31*hash + ((subItem2 == null)? 0 :subItem2.hashCode());
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof MySubItems) {
            return this.hashCode() == obj.hashCode();
        }
        return super.equals(obj);
    }
}

如何编写自定义序列化程序而不必遍历每个jsonObject并检查&#34; nil&#34;设置为null?

2 个答案:

答案 0 :(得分:1)

我查看了Gson库以及gson-fire project,但它们似乎都没有允许真正的通用(和高性能)解决方案。

一种方法是在将json字符串中的"nil"系统地替换为"null",然后再将其传递给gson对象。它不是很干净,但效果很好,可以工作。

这是一个基本方法(必须改进):

public static String convertNil( String json ){
      return json.replaceAll( ":\\s*\"nil\"", ": null" );
}

然后使用它:

MySimpleObject myObj1 = gson.fromJson( convertNil( myJson1 ), MySimpleObject.class );
MySimpleObject myObj2 = gson.fromJson( convertNil( myJson2 ), MySimpleObject.class );

答案 1 :(得分:0)

我设法提出一些自定义反序列化来完成以下工作。

@Test
public void myTestFunction() {
    String myJson1 = "{\"item1\":\"nil\",\"item2\":\"nil\",\"subItemList\":[{\"subItem1\":\"nil\",\"subItem2\":\"nil\"}]}";
    String myJson2 = "{\"subItemList\":[]}";

    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(new TypeToken<List<MySubItems>>(){ }.getType(), new MyOwnListDeserializer());
    gsonBuilder.registerTypeAdapter(String.class, new MyOwnStringDeserializer());
    Gson gson = gsonBuilder.create();

    MySimpleObject myObj1 = gson.fromJson(myJson1, MySimpleObject.class);
    MySimpleObject myObj2 = gson.fromJson(myJson2, MySimpleObject.class);

    assertThat(myObj1.equals((myObj2))).isTrue();
}

class MySimpleObject implements Serializable {
    String item1 = null;
    String item2 = null;
    List<MySubItems> subItemList;

    @Override
    public int hashCode() {
        int hash = 17;
        hash = 31*hash + ((item1 == null)? 0 :item1.hashCode());
        hash = 31*hash + ((item2 == null)? 0 :item2.hashCode());
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof MySimpleObject) {
            return this.hashCode() == obj.hashCode();
        }
        return super.equals(obj);
    }
}

class MySubItems implements Serializable {
    String subItem1 = null;
    String subItem2 = null;

    @Override
    public int hashCode() {
        int hash = 17;
        hash = 31*hash + ((subItem1 == null)? 0 :subItem1.hashCode());
        hash = 31*hash + ((subItem2 == null)? 0 :subItem2.hashCode());
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof MySubItems) {
            return this.hashCode() == obj.hashCode();
        }
        return super.equals(obj);
    }
}

class MyOwnStringDeserializer implements JsonDeserializer<String> {
    @Override
    public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        return (json.getAsString().equals("nil"))? null : json.getAsString();
    }
}

class MyOwnListDeserializer implements JsonDeserializer<List<MySubItems>> {
    @Override
    public List<MySubItems> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        List<MySubItems> list = new ArrayList<>();

        for (JsonElement element : json.getAsJsonArray()) {
            JsonObject subObj = element.getAsJsonObject();
            MySubItems subItems = new MySubItems();

            if (!subObj.get("subItem1").getAsString().equals("nil")) {
                subItems.subItem1 = subObj.get("subItem1").getAsString();
            }
            if (!subObj.get("subItem2").getAsString().equals("nil")) {
                subItems.subItem2 = subObj.get("subItem1").getAsString();
            }

            if (subItems.subItem1 != null || subItems.subItem2 != null) {
                list.add(subItems);
            }
        }

        return (list.size() == 0)? null : list;
    }
}

我仍然不满意MyOwnListDeserializer,必须手动处理subItem1和subItem2,尽管它们应该具有MyOwnStringDeserializer定义的相同规则。但我只是不知道如何将MyOwnStringDeserializer应用于MyOwnListDeserializer

对于其他比我更好的答案仍然是开放的。

更新可以找到更优化的答案 https://stackoverflow.com/a/39671580/3286489