如何使用AutoValue来解析构造为对象数组的JSON?

时间:2017-02-15 03:51:22

标签: java android gson auto-value

我有这个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();
    }
}

我在RxJava上使用Retrofit。我有这个例外:

  

java.lang.IllegalStateException:预期BEGIN_OBJECT但是BEGIN_ARRAY

如何设置我的POJO以将JSON读作对象数组而不是集合?

3 个答案:

答案 0 :(得分:1)

问题是您为列表指定了名称itemList,但在回复中不存在。

改造应该适用于这样的事情:

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

答案 1 :(得分:1)

AutoValue Gson不为数组生成Gson类型的适配器(至少从我从其源代码中看到的)。因此Gson期望一个List实例。请注意,列表数据模型与Gson默认值以及AutoValue Gson生成的内容冲突。你有两个解决方案。

解决方案1:不要使用PojoItemList

原因:数组/列表不需要itemsList()之类的内容。我不确定您是否会在PojoItemList中获得除itemList()之外的任何其他自动生成的值。 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:无法调用没有args的公共q42240399.PojoItem()

解决方案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扩展作者实现符合阵列的扩展。但是,我认为解决方案#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}]}

答案 2 :(得分:0)

我可以在有/没有RxJava的情况下使其工作 这有两种方式:

为了说清楚,我将提供用于实现该目标的API和完整的改造代码:

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());
        }
    });
    
  3. 我希望这会对某人有所帮助

    祝你好运,'。