com.github.gpluscb.ggjava.internal.json.Deserializer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gg-java Show documentation
Show all versions of gg-java Show documentation
Java wrapper for the smash.gg GraphQL API
The newest version!
package com.github.gpluscb.ggjava.internal.json;
import com.github.gpluscb.ggjava.api.exception.DeserializationException;
import com.github.gpluscb.ggjava.entity.EntityType;
import com.github.gpluscb.ggjava.entity.object.response.GGResponseObject;
import com.github.gpluscb.ggjava.entity.object.response.ListResponse;
import com.github.gpluscb.ggjava.entity.object.response.enums.EnumResponse;
import com.github.gpluscb.ggjava.entity.object.response.scalars.FloatResponse;
import com.github.gpluscb.ggjava.entity.object.response.scalars.IDResponse;
import com.github.gpluscb.ggjava.entity.object.response.scalars.IntResponse;
import com.github.gpluscb.ggjava.entity.object.response.scalars.TimestampResponse;
import com.github.gpluscb.ggjava.entity.object.response.unions.UnionResponse;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;
public class Deserializer {
@Nullable
public static T deserialize(@Nonnull JsonElement json, @Nonnull Class toClass) throws DeserializationException {
if (json.isJsonNull()) {
return null;
} else if (json.isJsonObject()) {
return deserialize(json.getAsJsonObject(), toClass);
} else if (json.isJsonArray()) {
throw new DeserializationException("For deserializing JsonArrays, use ListResponse deserialize(JsonArray, Class innerClass)");
} else if (json.isJsonPrimitive()) {
return deserialize(json.getAsJsonPrimitive(), toClass);
}
throw new DeserializationException("Illegal state reached: json is neither JsonNull, JsonObject, JsonArray nor JsonPrimitive");
}
@Nonnull
public static T deserialize(@Nonnull JsonObject json, @Nonnull Class toClass) throws DeserializationException {
if (UnionResponse.class.isAssignableFrom(toClass)) {
if (!json.has("__typename"))
throw new DeserializationException("__typename has to be provided for union type");
String __typename = json.getAsJsonPrimitive("__typename").getAsString();
try {
Method typeForTypename = toClass.getMethod("typeForTypename", String.class);
Object typeObject = typeForTypename.invoke(null, __typename);
if (!(typeObject instanceof Class))
throw new DeserializationException("typeForTypename did not return Class");
// No way to check if this is safe
Class extends GGResponseObject> type = (Class extends GGResponseObject>) typeObject;
try {
Constructor constructor = toClass.getConstructor(type);
GGResponseObject inner = deserialize(json, type);
return constructor.newInstance(inner);
} catch (NoSuchMethodException e) {
throw new DeserializationException("No fitting constructor found for " + toClass.toString(), e);
} catch (InstantiationException e) {
throw new DeserializationException("Constructor invocation failed", e);
}
} catch (NoSuchMethodException e) {
throw new DeserializationException("No typeForTypename(String) method found for " + toClass.toString(), e);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new DeserializationException("Invocation of typeForTypename failed", e);
}
}
// Getting constructors
Constructor constructor = null;
for (Constructor> c : toClass.getConstructors()) {
if (c.getParameterCount() != 0) {
// See documentation of getConstructors, this can only be Constructor
constructor = (Constructor) c;
break;
}
}
if (constructor == null)
throw new DeserializationException("No fitting constructor found for " + toClass.toString());
Object[] constructorArgs = new Object[constructor.getParameterCount()];
Parameter[] params = constructor.getParameters();
for (int i = 0; i < params.length; i++) {
Parameter param = params[i];
String name = param.getName();
Class> paramType = param.getType();
if (json.has(name)) {
JsonElement value = json.get(name);
if (!GGResponseObject.class.isAssignableFrom(paramType))
throw new DeserializationException("Type of parameter " + i + " of the constructor " + toClass.toString() + " ( type is " + paramType.toString() + ") does mot extend GGResponseObject");
if (value.isJsonArray()) {
Type paramParameterizedType = param.getParameterizedType();
if (!(paramParameterizedType instanceof ParameterizedType))
throw new DeserializationException("Cannot retrieve parameterized types for parameter " + param.toString());
Type[] typeArguments = ((ParameterizedType) paramParameterizedType).getActualTypeArguments();
if (typeArguments.length != 1)
throw new DeserializationException("Unexpected number of generic type parameters for param " + param.toString());
if (!(typeArguments[0] instanceof Class))
throw new DeserializationException("Generic type parameter does not implement class");
// No way to check if GGResponseObject is safe
Class extends GGResponseObject> classArgument = (Class extends GGResponseObject>) typeArguments[0];
constructorArgs[i] = deserialize(value.getAsJsonArray(), classArgument);
} else {
constructorArgs[i] = deserialize(value, (Class extends GGResponseObject>) paramType);
}
} else {
// Not provided, invoke no args constructor for this case
try {
if (paramType.equals(ListResponse.class)) {
// TODO: Code duplication
Type paramParameterizedType = param.getParameterizedType();
if (!(paramParameterizedType instanceof ParameterizedType))
throw new DeserializationException("Cannot retrieve parameterized types for parameter " + param.toString());
Type[] typeArguments = ((ParameterizedType) paramParameterizedType).getActualTypeArguments();
if (typeArguments.length != 1)
throw new DeserializationException("Unexpected number of generic type parameters for param " + param.toString());
if (!(typeArguments[0] instanceof Class))
throw new DeserializationException("Generic type parameter does not implement class");
// No way to check if GGResponseObject is safe
Class extends GGResponseObject> classArgument = (Class extends GGResponseObject>) typeArguments[0];
// Get EntityType constructor
Constructor> paramConstructor = paramType.getConstructor(EntityType.class);
EntityType paramEntityType = EntityType.getByResponseClass(classArgument);
constructorArgs[i] = paramConstructor.newInstance(paramEntityType);
} else {
// Get no args constructor
Constructor> paramConstructorNoArgs = paramType.getConstructor();
Object arg = paramConstructorNoArgs.newInstance();
constructorArgs[i] = arg;
}
} catch (NoSuchMethodException e) {
throw new DeserializationException("No fitting constructor found for " + paramType.toString(), e);
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
throw new DeserializationException("Exception caught while invoking no args constructor for " + paramType.toString(), e);
}
}
}
try {
return constructor.newInstance(constructorArgs);
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
throw new DeserializationException("Exception caught while invoking constructor for " + toClass.toString(), e);
}
}
@Nonnull
public static ListResponse deserialize(@Nonnull JsonArray json, @Nonnull Class toClass) throws DeserializationException {
EntityType type = EntityType.getByResponseClass(toClass);
if (type == null) throw new DeserializationException("No EntityType found for class " + toClass.toString());
List ret = new ArrayList<>();
for (JsonElement element : json)
ret.add(deserialize(element, toClass));
return new ListResponse<>(type, ret);
}
@Nonnull
public static T deserialize(@Nonnull JsonPrimitive json, @Nonnull Class toClass) throws DeserializationException {
// Getting constructors
Constructor constructor = null;
for (Constructor> c : toClass.getConstructors()) {
if (c.getParameterCount() == 1) {
// See documentation of getConstructors, this can only be Constructor
constructor = (Constructor) c;
break;
}
}
if (constructor == null)
throw new DeserializationException("No fitting constructor found for " + toClass.toString());
try {
if (EnumResponse.class.isAssignableFrom(toClass)) {
if (!json.isString())
throw new DeserializationException("toClass is an EnumResponse, but json is not a String");
// Enum
try {
// Get emum class
Class> enumClass = constructor.getParameters()[0].getType();
Method valueOf = enumClass.getMethod("valueOf", String.class);
// valueOf should be static
Object enumValue = valueOf.invoke(null, json.getAsString());
return constructor.newInstance(enumValue);
} catch (NoSuchMethodException e) {
throw new DeserializationException("No valueOf method found in class " + toClass.toString(), e);
}
} else {
// Scalar
if (json.isBoolean()) {
return constructor.newInstance(json.getAsBoolean());
} else if (json.isString()) {
return constructor.newInstance(json.getAsString());
} else if (json.isNumber()) {
if (toClass.equals(IntResponse.class)) {
return constructor.newInstance(json.getAsInt());
} else if (toClass.equals(TimestampResponse.class)) {
return constructor.newInstance(json.getAsLong());
} else if (toClass.equals(FloatResponse.class)) {
return constructor.newInstance(json.getAsFloat());
} else if (toClass.equals(IDResponse.class)) { // Documentation says "The ID type appears in a JSON response as a String", but testing says they appear as numbers
return constructor.newInstance(String.valueOf(json.getAsLong()));
} else {
throw new DeserializationException("Unknown scalar for JSON number type: " + toClass.toString());
}
} else {
throw new DeserializationException("JsonPrimitive is neither Boolean nor String nor Number, which is weird and probably illegal");
}
}
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new DeserializationException("Constructor or valueOf invocation failed", e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy