Skip to content

Instantly share code, notes, and snippets.

@vegaasen
Created October 22, 2018 09:56
Show Gist options
  • Save vegaasen/6a65d2be536ca2e24e1ed67df72ae816 to your computer and use it in GitHub Desktop.
Save vegaasen/6a65d2be536ca2e24e1ed67df72ae816 to your computer and use it in GitHub Desktop.
Simple "SafeListTypeAdapter" > Always return array for non-array objects that can be array together with non-arrays..
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.google.gson.stream.MalformedJsonException;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
public class SafeListTypeAdapter<E> implements TypeAdapterFactory {
private SafeListTypeAdapter() {
}
@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
if (!List.class.isAssignableFrom(typeToken.getRawType())) {
return null;
}
return (TypeAdapter<T>) new AlwaysListTypeAdapter<>((TypeAdapter<E>) gson.getAdapter(TypeToken.get(resolveTypeArgument(typeToken.getType())))).nullSafe();
}
private static Type resolveTypeArgument(Type type) {
if (!(type instanceof ParameterizedType)) {
return Object.class;
}
return ((ParameterizedType) type).getActualTypeArguments()[0];
}
private static final class AlwaysListTypeAdapter<E> extends TypeAdapter<List<E>> {
private final TypeAdapter<E> elementTypeAdapter;
private AlwaysListTypeAdapter(TypeAdapter<E> elementTypeAdapter) {
this.elementTypeAdapter = elementTypeAdapter;
}
@Override
public void write(JsonWriter out, List<E> list) {
throw new UnsupportedOperationException();
}
@Override
public List<E> read(JsonReader in) throws IOException {
List<E> candidates = new ArrayList<>();
JsonToken token = in.peek();
switch (token) {
case BEGIN_ARRAY:
in.beginArray();
while (in.hasNext()) {
candidates.add(elementTypeAdapter.read(in));
}
in.endArray();
break;
case BEGIN_OBJECT:
case STRING:
case NUMBER:
case BOOLEAN:
candidates.add(elementTypeAdapter.read(in));
break;
case NULL:
throw new AssertionError("Must never happen: check if the type adapter configured with .nullSafe()");
case NAME:
case END_ARRAY:
case END_OBJECT:
case END_DOCUMENT:
throw new MalformedJsonException("Unexpected token: " + token);
default:
throw new AssertionError("Must never happen: " + token);
}
return candidates;
}
}
}
@vegaasen
Copy link
Author

Usage:

    @JsonAdapter(SafeListTypeAdapter.class)
    @SerializedName("application-message")
    private List<Message> messages;

@vegaasen
Copy link
Author

Helps on translating such JSON (where it can variates between an object and an array):

Array-based:

[
  {
    "title": "Test2",
    "type": "error",
    "application": "open_tracking",
    "application-message": [
      {
        "message-title": "yep yep - en",
        "message-body": "yep yep yep - en",
        "language": "en"
      },
      {
        "message-title": "yep yep - no",
        "message-body": "yep yep yep - no",
        "language": "nb"
      }
    ]
  }
]

Object-based:

[
  {
    "title": "test3",
    "type": "error",
    "application": "controltower",
    "application-message": {
      "message-title": "11",
      "message-body": "12",
      "language": "nb"
    }
  }
]

..both ends up in an array :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment