使用Retrofit GsonConverterFactory反序列化错误或对象的json

时间:2017-04-05 12:59:46

标签: java android json gson retrofit2

我正在使用返回json的服务器。其中一个元素要么是对象要么是假的 - 它不存在。我知道服务器响应的实现很差,并且有很多这样的情况,但这是我必须要处理的。我该如何处理这种情况?如果有一个对象我成功反序列化它,如果没有 - 我得到错误 - 预期对象找到BOOLEAN。 更糟糕的是,我不知道将来我会在这个项目中遇到这种情况。 这是示例json:

    {
  "course": {
    "id": "47902",
    "course": "3844",
    "group": "1825",
    "teacher": "59502",
    "table": "1447",
    "client": "1",
    "course_id": "3844",
    "description": ""
  },
  "teacher": {
    "id": "59502",
    "post": "0",
    "experience": "",
    "dep_experience": "",
    "rank": "0",
    "online": "1458891283",
    "departments": [
      null
    ]
  },
  "depart": {
    "id": "100",
    "postcode": "",
    "public": "1",
    "alias": "",
    "faculty": "97",
    "client": "1"
  },
  "progress": false,
  "files": [
    {
      "teacher": {
        "id": "59502",
        "code": "53bd7c21ad05b03e",
        "photo": "1"
      },
      "files": [
        {
          "id": "0ffe41e5003ee5c0",
          "owner": "59502",
          "address": "0ffe41e5003ee5c0",
          "type": "f",
          "size": "0",
          "time": "2015-07-10 14:39:15",
          "data": ""
        }
      ]
    }
  ]
}

正如您所见,progress在这里是假的。其他时候它是普通的对象,如depart。反序列化由Retrofit 2完成。

非常感谢。

1 个答案:

答案 0 :(得分:1)

我假设您有一个类似于以下内容的顶级映射,并为Retrofit配置了Gson个实例:

final class Response {

    @SerializedName("progress")
    @JsonAdapter(FalseAsNullTypeAdapterFactory.class)
    final Progress progress = null;

}

final class Progress {

    final String foo = null;

}

请注意,progress属性是使用@JsonAdapter注释注释的:我们假设这只是progress属性可以是布尔值的地方(如果你有很多)像这样的地方,您可以使用此注释注释每个字段,或.registerTypeAdapter()通过GsonBuilder;如果是.registerTypeAdapterFactory(),工厂必须检查已知类型,以便不#34}拦截"所有类型)。

现在,这是一个处理您的问题的类型适配器工厂:

final class FalseAsNullTypeAdapterFactory
        implements TypeAdapterFactory {

    // Let Gson instantiate it itself
    private FalseAsNullTypeAdapterFactory() {
    }

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        // Get a downstream parser (for simplicity: get the default parser for the given type)
        final TypeAdapter<T> delegateTypeAdapter = gson.getDelegateAdapter(this, typeToken);
        return new TypeAdapter<T>() {
            @Override
            public void write(final JsonWriter out, final T value) {
                throw new UnsupportedOperationException();
            }

            @Override
            public T read(final JsonReader in)
                    throws IOException {
                // Peek whether the next JSON token is a boolean
                if ( in.peek() == BOOLEAN ) {
                    // And take the this JSON token as a boolean value
                    // Is it true?
                    if ( in.nextBoolean() ) {
                        // Then it's not something we can handle -- probably a boolean field annotated with @JsonAdapter(FalseAsNullTypeAdapterFactory.class)?
                        throw new MalformedJsonException("Unexpected boolean marker: true");
                    }
                    // We're assuming it's null
                    return null;
                }
                // If it's not a boolean value, then we just delegate parsing to the original type adapter
                return delegateTypeAdapter.read(in);
            }
        };
    }

}

现在就测试一下:

try ( final Reader reader = getPackageResourceReader(Q43231983.class, "success.json") ) {
    final Response response = gson.fromJson(reader, Response.class);
    System.out.println(response.progress.foo);
}
try ( final Reader reader = getPackageResourceReader(Q43231983.class, "failure.json") ) {
    final Response response = gson.fromJson(reader, Response.class);
    System.out.println(response.progress);
}

给定资源是:

  • success.json{"progress":{"foo": "bar"}};
  • failure.json{"progress":false}

输出如下:

  


  null