Moshi Determine if JSON is array or single object

流过昼夜 提交于 2019-12-19 21:48:45

问题


Is there a way to setup a Moshi adapter to automatically create a single Object or List<Object> based on the JSON response? Currently, I can do this explicitly. For example, I can receive the following responses:

{
    "userId": "1",
    "id": "2",
    "body": "body...",
    "title": "title..."
}

Or

[
  {
    "userId": "1",
    "id": "2",
    "body": "body...",
    "title": "title..."
  }
]

And I would like to create Object or List<Object> without having to explicitly specify which one to use.


回答1:


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



回答2:


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


来源:https://stackoverflow.com/questions/49165604/moshi-determine-if-json-is-array-or-single-object

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!