ProjectAres/API/api/src/main/java/tc/oc/api/document/DocumentSerializer.java

105 lines
4.5 KiB
Java

package tc.oc.api.document;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.inject.Singleton;
import com.google.common.collect.ImmutableMap;
import com.google.common.reflect.TypeToken;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonSyntaxException;
import com.google.inject.CreationException;
import com.google.inject.ProvisionException;
import tc.oc.api.docs.virtual.Document;
import tc.oc.api.document.DocumentMeta;
import tc.oc.api.document.DocumentRegistry;
import tc.oc.api.document.Getter;
import tc.oc.api.exceptions.SerializationException;
import tc.oc.commons.core.logging.Loggers;
@Singleton
public class DocumentSerializer implements JsonSerializer<Document>, JsonDeserializer<Document> {
protected final Logger logger;
protected final DocumentRegistry documentRegistry;
@Inject DocumentSerializer(Loggers loggers, DocumentRegistry documentRegistry) {
this.logger = loggers.get(getClass());
this.documentRegistry = documentRegistry;
}
@Override
public JsonElement serialize(Document document, Type documentType, JsonSerializationContext context) {
final JsonObject documentJson = new JsonObject();
final DocumentMeta<?> documentMeta = documentRegistry.getMeta(document.getClass());
final TypeToken documentTypeToken = TypeToken.of(documentType);
for(Map.Entry<String, Getter> entry : documentMeta.getters().entrySet()) {
final String name = entry.getKey();
final Getter getter = entry.getValue();
final Type resolvedType = getter.resolvedType(documentTypeToken);
final Object value;
try {
value = getter.get(document);
} catch(Exception e) {
throw new SerializationException("Exception reading property " + resolvedType + " " + documentType + "." + name, e);
}
try {
documentJson.add(name, context.serialize(value, resolvedType));
} catch(Exception e) {
throw new SerializationException("Exception serializing property " + resolvedType + " " + documentType + "." + name + " = " + value, e);
}
}
return documentJson;
}
@Override
public Document deserialize(JsonElement rawJson, Type documentType, JsonDeserializationContext context) throws JsonParseException {
final TypeToken documentTypeToken = TypeToken.of(documentType);
final DocumentMeta<?> documentMeta = documentRegistry.getMeta(documentTypeToken.getRawType());
final Class<? extends Document> instantiableType = documentMeta.type();
if(!(rawJson instanceof JsonObject)) {
throw new JsonSyntaxException("Expected JSON object while deserializing " + instantiableType.getName() + ", not " + rawJson.getClass().getSimpleName());
}
final JsonObject documentJson = (JsonObject) rawJson;
final ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
for(Map.Entry<String, Getter> entry : documentMeta.getters().entrySet()) {
final String name = entry.getKey();
final Getter getter = entry.getValue();
final Type resolvedType = getter.resolvedType(documentTypeToken);
final JsonElement propertyJson = documentJson.get(name);
try {
if(propertyJson == null || propertyJson.isJsonNull()) {
if(!getter.isNullable()) {
throw new NullPointerException("Missing value for non-nullable property " + name);
}
} else {
builder.put(name, context.deserialize(propertyJson, resolvedType));
}
} catch(Exception e) {
throw new SerializationException("Exception deserializing property " + resolvedType + " " + documentType + "." + name + " = " + propertyJson, e);
}
}
try {
return documentRegistry.instantiate(documentMeta, builder.build());
} catch(ProvisionException | CreationException e) {
throw new SerializationException("Exception instantiating document " + instantiableType.getName(), e);
}
}
}