AutoValue Gson does not generate Gson type adapters for array (at least from what I've seen from its source code). Thus Gson expects a List instance. Note that your list data model conflicts with Gson defaults, and with what AutoValue Gson generates. Your have two solutions.
Solution 1: Do not use PojoItemList
Why: arrays/lists do not need anything like itemsList()
. I'm not really sure you'll ever get any other auto-generated values in PojoItemList
except itemList()
. List<PojoItem>
is really enough to make it work. So, a raw Gson code that works with lists efficiently:
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()));
As far as I understand, Retrofit will pass the type to Gson itself, and, accordingly, your Retrofitted service must not use PojoItemList
in this case, and use List<PojoItem>
:
interface IService {
List<PojoItem> getPojoItems();
}
Note that you must add a type adapter for PojoItem
that can be generated by AutoValue Gson:
@AutoValue
public abstract class PojoItem {
...
public static TypeAdapter<PojoItem> typeAdapter(final Gson gson) {
return new AutoValue_PojoItem.GsonTypeAdapter(gson);
}
}
If the type adapter is not generated and registered, Gson won't be able to create a PojoItem
instance:
java.lang.RuntimeException: Failed to invoke public q42240399.PojoItem() with no args
Solution 2: Do AutoValue Gson job yourself
If for some reason you want to use PojoItemList
, then you have to write your custom TypeAdapter
because, as I mentioned above, AutoValue Gson does not generate array type adapters (I couldn't see any beginArray
invocations, though).
@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);
}
};
}
}
You might want to ask the AutoValue Gson extension authors for implementing an array-compliant extension. However, I think that solution #1 is much better for several reasons.
Both solutions work and will produce:
[PojoItem{field1=10, id=0, field2=22}, PojoItem{field1=11, id=1, field2=23}]
PojoItemList{itemList=[PojoItem{field1=10, id=0, field2=22}, PojoItem{field1=11, id=1, field2=23}]}