Moshi Determine if JSON is array or single object
You can use a JsonQualifier to generalize this.From your example, you might use it like
final class Foo { @SingleToArray final List<User> users;}
Here's the code with a test to demonstrate more thouroughly.
@Retention(RUNTIME)@JsonQualifier public @interface SingleToArray { final class Adapter extends JsonAdapter<List<Object>> { final JsonAdapter<List<Object>> delegateAdapter; final JsonAdapter<Object> elementAdapter; public static final Factory FACTORY = new Factory() { @Nullable @Override public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi) { Set<? extends Annotation> delegateAnnotations = Types.nextAnnotations(annotations, SingleToArray.class); if (delegateAnnotations == null) { return null; } if (Types.getRawType(type) != List.class) { throw new IllegalArgumentException( "Only lists may be annotated with @SingleToArray. Found: " + type); } Type elementType = Types.collectionElementType(type, List.class); JsonAdapter<List<Object>> delegateAdapter = moshi.adapter(type, delegateAnnotations); JsonAdapter<Object> elementAdapter = moshi.adapter(elementType); return new Adapter(delegateAdapter, elementAdapter); } }; Adapter(JsonAdapter<List<Object>> delegateAdapter, JsonAdapter<Object> elementAdapter) { this.delegateAdapter = delegateAdapter; this.elementAdapter = elementAdapter; } @Nullable @Override public List<Object> fromJson(JsonReader reader) throws IOException { if (reader.peek() != JsonReader.Token.BEGIN_ARRAY) { return Collections.singletonList(elementAdapter.fromJson(reader)); } return delegateAdapter.fromJson(reader); } @Override public void toJson(JsonWriter writer, @Nullable List<Object> value) throws IOException { if (value.size() == 1) { elementAdapter.toJson(writer, value.get(0)); } else { delegateAdapter.toJson(writer, value); } } }}@Test public void singleToArray() throws Exception { Moshi moshi = new Moshi.Builder().add(SingleToArray.Adapter.FACTORY).build(); JsonAdapter<List<String>> adapter = moshi.adapter(Types.newParameterizedType(List.class, String.class), SingleToArray.class); assertThat(adapter.fromJson("[\"Tom\",\"Huck\"]")).isEqualTo(Arrays.asList("Tom", "Huck")); assertThat(adapter.toJson(Arrays.asList("Tom", "Huck"))).isEqualTo("[\"Tom\",\"Huck\"]"); assertThat(adapter.fromJson("\"Jim\"")).isEqualTo(Collections.singletonList("Jim")); assertThat(adapter.toJson(Collections.singletonList("Jim"))).isEqualTo("\"Jim\""); assertThat(adapter.fromJson("[]")).isEqualTo(Collections.emptyList()); assertThat(adapter.toJson(Collections.<String>emptyList())).isEqualTo("[]");}
Using @Eric's comment, I came up with the correct code below:
public static <T> List<T> loadFakeData(String url, Class<T> cls){ List<T> list = new ArrayList<>(); Moshi moshi = new Moshi.Builder().build(); try { JsonReader reader = JsonReader.of(runHttpClient(url)); JsonReader.Token token = reader.peek(); if (token.equals(JsonReader.Token.BEGIN_ARRAY)) { Type type = Types.newParameterizedType(List.class, cls); JsonAdapter<List<T>> adapter = moshi.adapter(type); list = adapter.fromJson(reader); } else if (token.equals(JsonReader.Token.BEGIN_OBJECT)){ JsonAdapter<T> adapter = moshi.adapter(cls); T t = adapter.fromJson(reader); list.add(t); } } catch (IOException e) { e.printStackTrace(); } return list;}