AbstractResponseDeserializer.java
package org.vlaskin.moexiss.response;
import com.google.gson.JsonArray;
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 org.vlaskin.moexiss.entity.base.BasicEntity;
import org.vlaskin.moexiss.entity.base.EntityType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class AbstractResponseDeserializer<T extends Response> implements JsonDeserializer<T>
{
@Override
public T deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException
{
if (!jsonElement.isJsonObject())
throw new JsonParseException("MOEX ISS response root must be an object");
Map<EntityType<? extends BasicEntity<?>, ? extends Enum<?>>, List<? extends BasicEntity<?>>> dataMap = new HashMap<>();
for (Map.Entry<String, JsonElement> entry : jsonElement.getAsJsonObject().asMap().entrySet())
{
EntityType<? extends BasicEntity<?>, ? extends Enum<?>> entityType = EntityType.getEntityType(getResponseClass(), entry.getKey());
if (entityType == null)
continue;
String sectionName = entry.getKey();
JsonObject section = requireObject(entry.getValue(), "Section '" + sectionName + "'");
JsonObject metadata = requireObject(section.get("metadata"),
"Field 'metadata' of section '" + sectionName + "'");
JsonArray columns = requireArray(section.get("columns"),
"Field 'columns' of section '" + sectionName + "'");
JsonArray data = requireArray(section.get("data"),
"Field 'data' of section '" + sectionName + "'");
validateColumns(sectionName, columns, metadata);
List<BasicEntity<?>> dataList = new ArrayList<>();
for (int rowIndex = 0; rowIndex < data.size(); rowIndex++)
{
JsonElement element = data.get(rowIndex);
JsonArray row = requireArray(element,
"Row " + (rowIndex + 1) + " of section '" + sectionName + "'");
if (row.size() != columns.size())
throw new JsonParseException("Row " + (rowIndex + 1) + " of section '" + sectionName
+ "' contains " + row.size() + " values for " + columns.size() + " columns");
try
{
dataList.add(entityType.createInstance(row, columns, metadata));
}
catch (RuntimeException e)
{
throw new JsonParseException("Failed to parse row " + (rowIndex + 1)
+ " of section '" + sectionName + "': " + e.getMessage(), e);
}
}
dataMap.put(entityType, dataList);
}
return createResponse(dataMap);
}
private static JsonObject requireObject(JsonElement element, String description)
{
if (element == null || !element.isJsonObject())
throw new JsonParseException(description + " must be an object");
return element.getAsJsonObject();
}
private static JsonArray requireArray(JsonElement element, String description)
{
if (element == null || !element.isJsonArray())
throw new JsonParseException(description + " must be an array");
return element.getAsJsonArray();
}
private static void validateColumns(String sectionName, JsonArray columns, JsonObject metadata)
{
for (JsonElement column : columns)
{
if (!column.isJsonPrimitive() || !column.getAsJsonPrimitive().isString())
throw new JsonParseException("Column name of section '" + sectionName + "' must be a string");
String columnName = column.getAsString();
JsonElement fieldMetadata = metadata.get(columnName);
if (fieldMetadata == null || !fieldMetadata.isJsonObject())
throw new JsonParseException("Metadata for column '" + columnName
+ "' is missing in section '" + sectionName + "'");
JsonElement fieldType = fieldMetadata.getAsJsonObject().get("type");
if (fieldType == null || !fieldType.isJsonPrimitive() || !fieldType.getAsJsonPrimitive().isString())
throw new JsonParseException("String type is missing in metadata for column '" + columnName
+ "' of section '" + sectionName + "'");
}
}
protected abstract Class<T> getResponseClass();
protected abstract T createResponse(
Map<EntityType<? extends BasicEntity<?>, ? extends Enum<?>>, List<? extends BasicEntity<?>>> data);
}