如何使用 AutoValue 解析结构为对象数组的 JSON?
How to use AutoValue to parse JSON structured as an array of objects?
我有这个 JSON 正文:
[
{
"id": 0,
"field1": "10",
"field2": "22"
},
{
"id": 1,
"field1": "11",
"field2": "23"
}
]
我的pojoItem
:
@AutoValue
public abstract class PojoItem{
@SerializedName("field1")
public abstract String field1();
@SerializedName("id")
public abstract int id();
@SerializedName("field2")
public abstract String field2();
}
还有我的pojoItemList
:
@AutoValue
public abstract class PojoItemList{
public abstract List< PojoItem > itemList();
public static TypeAdapter<PojoItemList> typeAdapter(Gson gson) {
return new AutoValue_PojoItemList.GsonTypeAdapter(gson);
}
}
我有 AutoValueGsonFactory
:
@GsonTypeAdapterFactory
public abstract class AutoValueGsonFactory implements TypeAdapterFactory {
// Static factory method to access the package
// private generated implementation
public static TypeAdapterFactory create() {
return new AutoValueGson_AutoValueGsonFactory();
}
}
我正在使用 Retrofit 和 RxJava。我得到这个例外:
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY
如何设置我的 POJO 以读取 JSON 作为对象数组而不是集合?
问题是您为您的列表提供了一个名称 itemList
,但在响应中不存在。
Retrofit 应该与这样的东西一起工作:
public class Item {
@SerializedName("field1")
public String field1;
@SerializedName("id")
public int id;
@SerializedName("field2")
public String field2;
}
然后,当您定义 Retrofit 的界面时,使用如下内容:
@GET("/path")
Single<List<Item>> getItems();
AutoValue Gson 不会为数组生成 Gson 类型的适配器(至少从我从它的源代码中看到的)。因此 Gson 需要一个 List 实例。请注意,您的列表数据模型与 Gson 默认值以及 AutoValue Gson 生成的内容冲突。你有两个解决方案。
解决方案 1:不要使用 PojoItemList
原因:arrays/lists 不需要 itemsList()
之类的东西。我不太确定除了 itemList()
之外,您是否会在 PojoItemList
中获得任何其他 auto-generated 值。 List<PojoItem>
真的够用了。因此,有效处理列表的原始 Gson 代码:
final Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(AutoValueGsonFactory.create())
.create();
final TypeToken<List<PojoItem>> pojoItemListTypeToken = new TypeToken<List<PojoItem>>() {
};
out.println(gson.<List<PojoItem>>fromJson(JSON, pojoItemListTypeToken.getType()));
据我了解,Retrofit 会将类型传递给 Gson 本身,因此,在这种情况下,您的 Retrofitted 服务不得使用 PojoItemList
,而应使用 List<PojoItem>
:
interface IService {
List<PojoItem> getPojoItems();
}
请注意,您必须为 PojoItem
添加一个可以由 AutoValue Gson 生成的类型适配器:
@AutoValue
public abstract class PojoItem {
...
public static TypeAdapter<PojoItem> typeAdapter(final Gson gson) {
return new AutoValue_PojoItem.GsonTypeAdapter(gson);
}
}
如果不生成和注册类型适配器,Gson 将无法创建 PojoItem
实例:
java.lang.RuntimeException: Failed to invoke public q42240399.PojoItem() with no args
解决方案 2:自己执行 AutoValue Gson 作业
如果出于某种原因你想使用 PojoItemList
,那么你必须编写你的自定义 TypeAdapter
因为,正如我上面提到的,AutoValue Gson 不会生成数组类型的适配器(我不能'不过,没有看到任何 beginArray
调用。
@AutoValue
public abstract class PojoItemList {
public abstract List<PojoItem> itemList();
public static TypeAdapter<PojoItemList> typeAdapter(final Gson gson) {
// Get the original PojoItem type adapter you can use below
final TypeAdapter<PojoItem> pojoItemTypeAdapter = gson.getAdapter(PojoItem.class);
return new TypeAdapter<PojoItemList>() {
@Override
public void write(final JsonWriter out, final PojoItemList pojoItemList) {
out.beginArray();
for ( final PojoItem pojoItem : pojoItemList.itemList() ) {
pojoItemTypeAdapter.write(out, pojoItem);
}
out.endArray();
}
@Override
public PojoItemList read(final JsonReader in)
throws IOException {
final List<PojoItem> pojoItems = new ArrayList<>();
// The first token must be [
in.beginArray();
// And read until ] is found
while ( in.peek() != END_ARRAY ) {
// Delegate parsing to the PojoItem type adapter for each array element
pojoItems.add(pojoItemTypeAdapter.read(in));
}
// The last token must be ]
in.endArray();
// Construct the PojoItemList value
return new AutoValue_PojoItemList(pojoItems);
}
};
}
}
您可能想请 AutoValue Gson 扩展作者实现 array-compliant 扩展。但是,出于几个原因,我认为解决方案 #1 更好。
两种解决方案都有效并将产生:
- 对于
List<PojoItem>
:
[PojoItem{field1=10, id=0, field2=22}, PojoItem{field1=11, id=1, field2=23}]
- 对于
PojoItemList
:
PojoItemList{itemList=[PojoItem{field1=10, id=0, field2=22}, PojoItem{field1=11, id=1, field2=23}]}
我可以让它在最后工作 with/without RxJava
这里有两种方式:
为了清楚起见,我将提供 API 和用于实现该目标的完整 Retrofit 代码:
AppApi:
public interface AppApi {
@GET("path/path")
Observable<List<PojoItem>> getItemsbyRxJava();
@GET("path/path")
Call<List<PojoItem>> getItemsbyRetrofit();
}
使用 RxJava 的解决方案:
GsonConverterFactory gsonConverterFactory = GsonConverterFactory.create(new GsonBuilder().registerTypeAdapterFactory(AutoValueGsonFactory.create()).create());
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Retrofit retrofitClient = new Retrofit.Builder()
.baseUrl("http://[domain]/path/")
.addConverterFactory(gsonConverterFactory)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(httpClient.build())
.build();
AppApi appApi = retrofitClient.create(AppApi.class);
appApi.getItemsbyRxJava()
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<PojoItem>>() {
@Override
public void onCompleted() {
Log.d(TAG, "completed");
}
@Override
public void onError(Throwable e) {
Log.e(TAG, e.getMessage());
}
@Override
public void onNext(List<PojoItem> list) {
for (PojoItem pojoItem : list) {
Log.d(TAG, pojoItem.field1() + "," + pojoItem.field2());
}
}
});
仅改造的解决方案:
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Retrofit retrofitClient = new Retrofit.Builder()
.baseUrl("http://[domain]/path/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(httpClient.build())
.build();
AppApi appApi = retrofitClient.create(AppApi.class);
Call<List<PojoItem>> call = appApi.getItemsbyRetrofit();
call.enqueue(new Callback<List<PojoItem>>() {
@Override
public void onResponse(Call<List<PojoItem>> call, Response<List<PojoItem>> response) {
for (PojoItem pojoItem : response.body()) {
Log.d(TAG, pojoItem.field1() + "," + pojoItem.field2());
}
}
@Override
public void onFailure(Call<List<PojoItem>> call, Throwable t) {
Log.e(TAG, t.getMessage());
}
});
希望对大家有所帮助
祝你好运,'。
我有这个 JSON 正文:
[
{
"id": 0,
"field1": "10",
"field2": "22"
},
{
"id": 1,
"field1": "11",
"field2": "23"
}
]
我的pojoItem
:
@AutoValue
public abstract class PojoItem{
@SerializedName("field1")
public abstract String field1();
@SerializedName("id")
public abstract int id();
@SerializedName("field2")
public abstract String field2();
}
还有我的pojoItemList
:
@AutoValue
public abstract class PojoItemList{
public abstract List< PojoItem > itemList();
public static TypeAdapter<PojoItemList> typeAdapter(Gson gson) {
return new AutoValue_PojoItemList.GsonTypeAdapter(gson);
}
}
我有 AutoValueGsonFactory
:
@GsonTypeAdapterFactory
public abstract class AutoValueGsonFactory implements TypeAdapterFactory {
// Static factory method to access the package
// private generated implementation
public static TypeAdapterFactory create() {
return new AutoValueGson_AutoValueGsonFactory();
}
}
我正在使用 Retrofit 和 RxJava。我得到这个例外:
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY
如何设置我的 POJO 以读取 JSON 作为对象数组而不是集合?
问题是您为您的列表提供了一个名称 itemList
,但在响应中不存在。
Retrofit 应该与这样的东西一起工作:
public class Item {
@SerializedName("field1")
public String field1;
@SerializedName("id")
public int id;
@SerializedName("field2")
public String field2;
}
然后,当您定义 Retrofit 的界面时,使用如下内容:
@GET("/path")
Single<List<Item>> getItems();
AutoValue Gson 不会为数组生成 Gson 类型的适配器(至少从我从它的源代码中看到的)。因此 Gson 需要一个 List 实例。请注意,您的列表数据模型与 Gson 默认值以及 AutoValue Gson 生成的内容冲突。你有两个解决方案。
解决方案 1:不要使用 PojoItemList
原因:arrays/lists 不需要 itemsList()
之类的东西。我不太确定除了 itemList()
之外,您是否会在 PojoItemList
中获得任何其他 auto-generated 值。 List<PojoItem>
真的够用了。因此,有效处理列表的原始 Gson 代码:
final Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(AutoValueGsonFactory.create())
.create();
final TypeToken<List<PojoItem>> pojoItemListTypeToken = new TypeToken<List<PojoItem>>() {
};
out.println(gson.<List<PojoItem>>fromJson(JSON, pojoItemListTypeToken.getType()));
据我了解,Retrofit 会将类型传递给 Gson 本身,因此,在这种情况下,您的 Retrofitted 服务不得使用 PojoItemList
,而应使用 List<PojoItem>
:
interface IService {
List<PojoItem> getPojoItems();
}
请注意,您必须为 PojoItem
添加一个可以由 AutoValue Gson 生成的类型适配器:
@AutoValue
public abstract class PojoItem {
...
public static TypeAdapter<PojoItem> typeAdapter(final Gson gson) {
return new AutoValue_PojoItem.GsonTypeAdapter(gson);
}
}
如果不生成和注册类型适配器,Gson 将无法创建 PojoItem
实例:
java.lang.RuntimeException: Failed to invoke public q42240399.PojoItem() with no args
解决方案 2:自己执行 AutoValue Gson 作业
如果出于某种原因你想使用 PojoItemList
,那么你必须编写你的自定义 TypeAdapter
因为,正如我上面提到的,AutoValue Gson 不会生成数组类型的适配器(我不能'不过,没有看到任何 beginArray
调用。
@AutoValue
public abstract class PojoItemList {
public abstract List<PojoItem> itemList();
public static TypeAdapter<PojoItemList> typeAdapter(final Gson gson) {
// Get the original PojoItem type adapter you can use below
final TypeAdapter<PojoItem> pojoItemTypeAdapter = gson.getAdapter(PojoItem.class);
return new TypeAdapter<PojoItemList>() {
@Override
public void write(final JsonWriter out, final PojoItemList pojoItemList) {
out.beginArray();
for ( final PojoItem pojoItem : pojoItemList.itemList() ) {
pojoItemTypeAdapter.write(out, pojoItem);
}
out.endArray();
}
@Override
public PojoItemList read(final JsonReader in)
throws IOException {
final List<PojoItem> pojoItems = new ArrayList<>();
// The first token must be [
in.beginArray();
// And read until ] is found
while ( in.peek() != END_ARRAY ) {
// Delegate parsing to the PojoItem type adapter for each array element
pojoItems.add(pojoItemTypeAdapter.read(in));
}
// The last token must be ]
in.endArray();
// Construct the PojoItemList value
return new AutoValue_PojoItemList(pojoItems);
}
};
}
}
您可能想请 AutoValue Gson 扩展作者实现 array-compliant 扩展。但是,出于几个原因,我认为解决方案 #1 更好。
两种解决方案都有效并将产生:
- 对于
List<PojoItem>
:
[PojoItem{field1=10, id=0, field2=22}, PojoItem{field1=11, id=1, field2=23}]
- 对于
PojoItemList
:
PojoItemList{itemList=[PojoItem{field1=10, id=0, field2=22}, PojoItem{field1=11, id=1, field2=23}]}
我可以让它在最后工作 with/without RxJava 这里有两种方式:
为了清楚起见,我将提供 API 和用于实现该目标的完整 Retrofit 代码:
AppApi:
public interface AppApi {
@GET("path/path")
Observable<List<PojoItem>> getItemsbyRxJava();
@GET("path/path")
Call<List<PojoItem>> getItemsbyRetrofit();
}
使用 RxJava 的解决方案:
GsonConverterFactory gsonConverterFactory = GsonConverterFactory.create(new GsonBuilder().registerTypeAdapterFactory(AutoValueGsonFactory.create()).create()); OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); Retrofit retrofitClient = new Retrofit.Builder() .baseUrl("http://[domain]/path/") .addConverterFactory(gsonConverterFactory) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .client(httpClient.build()) .build(); AppApi appApi = retrofitClient.create(AppApi.class); appApi.getItemsbyRxJava() .subscribeOn(Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<List<PojoItem>>() { @Override public void onCompleted() { Log.d(TAG, "completed"); } @Override public void onError(Throwable e) { Log.e(TAG, e.getMessage()); } @Override public void onNext(List<PojoItem> list) { for (PojoItem pojoItem : list) { Log.d(TAG, pojoItem.field1() + "," + pojoItem.field2()); } } });
仅改造的解决方案:
OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); Retrofit retrofitClient = new Retrofit.Builder() .baseUrl("http://[domain]/path/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .client(httpClient.build()) .build(); AppApi appApi = retrofitClient.create(AppApi.class); Call<List<PojoItem>> call = appApi.getItemsbyRetrofit(); call.enqueue(new Callback<List<PojoItem>>() { @Override public void onResponse(Call<List<PojoItem>> call, Response<List<PojoItem>> response) { for (PojoItem pojoItem : response.body()) { Log.d(TAG, pojoItem.field1() + "," + pojoItem.field2()); } } @Override public void onFailure(Call<List<PojoItem>> call, Throwable t) { Log.e(TAG, t.getMessage()); } });
希望对大家有所帮助
祝你好运,'。