如何在android中使用gson反序列化json时,为POJO中的变量设置不同的类型(参见示例)

时间:2015-09-28 14:22:04

标签: gson json-deserialization android-json

忍受我的英语。我有一个简单的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);
   }
}

两个类都有相同的setter方法,相同的@SerializedName注释标记。只更改了Hint2对象的类型。但后者抛出以下所示的异常

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

所以我相信反序列化取决于什么样的变量&#34; Hint2&#34;是。
有没有办法让它与ObservableField一起使用而不是使用String?

我尝试这个的原因是android绑定库,它支持将对象直接绑定到xml文件。当POJO中的相应值发生变化时,ObservableField会自动更新UI。


更新
gson design document有此

使用字段与getter来指示Json元素
一些Json库使用类型的getter来推导出Json元素。我们选择使用非瞬态,静态或合成的所有字段(继承层次结构)。我们这样做是因为并非所有类都使用适当命名的getter编写。此外,getXXX或isXXX可能是语义而不是指示属性。
但是,也有很好的理由来支持物业。我们打算在后一版本中增强Gson以支持属性作为指示Json字段的备用映射。 目前,Gson是基于字段的。


所以这表明Gson是基于字段的。这几乎回答了我的问题,但仍在等待,如果有人对此有所了解。

2 个答案:

答案 0 :(得分:1)

我遇到了相同的要求,并最终解决了,这里是步骤:

  1. 创建类GsonUtils:
  2. 
    
        public class GsonUtils {
            // code following
            //...
        }
    
    
    

    以下代码在此类

    1. 编写一个定制的序列化器&amp;解串器:
    2. 
          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:
      2. 
        
            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. 创建反序列化器
        2. 使用的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;
              }
          
          
          

          这一切,希望有所帮助

答案 1 :(得分:0)

我刚刚遇到了我认为同样的问题,这是一个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接口,它允许提取包含的类类型。 Jackson提供了几种注册自定义反序列化器的选项,所以这只是一种方法。此外,如果您将其用于实数,那么在反序列化器中覆盖getNullValue可能是个好主意。