用于数组或对象的Json响应解析器

用于数组或对象的Json响应解析器,第1张

用于数组或对象的Json响应解析器

有时API响应不适合像Java这样的静态类型的语言。我要说的是,如果您遇到的问题是使用不太方便的响应格式,那么如果您希望 方便的
话就必须编写更多代码。在大多数情况下,Gson可以在这种情况下提供帮助,但并非免费提供。

有什么方法可以对此POJO进行建模,并使Gson无需手动处理这种情况就能识别出这种结构?

不会。Gson不会混合使用不同结构的对象,因此您仍然必须告诉它您的意图。

有没有更好的方法可以做到这一点?

我想是的,既可以对响应进行建模,又可以实现解析此类响应的方式。

我是否错过了解串器可能会失败或无法按预期工作的任何情况?

像所有反序列化器一样,它对响应格式也很敏感,因此通常它足够好,但是可以改进。

首先,让我们考虑您只能有两种情况:常规响应和错误。这是一个经典案例,可以这样建模:

abstract class ApiResponse<T> {    // A bunch of protected methods, no interface needed as we're considering it's a value type and we don't want to expose any of them    protected abstract boolean isSuccessful();    protected abstract T getData() throws UnsupportedOperationException;    protected abstract List<ApiResponseError> getErrors() throws UnsupportedOperationException;    // Since we can cover all two cases ourselves, let them all be here in this class    private ApiResponse() {    }    static <T> ApiResponse<T> success(final T data) {        return new SucceededApiResponse<>(data);    }    static <T> ApiResponse<T> failure(final List<ApiResponseError> errors) {        @SuppressWarnings("unchecked")        final ApiResponse<T> castApiResponse = (ApiResponse<T>) new FailedApiResponse(errors);        return castApiResponse;    }    // Despite those three protected methods can be technically public, let's encapsulate the state    final void accept(final IApiResponseConsumer<? super T> consumer) {        if ( isSuccessful() ) { consumer.acceptSuccess(getData());        } else { consumer.acceptFailure(getErrors());        }    }    // And make a couple of return-friendly accept methods    final T acceptOrNull() {        if ( !isSuccessful() ) { return null;        }        return getData();    }    final T acceptOrNull(final Consumer<? super List<ApiResponseError>> errorsConsumer) {        if ( !isSuccessful() ) { errorsConsumer.accept(getErrors()); return null;        }        return getData();    }    private static final class SucceededApiResponse<T> extends ApiResponse<T> {        private final T data;        private SucceededApiResponse(final T data) { this.data = data;        }        @Override        protected boolean isSuccessful() { return true;        }        @Override        protected T getData() { return data;        }        @Override        protected List<ApiResponseError> getErrors()     throws UnsupportedOperationException { throw new UnsupportedOperationException();        }    }    private static final class FailedApiResponse extends ApiResponse<Void> {        private final List<ApiResponseError> errors;        private FailedApiResponse(final List<ApiResponseError> errors) { this.errors = errors;        }        @Override        protected boolean isSuccessful() { return false;        }        @Override        protected List<ApiResponseError> getErrors() { return errors;        }        @Override        protected Void getData()     throws UnsupportedOperationException { throw new UnsupportedOperationException();        }    }}interface IApiResponseConsumer<T> {    void acceptSuccess(T data);    void acceptFailure(List<ApiResponseError> errors);}

一个简单的错误映射:

final class ApiResponseError {    // Since incoming DTO are read-only data bags in most-most cases, even getters may be noise here    // Gson can strip off the final modifier easily    // However, primitive values are inlined by javac, so we're cheating javac with Integer.valueOf    final int pre = Integer.valueOf(0);    final String message = null;}

还有一些值:

final class Person {    final String name = null;    final int age = Integer.valueOf(0);}

第二个组件是一种特殊类型的适配器来告诉GSON 如何
API响应必须反序列化。请注意,类型适配器不同于以流方式工作

JsonSerializer
并且
JsonDeserializer
不需要将整个JSON模型(
JsonElement
)存储在内存中,因此可以节省内存并提高大型JSON文档的性能。

final class ApiResponseTypeAdapterFactory        implements TypeAdapterFactory {    // No state, so it can be instantiated once    private static final TypeAdapterFactory apiResponseTypeAdapterFactory = new ApiResponseTypeAdapterFactory();    // Type tokens are effective value types and can be instantiated once per parameterization    private static final TypeToken<List<ApiResponseError>> apiResponseErrorsType = new TypeToken<List<ApiResponseError>>() {    };    private ApiResponseTypeAdapterFactory() {    }    static TypeAdapterFactory getApiResponseTypeAdapterFactory() {        return apiResponseTypeAdapterFactory;    }    @Override    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {        // Is it ApiResponse, a class we can handle?        if ( ApiResponse.class.isAssignableFrom(typeToken.getRawType()) ) { // Trying to resolve its parameterization final Type typeParameter = getTypeParameter0(typeToken.getType()); // And asking Gson for the success and failure type adapters to use downstream parsers final TypeAdapter<?> successTypeAdapter = gson.getDelegateAdapter(this, TypeToken.get(typeParameter)); final TypeAdapter<List<ApiResponseError>> failureTypeAdapter = gson.getDelegateAdapter(this, apiResponseErrorsType); @SuppressWarnings("unchecked") final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) new ApiResponseTypeAdapter<>(successTypeAdapter, failureTypeAdapter); return castTypeAdapter;        }        return null;    }    private static Type getTypeParameter0(final Type type) {        // Is this type parameterized?        if ( !(type instanceof ParameterizedType) ) { // No, it's raw return Object.class;        }        final ParameterizedType parameterizedType = (ParameterizedType) type;        return parameterizedType.getActualTypeArguments()[0];    }    private static final class ApiResponseTypeAdapter<T> extends TypeAdapter<ApiResponse<T>> {        private final TypeAdapter<T> successTypeAdapter;        private final TypeAdapter<List<ApiResponseError>> failureTypeAdapter;        private ApiResponseTypeAdapter(final TypeAdapter<T> successTypeAdapter, final TypeAdapter<List<ApiResponseError>> failureTypeAdapter) { this.successTypeAdapter = successTypeAdapter; this.failureTypeAdapter = failureTypeAdapter;        }        @Override        public void write(final JsonWriter out, final ApiResponse<T> value)     throws UnsupportedOperationException { throw new UnsupportedOperationException();        }        @Override        public ApiResponse<T> read(final JsonReader in)     throws IOException { final JsonToken token = in.peek(); switch ( token ) { case BEGIN_ARRAY:     // Is it array? Assuming that the responses come as arrays only     // Otherwise a more complex parsing is required probably replaced with JsonDeserializer for some cases     // So reading the next value (entire array) and wrapping it up in an API response with the success-on state     return success(successTypeAdapter.read(in)); case BEGIN_OBJECT:     // Otherwise it's probably an error object?     in.beginObject();     final String name = in.nextName();     if ( !name.equals("errors") ) {         // Let it fail fast, what if a successful response would be here?         throw new MalformedJsonException("Expected errors` but was " + name);     }     // Constructing a failed response object and terminating the error object     final ApiResponse<T> failure = failure(failureTypeAdapter.read(in));     in.endObject();     return failure; // A matter of style, but just to show the intention explicitly and make IntelliJ IDEA "switch on enums with missing case" to not report warnings here case END_ARRAY: case END_OBJECT: case NAME: case STRING: case NUMBER: case BOOLEAN: case NULL: case END_document:     throw new MalformedJsonException("Unexpected token: " + token); default:     throw new AssertionError(token); }        }    }}

现在,如何将它们放在一起。请注意,响应不会显式地公开其内部,而是要求消费者接受将其私有项真正封装起来。

public final class Q43113283 {    private Q43113283() {    }    private static final String SUCCESS_JSON = "[{"name":"John","age":21},{"name":"Sarah","age":32}]";    private static final String FAILURE_JSON = "{"errors":[{"pre":1001,"message":"Something blew up"}]}";    private static final Gson gson = new GsonBuilder() .registerTypeAdapterFactory(getApiResponseTypeAdapterFactory()) .create();    // Assuming that the Type instance is immutable under the hood so it might be cached    private static final Type personsApiResponseType = new TypeToken<ApiResponse<List<Person>>>() {    }.getType();    @SuppressWarnings("unchecked")    public static void main(final String... args) {        final ApiResponse<Iterable<Person>> successfulResponse = gson.fromJson(SUCCESS_JSON, personsApiResponseType);        final ApiResponse<Iterable<Person>> failedResponse = gson.fromJson(FAILURE_JSON, personsApiResponseType);        useFullyCallbackApproach(successfulResponse, failedResponse);        useSemiCallbackApproach(successfulResponse, failedResponse);        useNoCallbackApproach(successfulResponse, failedResponse);    }    private static void useFullyCallbackApproach(final ApiResponse<Iterable<Person>>... responses) {        System.out.println("<FULL CALLBACKS>");        final IApiResponseConsumer<Iterable<Person>> handler = new IApiResponseConsumer<Iterable<Person>>() { @Override public void acceptSuccess(final Iterable<Person> people) {     dumpPeople(people); } @Override public void acceptFailure(final List<ApiResponseError> errors) {     dumpErrors(errors); }        };        Stream.of(responses)     .forEach(response -> response.accept(handler));    }    private static void useSemiCallbackApproach(final ApiResponse<Iterable<Person>>... responses) {        System.out.println("<SEMI CALLBACKS>");        Stream.of(responses)     .forEach(response -> {         final Iterable<Person> people = response.acceptOrNull(Q43113283::dumpErrors);         if ( people != null ) {  dumpPeople(people);         }     });    }    private static void useNoCallbackApproach(final ApiResponse<Iterable<Person>>... responses) {        System.out.println("<NO CALLBACKS>");        Stream.of(responses)     .forEach(response -> {         final Iterable<Person> people = response.acceptOrNull();         if ( people != null ) {  dumpPeople(people);         }     });    }    private static void dumpPeople(final Iterable<Person> people) {        for ( final Person person : people ) { System.out.println(person.name + " (" + person.age + ")");        }    }    private static void dumpErrors(final Iterable<ApiResponseError> errors) {        for ( final ApiResponseError error : errors ) { System.err.println("ERROR: " + error.pre + " " + error.message);        }    }}

上面的代码将产生:


John(21)
Sarah(32)
错误:1001炸毁了

John(21)
Sarah(32)
错误:1001炸毁了

John(21)
Sarah(32)



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4911696.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-12
下一篇 2022-11-12

发表评论

登录后才能评论

评论列表(0条)

保存