Retrofit和Gson:解析数组/元素多态对象

时间:2017-04-26 12:22:49

标签: java gson retrofit2

我按顺序得到回复:

 "parameters": {
        "parameter": {
                     "Data":"value"
                     }
              },

 "parameters":{
        "parameter": [
                     {
                     "Data":"value"
                     },
                     {
                     "Data":"value"
                     },
                     ]
              },

如果我调用List<Class>参数:

,则会收到错误消息
  

预计BEGIN_OBJECT但获得BEGIN_ARRAY

我需要解析参数来获取值

public class ApiClient  {
public static final String BASE_URL ="http://.........";

private static Retrofit retrofit = null;

public static Retrofit getClient() {
    OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(1, TimeUnit.MINUTES)
            .writeTimeout(1, TimeUnit.MINUTES)
            .readTimeout(1, TimeUnit.MINUTES)
            .addInterceptor(new ServiceGenerator("Content-Type","application/json")).build();

    Gson gson = new GsonBuilder()
            .setLenient()
            .create();
    if (retrofit==null) {
        retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .client(client)
                .build();
    }
    return retrofit;
}

}

public class ServiceGenerator implements Interceptor{
private String httpUsername;
private String httpPassword;

public ServiceGenerator(String httpUsername, String httpPassword) {
    this.httpUsername = httpUsername;
    this.httpPassword = httpPassword;
}

@Override
public Response intercept(Chain chain) throws IOException {
    Request newRequest = chain.request().newBuilder()

            .addHeader("Authorization", getAuthorizationValue())

            .build();

    return chain.proceed(newRequest);
}

private String getAuthorizationValue() {
    final String userAndPassword = httpUsername + ":" + httpPassword;
    return "Basic " + Base64.encodeToString(userAndPassword.getBytes(), Base64.NO_WRAP);
}

}

@POST("OneWay.json")
Call<ApiResponse> sendOneWay(@Body Query data);

@SerializedName("FlightDetails")
public ApiResponse FlightDetails;

现在我打电话给ApiResponse 但如何调用两者 public ApiResponse FlightDetails; &安培; public List FlightDetails;

3 个答案:

答案 0 :(得分:1)

这只是一个非常微不足道的问题,通常会出现具有奇怪设计选择的API。你只需要&#34;对齐&#34;这两种格式都是统一的形式:列表可以涵盖两种情况。因此,您需要实现的只是一个类型适配器,它将检查是否需要这样的对齐,如果值是列表,则使用原始类型适配器,或者将其包装在单个元素列表中。

为简单起见,请考虑以下JSON文档:

single.json

{
    "virtual": {
        "key-1": "value-1"
    }
}

multiple.json

{
    "virtual": [
        {
            "key-1": "value-1"
        },
        {
            "key-2": "value-2"
        }
    ]
}

现在使用对齐字段定义映射:

final class Response {

    @JsonAdapter(AlwaysListTypeAdapterFactory.class)
    final List<Map<String, String>> virtual = null;

}

注意JsonAnnotaion注释:这是告诉Gson必须如何读取或写入字段的方法。 AlwaysListTypeAdapterFactory实现可能如下:

final class AlwaysListTypeAdapterFactory
        implements TypeAdapterFactory {

    // Always consider making constructors private
    // + Gson can instantiate this factory itself   
    private AlwaysListTypeAdapterFactory() {
    }

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        // Not a list?
        if ( !List.class.isAssignableFrom(typeToken.getRawType()) ) {
            // Not something we can to deal with
            return null;
        }
        // Now just return a special type adapter that could detect how to deal with objects
        @SuppressWarnings("unchecked")
        final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) new AlwaysListTypeAdapter<>(
                (TypeAdapter<Object>) gson.getAdapter(TypeToken.get(getTypeParameter0(typeToken.getType()))),
                (TypeAdapter<List<Object>>) gson.getAdapter(typeToken)
        );
        return castTypeAdapter;
    }

    // This is used to detect the list parameterization
    private static Type getTypeParameter0(final Type type) {
        if ( !(type instanceof ParameterizedType) ) {
            // Is it a wildcard or raw type? Then we cannot determine the real parameterization
            return Object.class;
        }
        // Or just resolve the actual E in List<E>
        final ParameterizedType parameterizedType = (ParameterizedType) type;
        return parameterizedType.getActualTypeArguments()[0];
    }

    private static final class AlwaysListTypeAdapter<E>
            extends TypeAdapter<List<E>> {

        private final TypeAdapter<E> elementTypeAdapter;
        private final TypeAdapter<List<E>> listTypeAdapter;

        private AlwaysListTypeAdapter(final TypeAdapter<E> elementTypeAdapter, final TypeAdapter<List<E>> listTypeAdapter) {
            this.elementTypeAdapter = elementTypeAdapter;
            this.listTypeAdapter = listTypeAdapter;
        }

        @Override
        public void write(final JsonWriter out, final List<E> value)
                throws IOException {
            listTypeAdapter.write(out, value);
        }

        @Override
        public List<E> read(final JsonReader in)
                throws IOException {
            final JsonToken token = in.peek();
            switch ( token ) {
            case BEGIN_ARRAY:
                // If the next token is [, assume is a normal list, and just delegate the job to Gson internals
                return listTypeAdapter.read(in);
            case BEGIN_OBJECT:
            case STRING:
            case NUMBER:
            case BOOLEAN:
            case NULL:
                // Any other value? Wrap it up ourselves, but use the element type adapter
                // Despite Collections.singletonList() might be used, Gson returns mutable ArrayList instances, so we do...
                final List<E> list = new ArrayList<>();
                list.add(elementTypeAdapter.read(in));
                return list;
            case END_ARRAY:
            case END_OBJECT:
            case NAME:
            case END_DOCUMENT:
                // Something terrible here...
                throw new MalformedJsonException("Unexpected token: " + token + " at " + in);
            default:
                // If someday Gson adds a new token
                throw new AssertionError(token);
            }
        }

    }

}

测试:

public static void main(final String... args)
        throws IOException {
    for ( final String resource : ImmutableList.of("single.json", "multiple.json") ) {
        try ( final Reader reader = getPackageResourceReader(Q43634110.class, resource) ) {
            final Response response = gson.fromJson(reader, Response.class);
            System.out.println(resource);
            System.out.println("\t" + response.virtual);
        }
    }
}

输出:

  

single.json
    [{键-1 =值1}]
  multiple.json
    [{key-1 = value-1},{key-2 = value-2}]

答案 1 :(得分:0)

您可以使用此网站为您生成java对象 http://www.jsonschema2pojo.org/只需输入json响应并选择Json作为Source类型,选择Gson作为Annotation样式。

将生成的java类复制到您的应用程序并将其用于改进响应。

答案 2 :(得分:0)

你在这里遇到的问题是,对于相同的json字段,你有不同的类型。因此,第一次获取JSON对象时,第二次使用JSON数组,这显然会因严格定义为解析为数组(List)而崩溃。

你需要动态地处理这个案例,或者由API人员要求修复坏的数据结构,这似乎是你要回来的(除非它是故意的)。

要更好地理解JSON类型,请阅读此http://www.json.org/