Как установить другой тип для переменной в POJO, чем ожидалось, при десериализации json с помощью gson в android (см. Пример)

Потерпи мой английский. У меня простой json,

{
    "Hint2": "L"
}

это POJO, который работает.

 public class Hints {
    @SerializedName("Hint2")
    @Expose
    private String Hint2;

   public void setHint1(Object Hint2) {
      this.Hint2 = (Hint2);
   }
 }

я хочу изменить это на

public class Hints {
    @SerializedName("Hint2")
    @Expose
    public final ObservableField<String> Hint2 = new ObservableField<>();

   public void setHint2(String Hint2) {
      this.Hint2.set(Hint2);
   }
}

оба класса имеют один и тот же метод установки, один и тот же тег аннотации @SerializedName. изменяется только тип объекта Hint2. но последний выдает исключение, показанное ниже

Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at..

поэтому я считаю, что десериализация зависит от типа переменной «Hint2».
Есть ли способ заставить его работать с ObservableField вместо использования String?

Причина, по которой я пытаюсь это сделать, - это библиотека привязки Android, которая поддерживает привязку объектов непосредственно к файлам xml. а ObservableField автоматически обновляет пользовательский интерфейс при изменении соответствующего значения в POJO.


Обновление:
дизайн gson документ имеет это

Использование полей и геттеров для обозначения элементов Json
Некоторые библиотеки Json используют геттеры типа для вывода элементов Json. Мы решили использовать все поля (вверх по иерархии наследования), которые не являются временными, статическими или синтетическими. Мы сделали это, потому что не все классы написаны с подходящими именами геттеров. Более того, getXXX или isXXX могут быть семантическими, а не указывать свойства.
Однако есть и веские аргументы в пользу поддержки свойств. Мы намерены улучшить Gson в последней версии для поддержки свойств в качестве альтернативного сопоставления для указания полей Json. На данный момент Gson основан на полях.


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


person Ankit    schedule 28.09.2015    source источник


Ответы (2)


Я столкнулся с теми же требованиями и, наконец, решил их, вот шаги:

  1. создайте класс GsonUtils:


    public class GsonUtils {
        // code following
        //...
    }


следующий код находится в этом классе

  1. напишите настраиваемый сериализатор и десериализатор:

    private static class ObservableFieldSerializerDeserializer implements JsonSerializer>, JsonDeserializer> {

    @Override
    public JsonElement serialize(ObservableField src, Type typeOfSrc, JsonSerializationContext context) {
        return context.serialize(src.get());
    }

    @Override
    public ObservableField deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            final Type type = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
            return new ObservableField((T) GsonUtils.getGson().fromJson(json, type));
        }
    }

  1. вам необходимо зарегистрировать типы ObservableField в Gson:


    private static GsonBuilder createGsonBuilder() {
        final GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.registerTypeAdapter(new TypeToken<ObservableField<String>>(){}.getType(), new ObservableFieldSerializerDeserializer<String>());
        ...// register more types which are wrapped by ObservableFields
        return gsonBuilder;
    }


  1. создать Gson, который используется десериализатором


    private static final Gson sGson = createGson();

    private static Gson createGson() {
        return createGsonBuilder().create();
    }

    // this is used by the deserializer
    public static Gson getGson() {
        return sGson;
    }


вот и все, надеюсь, это поможет

person yangrenyong    schedule 08.06.2016
comment
вы можете заменить GsonUtils.getGson().fromJson( на context.deserialize( - person melanke; 29.03.2018

Я только что столкнулся с той же проблемой, и вот тест JUnit4, показывающий, как я решил эту проблему с помощью Jackson для POJO, но, конечно, String тоже подойдет.

public class ObservableDeserializationTest {

private static class ObservableDeserializer extends JsonDeserializer<ObservableField> implements ContextualDeserializer {

    private Class<?> mTargetClass;

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
        mTargetClass = property.getType().containedType(0).getRawClass();
        return this;
    }

    @Override
    public ObservableField deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        ObservableField result = new ObservableField();
        result.set(p.readValueAs(mTargetClass));
        return result;
    }
}

private static class SomePojo {
    public String id;
    public String name;
}

private static class ObservableTestClass {

    @JsonDeserialize(using = ObservableDeserializer.class)
    public ObservableField<SomePojo> testObj = new ObservableField<>();

}

@Test
public void DeserializingAnObservableObjectShouldSetValueCorrectly() {
    ObservableTestClass tc = null;
    try {
        tc = new ObjectMapper().readValue("{\"testObj\":{\"name\":\"TestName\",\"id\":\"TestId\"}}", ObservableTestClass.class);
    } catch (IOException e) {
        e.printStackTrace();
    }
    Assert.assertEquals("TestName", tc.testObj.get().name);
    Assert.assertEquals("TestId", tc.testObj.get().id);
}

}

Ключевым моментом является интерфейс ContextualDeserializer, который позволяет извлекать содержащийся тип класса. Джексон предоставляет несколько вариантов регистрации настраиваемого десериализатора, так что это лишь один из способов сделать это. Кроме того, вероятно, было бы неплохо переопределить getNullValue в десериализаторе, если бы вы использовали это по-настоящему.

person Anders Jändel    schedule 09.11.2015