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

我按顺序得到回应:

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

如果我调用List参数,则会出现错误:

预计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 sendOneWay(@Body Query data); @SerializedName("FlightDetails") public ApiResponse FlightDetails; 

现在我打电话给ApiResponse类但是如何调用公共ApiResponse FlightDetails; &public List FlightDetails;

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

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

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

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

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

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

为简单起见,请考虑以下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> 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  TypeAdapter create(final Gson gson, final TypeToken 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 castTypeAdapter = (TypeAdapter) new AlwaysListTypeAdapter<>( (TypeAdapter) gson.getAdapter(TypeToken.get(getTypeParameter0(typeToken.getType()))), (TypeAdapter>) 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 final ParameterizedType parameterizedType = (ParameterizedType) type; return parameterizedType.getActualTypeArguments()[0]; } private static final class AlwaysListTypeAdapter extends TypeAdapter> { private final TypeAdapter elementTypeAdapter; private final TypeAdapter> listTypeAdapter; private AlwaysListTypeAdapter(final TypeAdapter elementTypeAdapter, final TypeAdapter> listTypeAdapter) { this.elementTypeAdapter = elementTypeAdapter; this.listTypeAdapter = listTypeAdapter; } @Override public void write(final JsonWriter out, final List value) throws IOException { listTypeAdapter.write(out, value); } @Override public List 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 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}]