GSON动态转换特定字段的整数值为布尔值

5
如何处理名称相同但类型不同的字段?我在同一请求中从API获取时有时会得到整数值,有时会得到布尔值。当我得到像这样的Json时,我想知道如何处理。我创建了类型适配器,但它不起作用。我考虑创建不同的POJO类,但这个问题不仅适用于一个请求。出于这个原因,我不想创建POJO。顺便说一下,我看到过类似的问题,但它并没有解决我的问题。
{
  "name" : "john doe",
  "isValid" : true 
}

有时我会遇到int类型的问题。
{
  "name" : "john doe",
  "isValid" : 1 
}

我在获取整数时遇到了意外的JSON异常

class XModel{
    private boolean isValid;
    ...
    ...
}

我希望对于每个请求返回一个布尔值。有人知道如何解决这个问题吗?

编辑: 我想通过类型适配器防止使用instanceOf关键字。


解决方案:@Michał Ziober的回答对我有效。

class BooleanJsonDeserializer implements JsonDeserializer<Boolean> {

    private final Set<String> TRUE_STRINGS = new HashSet<>(Arrays.asList("true", "1", "yes"));

    @Override
    public Boolean deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        System.out.println(json);
        JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive();
        if (jsonPrimitive.isBoolean()) {
            return jsonPrimitive.getAsBoolean();
        } else if (jsonPrimitive.isNumber()) {
            return jsonPrimitive.getAsNumber().intValue() == 1;
        } else if (jsonPrimitive.isString()) {
            return TRUE_STRINGS.contains(jsonPrimitive.getAsString().toLowerCase());
        }

        return false;
    }
}

该类在 Kotlin 或 Java 中都无效。 - Zoe stands with Ukraine
1
我同意 :) 我编辑了,只是注意力不够。 - Beyazid
请参阅 https://dev59.com/7oDba4cB1Zd3GeqPAB-s。在那里,可以从JSON中删除空对象和数组,但需要将其从一个字符串转换为另一个字符串。我仍在寻找创建生成器的方法。 - CoolMind
3个回答

5
如果 XModel 类不是很大,您可以按照以下方式编写自定义的反序列化器,在其中控制传入元素:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;

import java.io.File;
import java.io.FileReader;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class GsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        Gson gson = new GsonBuilder()
                .registerTypeAdapter(XModel.class, new XModelJsonDeserializer())
                .create();

        System.out.println(gson.fromJson(new FileReader(jsonFile), XModel.class));
    }
}

class XModelJsonDeserializer implements JsonDeserializer<XModel> {

    private final Set<String> TRUE_STRINGS = new HashSet<>(Arrays.asList("true", "1", "yes"));

    @Override
    public XModel deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        XModel response = new XModel();
        JsonObject jsonResponse = (JsonObject) json;
        response.setName(jsonResponse.get("name").getAsString());
        // other fields

        JsonElement dataElement = jsonResponse.get("isValid");
        if (dataElement.isJsonNull()) {
            response.setValid(false);
        } else if (dataElement.isJsonPrimitive()) {
            JsonPrimitive jsonPrimitive = dataElement.getAsJsonPrimitive();
            if (jsonPrimitive.isBoolean()) {
                response.setValid(jsonPrimitive.getAsBoolean());
            } else if (jsonPrimitive.isNumber()) {
                response.setValid(jsonPrimitive.getAsNumber().intValue() == 1);
            } else if (jsonPrimitive.isString()) {
                response.setValid(TRUE_STRINGS.contains(jsonPrimitive.getAsString()));
            }
            System.out.println("Json data is primitive: " + dataElement.getAsString());
        } else if (dataElement.isJsonObject() || dataElement.isJsonArray()) {
            response.setValid(true); //?!?!
        }

        return response;
    }
}

以下是需要翻译的 JSON 负载:

{
  "name" : "john doe",
  "isValid" : true
}

上面的程序输出:
Json data is primitive: true
XModel{name='john doe', isValid=true}

针对 JSON 负载:

{
  "name" : "john doe",
  "isValid" : 1
}

输出:

Json data is primitive: 1
XModel{name='john doe', isValid=true}

你的模型很清晰,因为所有的工作都在反序列化器层面上完成。

更加精确的解决方案是仅序列化 primitive 数据类型。假设模型如下:

class XModel {

    private String name;

    @JsonAdapter(value = BooleanJsonDeserializer.class)
    private boolean isValid;

    // getters, setters
}

我们的BooleanJsonDeserializer反序列化程序如下所示:

class BooleanJsonDeserializer implements JsonDeserializer<Boolean> {

    private final Set<String> TRUE_STRINGS = new HashSet<>(Arrays.asList("true", "1", "yes"));

    @Override
    public Boolean deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        System.out.println(json);
        JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive();
        if (jsonPrimitive.isBoolean()) {
            return jsonPrimitive.getAsBoolean();
        } else if (jsonPrimitive.isNumber()) {
            return jsonPrimitive.getAsNumber().intValue() == 1;
        } else if (jsonPrimitive.isString()) {
            return TRUE_STRINGS.contains(jsonPrimitive.getAsString().toLowerCase());
        }

        return false;
    }
}

你只需要在你的模型中使用这个适配器来注释每个boolean属性,它就可以处理:1True等值。


感谢@Michał Ziober的回复。如果只有几个类,这种方法可能是可接受的。但我需要一个适用于整个模型的解决方案。 - Beyazid
1
@Beyazidy,我知道这可能是一个有问题的解决方案。我的更新展示了一个新的反序列化器,只针对布尔原语。它处理了你需要的所有情况。希望在你的情况下会更好。 - Michał Ziober
1
谢谢@Michał Ziober,这个反序列化程序可用 :) - Beyazid

1
我不认为有一种简单的方法来执行这种映射,但以下内容可能会有所帮助。
public void setIsValid(Object isValid) {
    String isValidString = String.valueOf(isValid).replace("0", "false").replace("1", "true");
    return Boolean.valueOf(isValidString);
}

感谢您的回复,@Yassin Hajaj。您知道这不是最优解决方案,而且会导致重复的代码。但它能够工作。 - Beyazid

0
你可以查看Apache Commons Lang中的BooleanUtilities。其中有一种方法可以将不同类型的字符串(和其他对象)解析为布尔值。
System.out.println(BooleanUtils.toBoolean(1));
System.out.println(BooleanUtils.toBoolean(true));
System.out.println(BooleanUtils.toBoolean("TrUe"));
System.out.println(BooleanUtils.toBoolean("true"));

输出

true
true
true
true

然而 BooleanUtils.toBoolean("1");false,所以你可以像这样组合它:

String isValid = jsonPrimitive.get("isValid").getAsString();
System.out.println(BooleanUtils.toBoolean(isValid) || isValid.equals("1"));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接