我在使用外部 API 的 Android 应用程序中使用 Retrofit。问题在于我无法弄清楚如何反序列化以返回对象列表的响应。我得到的 JSON 格式如下:
{
"attribute_1": "value",
"attribute_2": "value",
"member_1": {
"param1": "value1",
"param2": "value2"
},
"member_2": {
"param1": "value1",
"param2": "value2"
},
...
}
Retrofit 中的 API 调用如下所示:
@GET("apiednpoint/path")
Call<List<Member>> getMembers();
我想忽略属性并检索 List<Member>
从这个回应。我知道我可以创建一个自定义反序列化器来忽略 JSON 中的某些字段,例如 here并将成员转换为数组,如 here ,但在第二个链接中,我需要一个来 self 的 List<Member>
的包装类我所期望的。是否可以不用包装器来完成我所期望的列表/数组?
最佳答案
它不是免费的,但可以通过 Retrofit 实现(对于独立的 Gson 来说并不容易,因为它需要更多的“魔法”)。以下解决方案远非完美,但您可以根据需要进行改进。假设您有以下 Member
映射:
final class Member {
final String param1 = null;
final String param2 = null;
}
以及以下服务:
interface IService {
@GET("/")
@ByRegExp("member_.+")
Call<List<Member>> getMembers();
}
请注意,@ByRegExp
是一个自定义注释,将在下面进行处理。注解声明:
@Retention(RUNTIME)
@Target(METHOD)
@interface ByRegExp {
String value();
}
现在,以下代码借用了我的调试/模拟代码中的一些代码,但它可以轻松转换为您的真实代码:
// This is just a mocked HTTP client that always returns your members.json
final OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(staticResponse(Q43925012.class, "members.json"))
.build();
// Gson stuff
final Gson gson = new GsonBuilder()
// ... configure your Gson here ...
.create();
final Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://whatever")
.client(client)
.addConverterFactory(new Converter.Factory() {
@Override
public Converter<ResponseBody, ?> responseBodyConverter(final Type type, final Annotation[] annotations, final Retrofit retrofit) {
// Checking if the method is declared with @ByRegExp annotation
final ByRegExp byRegExp = findByRegExp(annotations);
if ( byRegExp != null ) {
// If so, then compile the regexp pattern
final Pattern pattern = Pattern.compile(byRegExp.value());
// And resolve the list element type
final Type listElementType = getTypeParameter0(type);
// Obtaining the original your-type list type adapter
final TypeAdapter<?> listElementTypeAdapter = gson.getAdapter(TypeToken.get(listElementType));
return (Converter<ResponseBody, Object>) responseBody -> {
try {
// Getting input stream from the response body and converting it to a JsonReader -- a low level JSON parser
final JsonReader jsonReader = new JsonReader(new InputStreamReader(responseBody.byteStream()));
final List<Object> list = new ArrayList<>();
// Make sure that the first token is `{`
jsonReader.beginObject();
// And iterate over each JSON property
while ( jsonReader.hasNext() ) {
final String name = jsonReader.nextName();
final Matcher matcher = pattern.matcher(name);
// Check if the property need matches the pattern
if ( matcher.matches() ) {
// And if so, just deserialize it and put it to the result list
final Object element = listElementTypeAdapter.read(jsonReader);
list.add(element);
} else {
// Or skip the value entirely
jsonReader.skipValue();
}
}
// make sure that the current JSON token is `{` - NOT optional
jsonReader.endObject();
return list;
} finally {
responseBody.close();
}
};
}
return super.responseBodyConverter(type, annotations, retrofit);
}
private ByRegExp findByRegExp(final Annotation[] annotations) {
for ( final Annotation annotation : annotations ) {
if ( annotation instanceof ByRegExp ) {
return (ByRegExp) annotation;
}
}
return null;
}
// Trying to resolve how List<E> is parameterized (or raw if not)
private Type getTypeParameter0(final Type type) {
if ( !(type instanceof ParameterizedType) ) {
return Object.class;
}
final ParameterizedType parameterizedType = (ParameterizedType) type;
return parameterizedType.getActualTypeArguments()[0];
}
})
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
final IService service = retrofit.create(IService.class);
final List<Member> members = service.getMembers()
.execute()
.body();
for ( final Member member : members ) {
System.out.println(member.param1 + ", " + member.param2);
}
输出:
value1, value2
value1, value2
我认为它不会更容易实现(就 Gson/Retrofit 交互而言),但我希望它有所帮助。
关于java - 将 JSON 对象反序列化为对象数组以进行 Retrofit,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43925012/