package gov.nih.ncgc.bard.tools;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsonschema.JsonSchema;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* Utility methods to manipulate JSON objects.
*
* @author Rajarshi Guha
*/
public class JsonUtil {
public static JsonNode getJsonSchema(Class klass) throws JsonMappingException {
ObjectMapper mapper = new ObjectMapper();
JsonSchema schema = mapper.generateJsonSchema(klass);
JsonNode schemaRootNode = schema.getSchemaNode();
// lets examine the class to see if any fields were marked
// with @BARDJsonRequired and if so include a 'required' field
// in the schema
List<String> requiredFieldNames = new ArrayList<String>();
for (Field field : klass.getDeclaredFields()) {
if (field.isAnnotationPresent(BARDJsonRequired.class))
requiredFieldNames.add(field.getName());
// Hack to deal with Map<,> fields, where key and value are primitive types
if (field.getType().equals(Map.class)) {
JsonNode mapNode = getJsonSchemaForMap(field);
ObjectNode propertiesNode = (ObjectNode) schemaRootNode.get("properties");
propertiesNode.put(field.getName(), mapNode);
}
}
if (requiredFieldNames.size() > 0) {
((ObjectNode) schemaRootNode).put("required", mapper.valueToTree(requiredFieldNames));
}
return schemaRootNode;
}
static String getJsonTypeFromJavaType(Class klass) {
String jtype = "object";
if (klass == null) jtype = "null";
else if (klass.equals(String.class)) jtype = "string";
else if (klass.equals(Boolean.class)) jtype = "boolean";
else if (klass.equals(Integer.class)) jtype = "integer";
else if (klass.equals(BigInteger.class)) jtype = "integer";
else if (klass.equals(Float.class)) jtype = "number";
else if (klass.equals(Double.class)) jtype = "number";
else if (klass.equals(Long.class)) jtype = "number";
return jtype;
}
public static JsonNode getJsonSchemaForMap(Field field) {
ObjectMapper mapper = new ObjectMapper();
ParameterizedType mapType = (ParameterizedType) field.getGenericType();
Type keyType = mapType.getActualTypeArguments()[0];
Type valType = mapType.getActualTypeArguments()[1];
String jsonKeyType = getJsonTypeFromJavaType((Class) keyType);
String jsonValType = getJsonTypeFromJavaType((Class) valType);
ObjectNode node = mapper.createObjectNode();
node.put("type", "array");
ObjectNode itemsNode = mapper.createObjectNode();
itemsNode.put("type", "object");
ObjectNode propsNode = mapper.createObjectNode();
propsNode.put("key", jsonKeyType);
propsNode.put("value", jsonValType);
itemsNode.put("properties", propsNode);
node.put("items", itemsNode);
return node;
}
}