GSON преобразует целочисленное значение в логическое значение динамически для определенных полей - PullRequest
4 голосов
/ 10 марта 2019

Как я могу получить поле с одинаковым именем, но с разными типами?Я получаю иногда целочисленное значение, иногда логическое значение от API в том же запросе.Интересно, как справиться, когда я получу Json, как эти.Я создал адаптер типа, но он не работает

Я думал о создании различных классов POJO.Но эта проблема не только для одного запроса.По этой причине я не предпочитаю создавать POJO.Кстати, я видел похожие вопросы, но это не решает мою проблему.

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

иногда я получаю int

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

При получении целого числа

* я получаю неожиданное исключение json1010 *

Я хочу возвращать логическое значение для каждого запроса.Кто-нибудь знает, как решить эту проблему?

Редактировать: Я хочу предотвратить использование ключевого слова instanceOf с помощью Type Adapter


Решение: @ 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;
    }
}

Ответы [ 3 ]

3 голосов
/ 11 марта 2019

Если 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 с помощью этого адаптера в вашей модели, и он готов к обработке: 1, True и т. Д.

1 голос
/ 10 марта 2019

Я не верю, что это сопоставление легко выполнить, но, вероятно, может помочь следующее.

public void setIsValid(Object isValid) {
    String isValidString = String.valueOf(isValid).replace("0", "false").replace("1", "true");
    return Boolean.valueOf(isValidString);
}
0 голосов
/ 11 марта 2019

Вы можете посмотреть на BooleanUtilities из Apache Commons Lang . Существует метод, в котором вы можете анализировать различные типы строк (и других объектов) в логическое значение.

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