1

One of the fields in my JSON response is a String[] when containing more than one element, and a String when it's just one. Like this:

"assets": [
  "0901d196804adc1c",
  "0901d196804ebd93",
  "0901d196804ea5e2"
]

"assets": "0901d196804adc1c"

Ideally, I would like to get a String[] always, so if the JSON type of the element is String, convert it to a String[] with one element.

How can I do that?

Eric Cochran
  • 8,414
  • 5
  • 50
  • 91
Nemesis
  • 1,138
  • 1
  • 14
  • 29

3 Answers3

0

If you cannot edit the response on the server side: Please refer to this question and answers, looks quite similar to your situation.

If you can edit the response, just reply always with String arrays (f.e. "assets": ["0901d196804adc1c"]).

dalla92
  • 432
  • 2
  • 8
0

You have two options:

1) Implement custom type adapter for gson to handle such situations (preferable solution).

2) Define field of type Object and cast it to the appropriate type at runtime

public static class AssetsContainer{
        private Object assets;

        public List<String> getAssets() {
            if(assets instanceof List<?>) {
                return (List<String>) assets;
            } else if(assets instanceof String){
                return Arrays.asList((String) assets);
            } else {
                //TODO: handle
                return null;
            }
        }
    }
Mihail
  • 241
  • 1
  • 6
0

How about using the TypeAdapter API?

Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(SingletonListTypeAdapter.FACTORY)
    .create();

The following will check for non-array JSON types when expecting array types and try to make them into singleton Java Lists. (Note that this uses Lists, not arrays. You can adapt it if you want, but Effective Java notes that application-layer code should prefer the Collections APIs over arrays.)

final class SingletonListTypeAdapter<T> extends TypeAdapter<List<T>> {
  static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
    @SuppressWarnings("unchecked")
    @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
      if (type.getRawType() != List.class) {
        return null;
      }
      TypeToken<?> collectionElementType = TypeToken.get(
          getCollectionElementType((ParameterizedType) type.getType()));
      TypeAdapter<List<Object>> delegate = (TypeAdapter<List<Object>>)
          gson.getDelegateAdapter(this, collectionElementType);
      return (TypeAdapter<T>) new SingletonListTypeAdapter<>(delegate);
    }
  };

  private final TypeAdapter<T> delegate;

  SingletonListTypeAdapter(TypeAdapter<T> delegate) {
    this.delegate = delegate;
  }

  @Override public void write(JsonWriter out, List<T> value) throws IOException {
    out.beginArray();
    for (int i = 0, size = value.size(); i < size; i++) {
      delegate.write(out, value.get(i));
    }
    out.endArray();
  }

  @Override public List<T> read(JsonReader in) throws IOException {
    if (in.peek() != BEGIN_ARRAY) {
      return Collections.singletonList(delegate.read(in));
    }
    in.beginArray();
    List<T> expanding = new ArrayList<>();
    while (in.hasNext()) {
      expanding.add(delegate.read(in));
    }
    in.endArray();
    return Collections.unmodifiableList(expanding);
  }

  static Type getCollectionElementType(ParameterizedType type) {
    Type[] types = type.getActualTypeArguments();
    Type paramType = types[0];
    if (paramType instanceof WildcardType) {
      return ((WildcardType) paramType).getUpperBounds()[0];
    }
    return paramType;
  }
}
Eric Cochran
  • 8,414
  • 5
  • 50
  • 91