如何使用 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();
}
  1. 使用 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());
                    }
                }
            });
    
  2. 仅改造的解决方案:

    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());
        }
    });
    

希望对大家有所帮助

祝你好运,'。