Index: pending/jackson-json-schema.diff =================================================================== --- pending/jackson-json-schema.diff (revision 372) +++ pending/jackson-json-schema.diff (working copy) @@ -1,1374 +0,0 @@ -Index: src/test/org/codehaus/jackson/schema/TestGenerateJsonSchema.java -=================================================================== ---- src/test/org/codehaus/jackson/schema/TestGenerateJsonSchema.java (revision 0) -+++ src/test/org/codehaus/jackson/schema/TestGenerateJsonSchema.java (revision 0) -@@ -0,0 +1,75 @@ -+package org.codehaus.jackson.schema; -+ -+import junit.framework.TestCase; -+ -+import java.util.Collection; -+ -+import org.codehaus.jackson.map.ObjectMapper; -+ -+/** -+ * @author Ryan Heaton -+ */ -+public class TestGenerateJsonSchema -+ extends TestCase -+{ -+ -+ /** -+ * tests generating json-schema stuff. -+ */ -+ public void testGeneratingJsonSchema() -+ throws Exception -+ { -+ ObjectMapper mapper = new ObjectMapper(); -+ JsonSchema jsonSchema = mapper.generateJsonSchema(SimpleBean.class); -+ assertNotNull(jsonSchema); -+ } -+ -+ public static class SimpleBean -+ { -+ private int property1; -+ private String property2; -+ private String[] property3; -+ private Collection property4; -+ -+ public int getProperty1() -+ { -+ return property1; -+ } -+ -+ public void setProperty1(int property1) -+ { -+ this.property1 = property1; -+ } -+ -+ public String getProperty2() -+ { -+ return property2; -+ } -+ -+ public void setProperty2(String property2) -+ { -+ this.property2 = property2; -+ } -+ -+ public String[] getProperty3() -+ { -+ return property3; -+ } -+ -+ public void setProperty3(String[] property3) -+ { -+ this.property3 = property3; -+ } -+ -+ public Collection getProperty4() -+ { -+ return property4; -+ } -+ -+ public void setProperty4(Collection property4) -+ { -+ this.property4 = property4; -+ } -+ } -+ -+} - -Property changes on: src/test/org/codehaus/jackson/schema/TestGenerateJsonSchema.java -___________________________________________________________________ -Added: svn:mime-type - + text/plain -Added: svn:keywords - + Date Revision -Added: svn:eol-style - + native - -Index: src/java/org/codehaus/jackson/node/ObjectNode.java -=================================================================== ---- src/java/org/codehaus/jackson/node/ObjectNode.java (revision 353) -+++ src/java/org/codehaus/jackson/node/ObjectNode.java (working copy) -@@ -10,7 +10,7 @@ - /** - * Note that maps to Json Object structures in Json content. - */ --public final class ObjectNode -+public class ObjectNode - extends ContainerNode - { - LinkedHashMap _children = null; -Index: src/java/org/codehaus/jackson/schema/JsonSchema.java -=================================================================== ---- src/java/org/codehaus/jackson/schema/JsonSchema.java (revision 0) -+++ src/java/org/codehaus/jackson/schema/JsonSchema.java (revision 0) -@@ -0,0 +1,47 @@ -+package org.codehaus.jackson.schema; -+ -+import org.codehaus.jackson.JsonNode; -+import org.codehaus.jackson.JsonGenerator; -+import org.codehaus.jackson.JsonGenerationException; -+import org.codehaus.jackson.node.ObjectNode; -+ -+import java.io.IOException; -+ -+/** -+ * A {@link org.codehaus.jackson.JsonNode} that represents a JSON-Schema instance. -+ * -+ * @author Ryan Heaton -+ * @see http://json-schema.org/ -+ */ -+public class JsonSchema extends JsonNode { -+ -+ private final ObjectNode schema; -+ -+ public JsonSchema(ObjectNode schema) { -+ this.schema = schema; -+ } -+ -+ public String getValueAsText() { -+ return this.schema.getValueAsText(); -+ } -+ -+ public JsonNode path(String fieldName) { -+ return this.schema.path(fieldName); -+ } -+ -+ public JsonNode path(int index) { -+ return this.schema.path(index); -+ } -+ -+ public void writeTo(JsonGenerator jg) throws IOException, JsonGenerationException { -+ this.schema.writeTo(jg); -+ } -+ -+ public String toString() { -+ return this.schema.toString(); -+ } -+ -+ public boolean equals(Object o) { -+ return this.schema.equals(o); -+ } -+} - -Property changes on: src/java/org/codehaus/jackson/schema/JsonSchema.java -___________________________________________________________________ -Added: svn:mime-type - + text/plain -Added: svn:keywords - + Date Revision -Added: svn:eol-style - + native - -Index: src/java/org/codehaus/jackson/map/JsonSerializer.java -=================================================================== ---- src/java/org/codehaus/jackson/map/JsonSerializer.java (revision 353) -+++ src/java/org/codehaus/jackson/map/JsonSerializer.java (working copy) -@@ -1,8 +1,11 @@ - package org.codehaus.jackson.map; - - import java.io.IOException; -+import java.lang.reflect.Type; - - import org.codehaus.jackson.*; -+import org.codehaus.jackson.node.ObjectNode; -+import org.codehaus.jackson.node.JsonNodeFactory; - - /** - * Abstract class that defines API used by {@link ObjectMapper} (and -@@ -22,4 +25,19 @@ - */ - public abstract void serialize(T value, JsonGenerator jgen, SerializerProvider provider) - throws IOException, JsonProcessingException; -+ -+ /** -+ * Get the representation of the schema to which this serializer will conform. -+ * -+ * @param provider The serializer provider. -+ * @param typeHint A hint about the type. -+ * @return The {@link http://json-schema.org/ json-schema} for this serializer. -+ */ -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "any"); -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } -Index: src/java/org/codehaus/jackson/map/ser/BeanSerializer.java -=================================================================== ---- src/java/org/codehaus/jackson/map/ser/BeanSerializer.java (revision 353) -+++ src/java/org/codehaus/jackson/map/ser/BeanSerializer.java (working copy) -@@ -3,10 +3,14 @@ - import java.io.IOException; - import java.lang.reflect.InvocationTargetException; - import java.lang.reflect.Modifier; -+import java.lang.reflect.Type; - import java.util.Collection; - - import org.codehaus.jackson.JsonGenerationException; - import org.codehaus.jackson.JsonGenerator; -+import org.codehaus.jackson.JsonNode; -+import org.codehaus.jackson.node.ObjectNode; -+import org.codehaus.jackson.node.JsonNodeFactory; - import org.codehaus.jackson.map.*; - - /** -@@ -72,11 +76,34 @@ - } - } - -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ //todo: should the classname go in the title? -+ //o.put("title", _className); -+ o.put("type", "object"); -+ o.put("optional", true); -+ ObjectNode propertiesNode = o.objectNode(); -+ for (int i = 0; i < _props.length; i++) { -+ BeanPropertyWriter prop = _props[i]; -+ Type hint = prop.getSerializationType(); -+ if (hint == null) { -+ hint = prop._accessorMethod.getGenericReturnType(); -+ } -+ JsonSerializer jsonSerializer = provider.findValueSerializer(prop.getSerializationType() == null ? prop.getReturnType() : prop.getSerializationType()); -+ propertiesNode.put(prop.getName(), jsonSerializer.getSchema(provider, hint)); -+ } -+ o.put("properties", propertiesNode); -+ return o; -+ } -+ - /* -- //////////////////////////////////////////////////////// -- // ResolvableSerializer impl -- //////////////////////////////////////////////////////// -- */ -+ //////////////////////////////////////////////////////// -+ // ResolvableSerializer impl -+ //////////////////////////////////////////////////////// -+ */ - - public void resolve(SerializerProvider provider) - throws JsonMappingException -Index: src/java/org/codehaus/jackson/map/ser/ContainerSerializers.java -=================================================================== ---- src/java/org/codehaus/jackson/map/ser/ContainerSerializers.java (revision 353) -+++ src/java/org/codehaus/jackson/map/ser/ContainerSerializers.java (working copy) -@@ -2,9 +2,17 @@ - - import java.io.IOException; - import java.util.*; -+import java.lang.reflect.Type; -+import java.lang.reflect.ParameterizedType; - - import org.codehaus.jackson.*; -+import org.codehaus.jackson.type.JavaType; -+import org.codehaus.jackson.node.ObjectNode; -+import org.codehaus.jackson.node.JsonNodeFactory; - import org.codehaus.jackson.map.*; -+import org.codehaus.jackson.map.type.TypeFactory; -+import org.codehaus.jackson.map.type.ArrayType; -+import org.codehaus.jackson.map.type.CollectionType; - - /** - * Dummy container class to group standard container serializers: serializers -@@ -76,6 +84,24 @@ - - jgen.writeEndArray(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "array"); -+ if (typeHint != null) { -+ JavaType javaType = TypeFactory.instance._fromType(typeHint); -+ if (javaType instanceof CollectionType) { -+ Class componentType = ((CollectionType) javaType).getElementType().getRawClass(); -+ JsonSerializer ser = provider.findValueSerializer(componentType); -+ o.put("items", ser.getSchema(provider, null)); -+ } -+ } -+ o.put("optional", true); -+ return o; -+ } - } - - /** -@@ -132,6 +158,24 @@ - } - jgen.writeEndArray(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "array"); -+ if (typeHint != null) { -+ JavaType javaType = TypeFactory.instance._fromType(typeHint); -+ if (javaType instanceof CollectionType) { -+ Class componentType = ((CollectionType) javaType).getElementType().getRawClass(); -+ JsonSerializer ser = provider.findValueSerializer(componentType); -+ o.put("items", ser.getSchema(provider, null)); -+ } -+ } -+ o.put("optional", true); -+ return o; -+ } - } - - public final static class IteratorSerializer -@@ -168,6 +212,24 @@ - } - jgen.writeEndArray(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "array"); -+ if (typeHint instanceof ParameterizedType) { -+ Type[] typeArgs = ((ParameterizedType) typeHint).getActualTypeArguments(); -+ if (typeArgs.length == 1) { -+ JavaType javaType = TypeFactory.instance._fromType(typeArgs[0]); -+ JsonSerializer ser = provider.findValueSerializer(javaType.getRawClass()); -+ o.put("items", ser.getSchema(provider, null)); -+ } -+ } -+ o.put("optional", true); -+ return o; -+ } - } - - public final static class IterableSerializer -@@ -205,6 +267,24 @@ - } - jgen.writeEndArray(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "array"); -+ if (typeHint instanceof ParameterizedType) { -+ Type[] typeArgs = ((ParameterizedType) typeHint).getActualTypeArguments(); -+ if (typeArgs.length == 1) { -+ JavaType javaType = TypeFactory.instance._fromType(typeArgs[0]); -+ JsonSerializer ser = provider.findValueSerializer(javaType.getRawClass()); -+ o.put("items", ser.getSchema(provider, null)); -+ } -+ } -+ o.put("optional", true); -+ return o; -+ } - } - - public final static class EnumSetSerializer -@@ -222,6 +302,24 @@ - } - jgen.writeEndArray(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "array"); -+ if (typeHint instanceof ParameterizedType) { -+ Type[] typeArgs = ((ParameterizedType) typeHint).getActualTypeArguments(); -+ if (typeArgs.length == 1) { -+ JavaType javaType = TypeFactory.instance._fromType(typeArgs[0]); -+ JsonSerializer ser = provider.findValueSerializer(javaType.getRawClass()); -+ o.put("items", ser.getSchema(provider, null)); -+ } -+ } -+ o.put("optional", true); -+ return o; -+ } - } - - /* -@@ -286,6 +384,17 @@ - - jgen.writeEndObject(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "object"); -+ //(ryan) even though it's possible to statically determine the "value" type of the map, -+ // there's no way to statically determine the keys, so the "properties" can't be determined. -+ o.put("optional", true); -+ return o; -+ } - } - - public final static class EnumMapSerializer -@@ -328,5 +437,29 @@ - } - jgen.writeEndObject(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "object"); -+ if (typeHint instanceof ParameterizedType) { -+ Type[] typeArgs = ((ParameterizedType) typeHint).getActualTypeArguments(); -+ if (typeArgs.length == 2) { -+ JavaType enumType = TypeFactory.instance._fromType(typeArgs[0]); -+ JavaType valueType = TypeFactory.instance._fromType(typeArgs[1]); -+ Class enumClass = (Class) enumType.getRawClass(); -+ ObjectNode propsNode = JsonNodeFactory.instance.objectNode(); -+ for (Object enumValue : EnumSet.allOf(enumClass)) { -+ JsonSerializer ser = provider.findValueSerializer(valueType.getRawClass()); -+ propsNode.put(((Enum)enumValue).name(), ser.getSchema(provider, null)); -+ } -+ o.put("properties", propsNode); -+ } -+ } -+ o.put("optional", true); -+ return o; -+ } - } - } -Index: src/java/org/codehaus/jackson/map/ser/StdSerializerProvider.java -=================================================================== ---- src/java/org/codehaus/jackson/map/ser/StdSerializerProvider.java (revision 353) -+++ src/java/org/codehaus/jackson/map/ser/StdSerializerProvider.java (working copy) -@@ -3,8 +3,11 @@ - import java.io.IOException; - import java.text.DateFormat; - import java.util.Date; -+import java.lang.reflect.Type; - - import org.codehaus.jackson.*; -+import org.codehaus.jackson.node.ObjectNode; -+import org.codehaus.jackson.schema.JsonSchema; - - import org.codehaus.jackson.map.*; - -@@ -52,6 +55,13 @@ - */ - throw new JsonMappingException("No serializer found for class "+value.getClass().getName()+" (and no bean properties discovered to create bean serializer)"); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ throw new JsonMappingException("No serializer found for class "+typeHint+" (and no bean properties discovered to create bean serializer)"); -+ } - }; - - /* -@@ -206,6 +216,33 @@ - inst._serializeValue(jgen, value); - } - -+ @Override -+ public JsonSchema generateJsonSchema(Class type, SerializationConfig config, SerializerFactory jsf) -+ throws JsonMappingException -+ { -+ if (type == null) { -+ throw new IllegalArgumentException("A class must be provided."); -+ } -+ -+ /* First: we need a separate instance, which will hold a copy of the -+ * non-shared ("local") read-only lookup Map for fast -+ * class-to-serializer lookup -+ */ -+ StdSerializerProvider inst = createInstance(config, jsf); -+ // sanity check to avoid weird errors; to ensure sub-classes do override createInstance -+ if (inst.getClass() != getClass()) { -+ throw new IllegalStateException("Broken serializer provider: createInstance returned instance of type "+inst.getClass()+"; blueprint of type "+getClass()); -+ } -+ JsonSerializer ser = inst.findValueSerializer(type); -+ JsonNode schemaNode = ser.getSchema(inst, null); -+ if (!(schemaNode instanceof ObjectNode)) { -+ throw new IllegalArgumentException("Class " + type.getName() + -+ " would not be serialized as a JSON object and therefore has no schema."); -+ } -+ -+ return new JsonSchema((ObjectNode) schemaNode); -+ } -+ - public boolean hasSerializerFor(SerializationConfig config, - Class cls, SerializerFactory jsf) - { -Index: src/java/org/codehaus/jackson/map/ser/JsonValueSerializer.java -=================================================================== ---- src/java/org/codehaus/jackson/map/ser/JsonValueSerializer.java (revision 353) -+++ src/java/org/codehaus/jackson/map/ser/JsonValueSerializer.java (working copy) -@@ -4,9 +4,11 @@ - import java.lang.reflect.InvocationTargetException; - import java.lang.reflect.Method; - import java.lang.reflect.Modifier; -+import java.lang.reflect.Type; - - import org.codehaus.jackson.JsonGenerationException; - import org.codehaus.jackson.JsonGenerator; -+import org.codehaus.jackson.JsonNode; - import org.codehaus.jackson.map.*; - - /** -@@ -73,12 +75,19 @@ - throw JsonMappingException.wrapWithPath(t, bean, _accessorMethod.getName()+"()"); - } - } -- -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ return _serializer.getSchema(provider, null); -+ } -+ - /* -- //////////////////////////////////////////////////////// -- // ResolvableSerializer impl -- //////////////////////////////////////////////////////// -- */ -+ //////////////////////////////////////////////////////// -+ // ResolvableSerializer impl -+ //////////////////////////////////////////////////////// -+ */ - - /** - * We can try to find the actual serializer for value, if we can -Index: src/java/org/codehaus/jackson/map/ser/BasicSerializerFactory.java -=================================================================== ---- src/java/org/codehaus/jackson/map/ser/BasicSerializerFactory.java (revision 353) -+++ src/java/org/codehaus/jackson/map/ser/BasicSerializerFactory.java (working copy) -@@ -1,16 +1,24 @@ - package org.codehaus.jackson.map.ser; - -+import org.codehaus.jackson.JsonGenerationException; -+import org.codehaus.jackson.JsonGenerator; -+import org.codehaus.jackson.JsonNode; -+import org.codehaus.jackson.annotate.JsonUseSerializer; -+import org.codehaus.jackson.map.*; -+import org.codehaus.jackson.map.introspect.Annotated; -+import org.codehaus.jackson.map.introspect.BasicBeanDescription; -+import org.codehaus.jackson.map.type.TypeFactory; -+import org.codehaus.jackson.node.ArrayNode; -+import org.codehaus.jackson.node.JsonNodeFactory; -+import org.codehaus.jackson.node.ObjectNode; -+import org.codehaus.jackson.type.JavaType; -+ - import java.io.IOException; -+import java.lang.reflect.Type; - import java.math.BigDecimal; - import java.math.BigInteger; - import java.util.*; - --import org.codehaus.jackson.*; --import org.codehaus.jackson.annotate.JsonUseSerializer; --import org.codehaus.jackson.map.*; --import org.codehaus.jackson.map.introspect.Annotated; --import org.codehaus.jackson.map.introspect.BasicBeanDescription; -- - /** - * Factory class that can provide serializers for standard JDK classes, - * as well as custom classes that extend standard classes or implement -@@ -354,6 +362,16 @@ - { - jgen.writeBoolean(value.booleanValue()); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "boolean"); -+ objectNode.put("optional", "true"); //(ryan) it may not, in fact, be optional, but there's no way to tell whether we're referencing a boolean or java.lang.Boolean. -+ return objectNode; -+ } - } - - /** -@@ -368,6 +386,16 @@ - { - jgen.writeString(value); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "string"); -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } - - /** -@@ -393,6 +421,16 @@ - { - jgen.writeString(value.toString()); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "string"); -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } - - /** -@@ -408,6 +446,16 @@ - { - jgen.writeString(value.getName()); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "string"); -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } - - /* -@@ -425,6 +473,16 @@ - { - jgen.writeNumber(value.intValue()); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "integer"); -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } - - /** -@@ -443,6 +501,16 @@ - { - jgen.writeNumber(value.intValue()); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "integer"); -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } - - public final static class LongSerializer -@@ -456,6 +524,16 @@ - { - jgen.writeNumber(value.longValue()); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "number"); -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } - - public final static class FloatSerializer -@@ -469,6 +547,16 @@ - { - jgen.writeNumber(value.floatValue()); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "number"); -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } - - public final static class DoubleSerializer -@@ -482,6 +570,16 @@ - { - jgen.writeNumber(value.doubleValue()); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "number"); -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } - - /** -@@ -500,6 +598,16 @@ - // We'll have to use fallback "untyped" number write method - jgen.writeNumber(value.toString()); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "number"); -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } - - -@@ -518,6 +626,26 @@ - { - jgen.writeString(value.name()); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "string"); -+ if (typeHint != null) { -+ JavaType type = TypeFactory.instance._fromType(typeHint); -+ if (type.isEnumType()) { -+ EnumSet enumSet = EnumSet.allOf((Class) type.getRawClass()); -+ ArrayNode arrayNode = JsonNodeFactory.instance.arrayNode(); -+ for (Enum enumValue : enumSet) { -+ arrayNode.add(enumValue.name()); -+ } -+ } -+ } -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } - - /** -@@ -535,6 +663,17 @@ - { - provider.defaultSerializeDateValue(value.getTimeInMillis(), jgen); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "string"); -+ //todo: (ryan) add a format for the date in the schema? -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } - - /** -@@ -551,6 +690,17 @@ - { - provider.defaultSerializeDateValue(value, jgen); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "string"); -+ //todo: (ryan) add a format for the date in the schema? -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } - - /** -@@ -567,6 +717,17 @@ - { - jgen.writeString(value.toString()); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "string"); -+ //todo: (ryan) add a format for the date in the schema? -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } - - public final static class SqlTimeSerializer -@@ -578,6 +739,16 @@ - { - jgen.writeString(value.toString()); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "string"); -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } - - /** -@@ -598,6 +769,15 @@ - { - jgen.writeNull(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "null"); -+ return objectNode; -+ } - } - - public final static class SerializableSerializer -@@ -613,5 +793,45 @@ - { - value.serialize(jgen, provider); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ String schemaType = "any"; -+ String objectProperties = null; -+ String itemDefinition = null; -+ if (typeHint != null) { -+ Class rawClass = TypeFactory.fromType(typeHint).getRawClass(); -+ if (rawClass.isAnnotationPresent(JsonSerializableSchema.class)) { -+ JsonSerializableSchema schemaInfo = rawClass.getAnnotation(JsonSerializableSchema.class); -+ schemaType = schemaInfo.schemaType(); -+ if (!"##irrelevant".equals(schemaInfo.schemaObjectPropertiesDefinition())) { -+ objectProperties = schemaInfo.schemaObjectPropertiesDefinition(); -+ } -+ if (!"##irrelevant".equals(schemaInfo.schemaItemDefinition())) { -+ itemDefinition = schemaInfo.schemaItemDefinition(); -+ } -+ } -+ } -+ objectNode.put("type", schemaType); -+ if (objectProperties != null) { -+ try { -+ objectNode.put("properties", new ObjectMapper().readValue(objectProperties, JsonNode.class)); -+ } catch (IOException e) { -+ throw new IllegalStateException(e); -+ } -+ } -+ if (itemDefinition != null) { -+ try { -+ objectNode.put("items", new ObjectMapper().readValue(itemDefinition, JsonNode.class)); -+ } catch (IOException e) { -+ throw new IllegalStateException(e); -+ } -+ } -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } - } - } -Index: src/java/org/codehaus/jackson/map/ser/FailingSerializer.java -=================================================================== ---- src/java/org/codehaus/jackson/map/ser/FailingSerializer.java (revision 353) -+++ src/java/org/codehaus/jackson/map/ser/FailingSerializer.java (working copy) -@@ -1,11 +1,14 @@ - package org.codehaus.jackson.map.ser; - - import java.io.IOException; -+import java.lang.reflect.Type; - - import org.codehaus.jackson.JsonGenerationException; - import org.codehaus.jackson.JsonGenerator; -+import org.codehaus.jackson.JsonNode; - import org.codehaus.jackson.map.JsonSerializer; - import org.codehaus.jackson.map.SerializerProvider; -+import org.codehaus.jackson.map.JsonMappingException; - - /** - * Special bogus "serializer" that will throw -@@ -26,4 +29,11 @@ - { - throw new JsonGenerationException(_msg); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ throw new UnsupportedOperationException(); -+ } - } -Index: src/java/org/codehaus/jackson/map/ser/ToStringSerializer.java -=================================================================== ---- src/java/org/codehaus/jackson/map/ser/ToStringSerializer.java (revision 353) -+++ src/java/org/codehaus/jackson/map/ser/ToStringSerializer.java (working copy) -@@ -1,11 +1,16 @@ - package org.codehaus.jackson.map.ser; - - import java.io.IOException; -+import java.lang.reflect.Type; - - import org.codehaus.jackson.JsonGenerationException; - import org.codehaus.jackson.JsonGenerator; -+import org.codehaus.jackson.JsonNode; -+import org.codehaus.jackson.node.ObjectNode; -+import org.codehaus.jackson.node.JsonNodeFactory; - import org.codehaus.jackson.map.JsonSerializer; - import org.codehaus.jackson.map.SerializerProvider; -+import org.codehaus.jackson.map.JsonMappingException; - - /** - * Simple general purpose serializer, useful for any -@@ -35,4 +40,15 @@ - { - jgen.writeString(value.toString()); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "string"); -+ objectNode.put("optional", "true"); -+ return objectNode; -+ } -+ - } -Index: src/java/org/codehaus/jackson/map/ser/ArraySerializers.java -=================================================================== ---- src/java/org/codehaus/jackson/map/ser/ArraySerializers.java (revision 353) -+++ src/java/org/codehaus/jackson/map/ser/ArraySerializers.java (working copy) -@@ -2,9 +2,16 @@ - - import java.io.IOException; - import java.lang.reflect.InvocationTargetException; -+import java.lang.reflect.Type; -+import java.lang.reflect.GenericArrayType; - - import org.codehaus.jackson.*; -+import org.codehaus.jackson.type.JavaType; -+import org.codehaus.jackson.node.ObjectNode; -+import org.codehaus.jackson.node.JsonNodeFactory; - import org.codehaus.jackson.map.*; -+import org.codehaus.jackson.map.type.TypeFactory; -+import org.codehaus.jackson.map.type.ArrayType; - - /** - * Dummy container class to group standard array serializer implementations. -@@ -79,6 +86,24 @@ - } - jgen.writeEndArray(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "array"); -+ if (typeHint != null) { -+ JavaType javaType = TypeFactory.instance._fromType(typeHint); -+ if (javaType.isArrayType()) { -+ Class componentType = ((ArrayType) javaType).getComponentType().getRawClass(); -+ JsonSerializer ser = provider.findValueSerializer(componentType); -+ o.put("items", ser.getSchema(provider, null)); -+ } -+ } -+ o.put("optional", true); -+ return o; -+ } - } - - public final static class StringArraySerializer -@@ -111,6 +136,18 @@ - } - jgen.writeEndArray(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "array"); -+ ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); -+ itemSchema.put("type", "string"); -+ o.put("items", itemSchema); -+ o.put("optional", true); -+ return o; -+ } - } - - public final static class BooleanArraySerializer -@@ -126,6 +163,18 @@ - } - jgen.writeEndArray(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "array"); -+ ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); -+ itemSchema.put("type", "boolean"); -+ o.put("items", itemSchema); -+ o.put("optional", true); -+ return o; -+ } - } - - /** -@@ -142,6 +191,18 @@ - { - jgen.writeBinary(value); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "array"); -+ ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); -+ itemSchema.put("type", "string"); //binary values written as strings? -+ o.put("items", itemSchema); -+ o.put("optional", true); -+ return o; -+ } - } - - public final static class ShortArraySerializer -@@ -158,6 +219,18 @@ - } - jgen.writeEndArray(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "array"); -+ ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); -+ itemSchema.put("type", "integer"); //no "short" type defined by json -+ o.put("items", itemSchema); -+ o.put("optional", true); -+ return o; -+ } - } - - /** -@@ -174,6 +247,18 @@ - { - jgen.writeString(value, 0, value.length); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "array"); -+ ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); -+ itemSchema.put("type", "string"); -+ o.put("items", itemSchema); -+ o.put("optional", true); -+ return o; -+ } - } - - -@@ -190,6 +275,18 @@ - } - jgen.writeEndArray(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "array"); -+ ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); -+ itemSchema.put("type", "integer"); -+ o.put("items", itemSchema); -+ o.put("optional", true); -+ return o; -+ } - } - - public final static class LongArraySerializer -@@ -205,6 +302,18 @@ - } - jgen.writeEndArray(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "array"); -+ ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); -+ itemSchema.put("type", "number"); -+ o.put("items", itemSchema); -+ o.put("optional", true); -+ return o; -+ } - } - - public final static class FloatArraySerializer -@@ -220,6 +329,18 @@ - } - jgen.writeEndArray(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "array"); -+ ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); -+ itemSchema.put("type", "number"); -+ o.put("items", itemSchema); -+ o.put("optional", true); -+ return o; -+ } - } - - public final static class DoubleArraySerializer -@@ -235,5 +356,17 @@ - } - jgen.writeEndArray(); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ { -+ ObjectNode o = JsonNodeFactory.instance.objectNode(); -+ o.put("type", "array"); -+ ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); -+ itemSchema.put("type", "number"); -+ o.put("items", itemSchema); -+ o.put("optional", true); -+ return o; -+ } - } - } -Index: src/java/org/codehaus/jackson/map/ser/StdKeySerializer.java -=================================================================== ---- src/java/org/codehaus/jackson/map/ser/StdKeySerializer.java (revision 353) -+++ src/java/org/codehaus/jackson/map/ser/StdKeySerializer.java (working copy) -@@ -1,11 +1,16 @@ - package org.codehaus.jackson.map.ser; - - import java.io.IOException; -+import java.lang.reflect.Type; - - import org.codehaus.jackson.JsonGenerationException; - import org.codehaus.jackson.JsonGenerator; -+import org.codehaus.jackson.JsonNode; -+import org.codehaus.jackson.node.ObjectNode; -+import org.codehaus.jackson.node.JsonNodeFactory; - import org.codehaus.jackson.map.JsonSerializer; - import org.codehaus.jackson.map.SerializerProvider; -+import org.codehaus.jackson.map.JsonMappingException; - - /** - * Specialized serializer that can be used as the generic key -@@ -25,4 +30,13 @@ - ((String) value) : value.toString(); - jgen.writeFieldName(keyStr); - } -+ -+ @Override -+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) -+ throws JsonMappingException -+ { -+ ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); -+ objectNode.put("type", "string"); -+ return objectNode; -+ } - } -Index: src/java/org/codehaus/jackson/map/ObjectMapper.java -=================================================================== ---- src/java/org/codehaus/jackson/map/ObjectMapper.java (revision 353) -+++ src/java/org/codehaus/jackson/map/ObjectMapper.java (working copy) -@@ -5,6 +5,7 @@ - import java.util.concurrent.ConcurrentHashMap; - - import org.codehaus.jackson.*; -+import org.codehaus.jackson.schema.JsonSchema; - import org.codehaus.jackson.map.deser.StdDeserializationContext; - import org.codehaus.jackson.map.deser.StdDeserializerProvider; - import org.codehaus.jackson.map.introspect.BasicClassIntrospector; -@@ -13,6 +14,7 @@ - import org.codehaus.jackson.map.ser.BeanSerializerFactory; - import org.codehaus.jackson.map.type.TypeFactory; - import org.codehaus.jackson.node.NullNode; -+import org.codehaus.jackson.node.ObjectNode; - import org.codehaus.jackson.type.JavaType; - import org.codehaus.jackson.type.TypeReference; - -@@ -580,6 +582,18 @@ - } - - /** -+ * Generate the {@link http://json-schema.org/ Json-schema} for the specified class. -+ * -+ * @param t The class. -+ * @return The json-schema. -+ */ -+ public JsonSchema generateJsonSchema(Class t) -+ throws JsonMappingException -+ { -+ return _serializerProvider.generateJsonSchema(t, _getUnsharedSConfig(), _serializerFactory); -+ } -+ -+ /** - * Method called to configure the generator as necessary and then - * call write functionality - */ -Index: src/java/org/codehaus/jackson/map/JsonSerializableSchema.java -=================================================================== ---- src/java/org/codehaus/jackson/map/JsonSerializableSchema.java (revision 353) -+++ src/java/org/codehaus/jackson/map/JsonSerializableSchema.java (working copy) -@@ -1,21 +1,40 @@ - package org.codehaus.jackson.map; - --import java.io.IOException; -+import org.codehaus.jackson.node.ObjectNode; - --import org.codehaus.jackson.*; -+import java.lang.annotation.Target; -+import java.lang.annotation.ElementType; -+import java.lang.annotation.Retention; -+import java.lang.annotation.RetentionPolicy; - - /** -- * Interface that can be implemented by objects that know how to -- * serialize themselves to Json, using {@link JsonGenerator} -- * (and {@link SerializerProvider} if necessary). -- *

-- * Note that implementing this interface binds implementing object -- * closely to Jackson API, and that it is often not necessary to do -- * so -- if class is a bean, it can be serialized without -- * implementing this interface. -+ * Metadata that can be applied to a class that implements {@link org.codehaus.jackson.map.JsonSerializable} in -+ * order to provide a definition of its schema. - */ --public interface JsonSerializable -+@Target (ElementType.TYPE) -+@Retention(RetentionPolicy.RUNTIME) -+public @interface JsonSerializableSchema - { -- public void serialize(JsonGenerator jgen, SerializerProvider provider) -- throws IOException, JsonProcessingException; --} -+ -+ /** -+ * The schema type for this JsonSerializable instance. -+ * Possible values: "string", "number", "boolean", "object", "array", "null", "any" -+ * -+ * @return The schema type for this JsonSerializable instance. -+ */ -+ String schemaType() default "any"; -+ -+ /** -+ * If the schema type is "object", the node that defines the properties of the object. -+ * -+ * @return The node representing the schema properties, or "##irrelevant" if irrelevant. -+ */ -+ String schemaObjectPropertiesDefinition() default "##irrelevant"; -+ -+ /** -+ * If the schema type if "array", the node that defines the schema for the items in the array. -+ * -+ * @return The schema for the items in the array, or "##irrelevant" if irrelevant. -+ */ -+ String schemaItemDefinition() default "##irrelevant"; -+} -\ No newline at end of file -Index: src/java/org/codehaus/jackson/map/SerializerProvider.java -=================================================================== ---- src/java/org/codehaus/jackson/map/SerializerProvider.java (revision 353) -+++ src/java/org/codehaus/jackson/map/SerializerProvider.java (working copy) -@@ -4,6 +4,7 @@ - import java.util.Date; - - import org.codehaus.jackson.*; -+import org.codehaus.jackson.schema.JsonSchema; - - /** - * Abstract class that defines API used by {@link ObjectMapper} and -@@ -44,6 +45,19 @@ - throws IOException, JsonGenerationException; - - /** -+ * Generate the {@link http://json-schema.org/ json-schema}. -+ * -+ * @param type The type. -+ * @param config The config. -+ * @param jsf The serializer factory. -+ * @return The config. -+ */ -+ public JsonSchema generateJsonSchema(Class type, SerializationConfig config, SerializerFactory jsf) -+ throws JsonMappingException { -+ throw new UnsupportedOperationException(); -+ } -+ -+ /** - * Method that can be called to see if this serializer provider - * can find a serializer for an instance of given class. - *

Index: src/test/org/codehaus/jackson/schema/TestGenerateJsonSchema.java =================================================================== --- src/test/org/codehaus/jackson/schema/TestGenerateJsonSchema.java (revision 0) +++ src/test/org/codehaus/jackson/schema/TestGenerateJsonSchema.java (revision 0) @@ -0,0 +1,99 @@ +package org.codehaus.jackson.schema; + +import junit.framework.TestCase; + +import java.util.Collection; + +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.JsonNode; + +/** + * @author Ryan Heaton + */ +public class TestGenerateJsonSchema + extends TestCase +{ + + /** + * tests generating json-schema stuff. + */ + public void testGeneratingJsonSchema() + throws Exception + { + ObjectMapper mapper = new ObjectMapper(); + JsonSchema jsonSchema = mapper.generateJsonSchema(SimpleBean.class); +// System.out.println(jsonSchema.toString()); + assertNotNull(jsonSchema); + assertEquals("object", jsonSchema.getSchemaNode().get("type").getValueAsText()); + assertEquals(true, jsonSchema.getSchemaNode().get("optional").getBooleanValue()); + JsonNode propertiesSchema = jsonSchema.getSchemaNode().get("properties"); + assertNotNull(propertiesSchema); + JsonNode property1Schema = propertiesSchema.get("property1"); + assertNotNull(property1Schema); + assertEquals("integer", property1Schema.get("type").getValueAsText()); + assertEquals(true, property1Schema.get("optional").getBooleanValue()); + JsonNode property2Schema = propertiesSchema.get("property2"); + assertNotNull(property2Schema); + assertEquals("string", property2Schema.get("type").getValueAsText()); + assertEquals(true, property2Schema.get("optional").getBooleanValue()); + JsonNode property3Schema = propertiesSchema.get("property3"); + assertNotNull(property3Schema); + assertEquals("array", property3Schema.get("type").getValueAsText()); + assertEquals(true, property3Schema.get("optional").getBooleanValue()); + assertEquals("string", property3Schema.get("items").get("type").getValueAsText()); + JsonNode property4Schema = propertiesSchema.get("property4"); + assertNotNull(property4Schema); + assertEquals("array", property4Schema.get("type").getValueAsText()); + assertEquals(true, property4Schema.get("optional").getBooleanValue()); + assertEquals("number", property4Schema.get("items").get("type").getValueAsText()); + } + + public static class SimpleBean + { + private int property1; + private String property2; + private String[] property3; + private Collection property4; + + public int getProperty1() + { + return property1; + } + + public void setProperty1(int property1) + { + this.property1 = property1; + } + + public String getProperty2() + { + return property2; + } + + public void setProperty2(String property2) + { + this.property2 = property2; + } + + public String[] getProperty3() + { + return property3; + } + + public void setProperty3(String[] property3) + { + this.property3 = property3; + } + + public Collection getProperty4() + { + return property4; + } + + public void setProperty4(Collection property4) + { + this.property4 = property4; + } + } + +} Property changes on: src/test/org/codehaus/jackson/schema/TestGenerateJsonSchema.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Date Revision Added: svn:eol-style + native Index: src/test/org/codehaus/jackson/map/introspect/TestJacksonAnnotationIntrospector.java =================================================================== --- src/test/org/codehaus/jackson/map/introspect/TestJacksonAnnotationIntrospector.java (revision 372) +++ src/test/org/codehaus/jackson/map/introspect/TestJacksonAnnotationIntrospector.java (working copy) @@ -17,6 +17,7 @@ import java.io.StringWriter; import java.util.Arrays; import java.util.List; +import java.lang.reflect.Type; /** * @author Ryan Heaton Index: src/java/org/codehaus/jackson/node/ObjectNode.java =================================================================== --- src/java/org/codehaus/jackson/node/ObjectNode.java (revision 372) +++ src/java/org/codehaus/jackson/node/ObjectNode.java (working copy) @@ -10,7 +10,7 @@ /** * Note that maps to Json Object structures in Json content. */ -public final class ObjectNode +public class ObjectNode extends ContainerNode { LinkedHashMap _children = null; Index: src/java/org/codehaus/jackson/schema/SchemaAware.java =================================================================== --- src/java/org/codehaus/jackson/schema/SchemaAware.java (revision 0) +++ src/java/org/codehaus/jackson/schema/SchemaAware.java (revision 0) @@ -0,0 +1,25 @@ +package org.codehaus.jackson.schema; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.SerializerProvider; +import org.codehaus.jackson.map.JsonMappingException; + +import java.lang.reflect.Type; + +/** + * Marker interface for schema-aware serializers. + * + * @author Ryan Heaton + */ +public interface SchemaAware +{ + /** + * Get the representation of the schema to which this serializer will conform. + * + * @param provider The serializer provider. + * @param typeHint A hint about the type. + * @return The {@link http://json-schema.org/ json-schema} for this serializer. + */ + JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException; +} Property changes on: src/java/org/codehaus/jackson/schema/SchemaAware.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Date Revision Added: svn:eol-style + native Index: src/java/org/codehaus/jackson/schema/JsonSchema.java =================================================================== --- src/java/org/codehaus/jackson/schema/JsonSchema.java (revision 0) +++ src/java/org/codehaus/jackson/schema/JsonSchema.java (revision 0) @@ -0,0 +1,60 @@ +package org.codehaus.jackson.schema; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.map.SerializerProvider; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.node.ObjectNode; +import org.codehaus.jackson.node.JsonNodeFactory; + +import java.io.IOException; +import java.lang.reflect.Type; + +/** + * A {@link org.codehaus.jackson.JsonNode} that represents a JSON-Schema instance. + * + * @author Ryan Heaton + * @see http://json-schema.org/ + */ +public class JsonSchema +{ + + private final ObjectNode schema; + + public JsonSchema(ObjectNode schema) + { + this.schema = schema; + } + + public ObjectNode getSchemaNode() + { + return schema; + } + + @Override + public String toString() + { + return this.schema.toString(); + } + + @Override + public boolean equals(Object o) + { + return this.schema.equals(o); + } + + /** + * Get the default schema node. + * + * @return The default schema node. + */ + public static JsonNode getDefaultSchemaNode() + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "any"); + objectNode.put("optional", true); + return objectNode; + } + +} Property changes on: src/java/org/codehaus/jackson/schema/JsonSchema.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Date Revision Added: svn:eol-style + native Index: src/java/org/codehaus/jackson/xc/XmlAdapterJsonSerializer.java =================================================================== --- src/java/org/codehaus/jackson/xc/XmlAdapterJsonSerializer.java (revision 372) +++ src/java/org/codehaus/jackson/xc/XmlAdapterJsonSerializer.java (working copy) @@ -2,6 +2,8 @@ import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.schema.SchemaAware; +import org.codehaus.jackson.schema.JsonSchema; import org.codehaus.jackson.map.JsonMappingException; import org.codehaus.jackson.map.JsonSerializer; import org.codehaus.jackson.map.SerializerProvider; @@ -14,7 +16,7 @@ /** * @author Ryan Heaton */ -public class XmlAdapterJsonSerializer extends JsonSerializer +public class XmlAdapterJsonSerializer extends JsonSerializer implements SchemaAware { private final XmlAdapter xmlAdapter; @@ -37,12 +39,16 @@ jsonSerializer.serialize(adapted, jgen, provider); } -// @Override -// public JsonNode getSchema(SerializerProvider provider, Type typeHint) -// throws JsonMappingException -// { -// return provider.findValueSerializer(findValueClass()).getSchema(provider, typeHint); -// } + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + JsonSerializer ser = provider.findValueSerializer(findValueClass()); + JsonNode schemaNode = (ser instanceof SchemaAware) ? + ((SchemaAware) ser).getSchema(provider, null) : + JsonSchema.getDefaultSchemaNode(); + return schemaNode; + } private Class findValueClass() { Index: src/java/org/codehaus/jackson/map/ser/StdSerializerProvider.java =================================================================== --- src/java/org/codehaus/jackson/map/ser/StdSerializerProvider.java (revision 372) +++ src/java/org/codehaus/jackson/map/ser/StdSerializerProvider.java (working copy) @@ -3,8 +3,12 @@ import java.io.IOException; import java.text.DateFormat; import java.util.Date; +import java.lang.reflect.Type; import org.codehaus.jackson.*; +import org.codehaus.jackson.node.ObjectNode; +import org.codehaus.jackson.schema.JsonSchema; +import org.codehaus.jackson.schema.SchemaAware; import org.codehaus.jackson.map.*; @@ -206,6 +210,35 @@ inst._serializeValue(jgen, value); } + @Override + public JsonSchema generateJsonSchema(Class type, SerializationConfig config, SerializerFactory jsf) + throws JsonMappingException + { + if (type == null) { + throw new IllegalArgumentException("A class must be provided."); + } + + /* First: we need a separate instance, which will hold a copy of the + * non-shared ("local") read-only lookup Map for fast + * class-to-serializer lookup + */ + StdSerializerProvider inst = createInstance(config, jsf); + // sanity check to avoid weird errors; to ensure sub-classes do override createInstance + if (inst.getClass() != getClass()) { + throw new IllegalStateException("Broken serializer provider: createInstance returned instance of type "+inst.getClass()+"; blueprint of type "+getClass()); + } + JsonSerializer ser = inst.findValueSerializer(type); + JsonNode schemaNode = (ser instanceof SchemaAware) ? + ((SchemaAware) ser).getSchema(inst, null) : + JsonSchema.getDefaultSchemaNode(); + if (!(schemaNode instanceof ObjectNode)) { + throw new IllegalArgumentException("Class " + type.getName() + + " would not be serialized as a JSON object and therefore has no schema."); + } + + return new JsonSchema((ObjectNode) schemaNode); + } + public boolean hasSerializerFor(SerializationConfig config, Class cls, SerializerFactory jsf) { Index: src/java/org/codehaus/jackson/map/ser/ContainerSerializers.java =================================================================== --- src/java/org/codehaus/jackson/map/ser/ContainerSerializers.java (revision 372) +++ src/java/org/codehaus/jackson/map/ser/ContainerSerializers.java (working copy) @@ -1,11 +1,24 @@ package org.codehaus.jackson.map.ser; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.JsonSerializer; +import org.codehaus.jackson.map.SerializerProvider; +import org.codehaus.jackson.map.type.CollectionType; +import org.codehaus.jackson.map.type.TypeFactory; +import org.codehaus.jackson.node.JsonNodeFactory; +import org.codehaus.jackson.node.ObjectNode; +import org.codehaus.jackson.schema.JsonSchema; +import org.codehaus.jackson.schema.SchemaAware; +import org.codehaus.jackson.type.JavaType; + import java.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.*; -import org.codehaus.jackson.*; -import org.codehaus.jackson.map.*; - /** * Dummy container class to group standard container serializers: serializers * that can serialize things like {@link java.util.List}s, @@ -30,7 +43,7 @@ * that can not}. */ public final static class IndexedListSerializer - extends JsonSerializer> + extends JsonSerializer> implements SchemaAware { public final static IndexedListSerializer instance = new IndexedListSerializer(); @@ -76,6 +89,27 @@ jgen.writeEndArray(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "array"); + if (typeHint != null) { + JavaType javaType = TypeFactory.instance._fromType(typeHint); + if (javaType instanceof CollectionType) { + Class componentType = ((CollectionType) javaType).getElementType().getRawClass(); + JsonSerializer ser = provider.findValueSerializer(componentType); + JsonNode schemaNode = (ser instanceof SchemaAware) ? + ((SchemaAware) ser).getSchema(provider, null) : + JsonSchema.getDefaultSchemaNode(); + o.put("items", schemaNode); + } + } + o.put("optional", true); + return o; + } } /** @@ -86,7 +120,7 @@ * to iterate over elements. */ public final static class CollectionSerializer - extends JsonSerializer> + extends JsonSerializer> implements SchemaAware { public final static CollectionSerializer instance = new CollectionSerializer(); @@ -132,10 +166,31 @@ } jgen.writeEndArray(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "array"); + if (typeHint != null) { + JavaType javaType = TypeFactory.instance._fromType(typeHint); + if (javaType instanceof CollectionType) { + Class componentType = ((CollectionType) javaType).getElementType().getRawClass(); + JsonSerializer ser = provider.findValueSerializer(componentType); + JsonNode schemaNode = (ser instanceof SchemaAware) ? + ((SchemaAware) ser).getSchema(provider, null) : + JsonSchema.getDefaultSchemaNode(); + o.put("items", schemaNode); + } + } + o.put("optional", true); + return o; + } } public final static class IteratorSerializer - extends JsonSerializer> + extends JsonSerializer> implements SchemaAware { public final static IteratorSerializer instance = new IteratorSerializer(); @@ -168,10 +223,31 @@ } jgen.writeEndArray(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "array"); + if (typeHint instanceof ParameterizedType) { + Type[] typeArgs = ((ParameterizedType) typeHint).getActualTypeArguments(); + if (typeArgs.length == 1) { + JavaType javaType = TypeFactory.instance._fromType(typeArgs[0]); + JsonSerializer ser = provider.findValueSerializer(javaType.getRawClass()); + JsonNode schemaNode = (ser instanceof SchemaAware) ? + ((SchemaAware) ser).getSchema(provider, null) : + JsonSchema.getDefaultSchemaNode(); + o.put("items", schemaNode); + } + } + o.put("optional", true); + return o; + } } public final static class IterableSerializer - extends JsonSerializer> + extends JsonSerializer> implements SchemaAware { public final static IterableSerializer instance = new IterableSerializer(); @@ -205,10 +281,31 @@ } jgen.writeEndArray(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "array"); + if (typeHint instanceof ParameterizedType) { + Type[] typeArgs = ((ParameterizedType) typeHint).getActualTypeArguments(); + if (typeArgs.length == 1) { + JavaType javaType = TypeFactory.instance._fromType(typeArgs[0]); + JsonSerializer ser = provider.findValueSerializer(javaType.getRawClass()); + JsonNode schemaNode = (ser instanceof SchemaAware) ? + ((SchemaAware) ser).getSchema(provider, null) : + JsonSchema.getDefaultSchemaNode(); + o.put("items", schemaNode); + } + } + o.put("optional", true); + return o; + } } public final static class EnumSetSerializer - extends JsonSerializer>> + extends JsonSerializer>> implements SchemaAware { public final static CollectionSerializer instance = new CollectionSerializer(); @@ -222,6 +319,27 @@ } jgen.writeEndArray(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "array"); + if (typeHint instanceof ParameterizedType) { + Type[] typeArgs = ((ParameterizedType) typeHint).getActualTypeArguments(); + if (typeArgs.length == 1) { + JavaType javaType = TypeFactory.instance._fromType(typeArgs[0]); + JsonSerializer ser = provider.findValueSerializer(javaType.getRawClass()); + JsonNode schemaNode = (ser instanceof SchemaAware) ? + ((SchemaAware) ser).getSchema(provider, null) : + JsonSchema.getDefaultSchemaNode(); + o.put("items", schemaNode); + } + } + o.put("optional", true); + return o; + } } /* @@ -231,7 +349,7 @@ */ public final static class MapSerializer - extends JsonSerializer> + extends JsonSerializer> implements SchemaAware { public final static MapSerializer instance = new MapSerializer(); @@ -286,10 +404,21 @@ jgen.writeEndObject(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "object"); + //(ryan) even though it's possible to statically determine the "value" type of the map, + // there's no way to statically determine the keys, so the "properties" can't be determined. + o.put("optional", true); + return o; + } } public final static class EnumMapSerializer - extends JsonSerializer, ?>> + extends JsonSerializer, ?>> implements SchemaAware { @Override public void serialize(EnumMap,?> value, JsonGenerator jgen, SerializerProvider provider) @@ -328,5 +457,32 @@ } jgen.writeEndObject(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "object"); + if (typeHint instanceof ParameterizedType) { + Type[] typeArgs = ((ParameterizedType) typeHint).getActualTypeArguments(); + if (typeArgs.length == 2) { + JavaType enumType = TypeFactory.instance._fromType(typeArgs[0]); + JavaType valueType = TypeFactory.instance._fromType(typeArgs[1]); + Class enumClass = (Class) enumType.getRawClass(); + ObjectNode propsNode = JsonNodeFactory.instance.objectNode(); + for (Object enumValue : EnumSet.allOf(enumClass)) { + JsonSerializer ser = provider.findValueSerializer(valueType.getRawClass()); + JsonNode schemaNode = (ser instanceof SchemaAware) ? + ((SchemaAware) ser).getSchema(provider, null) : + JsonSchema.getDefaultSchemaNode(); + propsNode.put(provider.getConfig().getAnnotationIntrospector().findEnumValue((Enum)enumValue), schemaNode); + } + o.put("properties", propsNode); + } + } + o.put("optional", true); + return o; + } } } Index: src/java/org/codehaus/jackson/map/ser/BeanSerializer.java =================================================================== --- src/java/org/codehaus/jackson/map/ser/BeanSerializer.java (revision 372) +++ src/java/org/codehaus/jackson/map/ser/BeanSerializer.java (working copy) @@ -3,10 +3,16 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; +import java.lang.reflect.Type; import java.util.Collection; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.schema.SchemaAware; +import org.codehaus.jackson.schema.JsonSchema; +import org.codehaus.jackson.node.ObjectNode; +import org.codehaus.jackson.node.JsonNodeFactory; import org.codehaus.jackson.map.*; /** @@ -19,7 +25,7 @@ */ public class BeanSerializer extends JsonSerializer - implements ResolvableSerializer + implements ResolvableSerializer, SchemaAware { final protected String _className; @@ -72,11 +78,38 @@ } } + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + //todo: should the classname go in the title? + //o.put("title", _className); + o.put("type", "object"); + o.put("optional", true); + ObjectNode propertiesNode = o.objectNode(); + for (int i = 0; i < _props.length; i++) { + BeanPropertyWriter prop = _props[i]; + Type hint = prop.getSerializationType(); + if (hint == null) { + hint = prop.getGenericPropertyType(); + } + JsonSerializer ser = provider.findValueSerializer(prop.getSerializationType() == null ? prop.getReturnType() : prop.getSerializationType()); + JsonNode schemaNode = (ser instanceof SchemaAware) ? + ((SchemaAware) ser).getSchema(provider, hint) : + JsonSchema.getDefaultSchemaNode(); + o.put("items", schemaNode); + propertiesNode.put(prop.getName(), schemaNode); + } + o.put("properties", propertiesNode); + return o; + } + /* - //////////////////////////////////////////////////////// - // ResolvableSerializer impl - //////////////////////////////////////////////////////// - */ + //////////////////////////////////////////////////////// + // ResolvableSerializer impl + //////////////////////////////////////////////////////// + */ public void resolve(SerializerProvider provider) throws JsonMappingException Index: src/java/org/codehaus/jackson/map/ser/JsonValueSerializer.java =================================================================== --- src/java/org/codehaus/jackson/map/ser/JsonValueSerializer.java (revision 372) +++ src/java/org/codehaus/jackson/map/ser/JsonValueSerializer.java (working copy) @@ -4,9 +4,13 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Type; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.schema.SchemaAware; +import org.codehaus.jackson.schema.JsonSchema; import org.codehaus.jackson.map.*; /** @@ -14,7 +18,7 @@ * {@link org.codehaus.jackson.annotate.JsonValue} annotation to * indicate that serialization should be done by calling the method * annotated, and serializing result it returns. - *

+ *

* Implementation note: we will post-process resulting serializer * (much like what is done with {@link BeanSerializer}) * to figure out actual serializers for final types. This must be @@ -22,8 +26,8 @@ * otherwise we could end up with an infinite loop. */ public final class JsonValueSerializer - extends JsonSerializer - implements ResolvableSerializer + extends JsonSerializer + implements ResolvableSerializer, SchemaAware { final Method _accessorMethod; @@ -31,9 +35,9 @@ /** * @param ser Explicit serializer to use, if caller knows it (which - * occurs if and only if the "value method" was annotated with - * {@link org.codehaus.jackson.map.annotate.JsonSerialize#using}), otherwise - * null + * occurs if and only if the "value method" was annotated with + * {@link org.codehaus.jackson.map.annotate.JsonSerialize#using}), otherwise + * null */ public JsonValueSerializer(Method valueMethod, JsonSerializer ser) { @@ -42,12 +46,12 @@ } public void serialize(Object bean, JsonGenerator jgen, SerializerProvider prov) - throws IOException, JsonGenerationException + throws IOException, JsonGenerationException { try { Object value = _accessorMethod.invoke(bean); JsonSerializer ser; - + if (value == null) { ser = prov.getNullValueSerializer(); } else { @@ -70,10 +74,19 @@ throw (Error) t; } // let's try to indicate the path best we can... - throw JsonMappingException.wrapWithPath(t, bean, _accessorMethod.getName()+"()"); + throw JsonMappingException.wrapWithPath(t, bean, _accessorMethod.getName() + "()"); } } + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + return (_serializer instanceof SchemaAware) ? + ((SchemaAware) _serializer).getSchema(provider, null) : + JsonSchema.getDefaultSchemaNode(); + } + /* //////////////////////////////////////////////////////// // ResolvableSerializer impl @@ -85,7 +98,7 @@ * statically figure out what the result type must be. */ public void resolve(SerializerProvider provider) - throws JsonMappingException + throws JsonMappingException { if (_serializer == null) { Class rt = _accessorMethod.getReturnType(); @@ -106,7 +119,8 @@ */ @Override - public String toString() { - return "(@JsonValue serializer for method "+_accessorMethod.getDeclaringClass()+"#"+_accessorMethod.getName()+")"; + public String toString() + { + return "(@JsonValue serializer for method " + _accessorMethod.getDeclaringClass() + "#" + _accessorMethod.getName() + ")"; } } Index: src/java/org/codehaus/jackson/map/ser/ToStringSerializer.java =================================================================== --- src/java/org/codehaus/jackson/map/ser/ToStringSerializer.java (revision 372) +++ src/java/org/codehaus/jackson/map/ser/ToStringSerializer.java (working copy) @@ -1,11 +1,17 @@ package org.codehaus.jackson.map.ser; import java.io.IOException; +import java.lang.reflect.Type; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.schema.SchemaAware; +import org.codehaus.jackson.node.ObjectNode; +import org.codehaus.jackson.node.JsonNodeFactory; import org.codehaus.jackson.map.JsonSerializer; import org.codehaus.jackson.map.SerializerProvider; +import org.codehaus.jackson.map.JsonMappingException; /** * Simple general purpose serializer, useful for any @@ -13,7 +19,7 @@ * value. */ public final class ToStringSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { /** * Singleton instance to use. @@ -36,4 +42,15 @@ { jgen.writeString(value.toString()); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "string"); + objectNode.put("optional", true); + return objectNode; + } + } Index: src/java/org/codehaus/jackson/map/ser/FailingSerializer.java =================================================================== --- src/java/org/codehaus/jackson/map/ser/FailingSerializer.java (revision 372) +++ src/java/org/codehaus/jackson/map/ser/FailingSerializer.java (working copy) @@ -1,11 +1,14 @@ package org.codehaus.jackson.map.ser; import java.io.IOException; +import java.lang.reflect.Type; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.JsonSerializer; import org.codehaus.jackson.map.SerializerProvider; +import org.codehaus.jackson.map.JsonMappingException; /** * Special bogus "serializer" that will throw Index: src/java/org/codehaus/jackson/map/ser/BasicSerializerFactory.java =================================================================== --- src/java/org/codehaus/jackson/map/ser/BasicSerializerFactory.java (revision 372) +++ src/java/org/codehaus/jackson/map/ser/BasicSerializerFactory.java (working copy) @@ -4,9 +4,16 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.util.*; +import java.lang.reflect.Type; import org.codehaus.jackson.*; +import org.codehaus.jackson.schema.SchemaAware; +import org.codehaus.jackson.type.JavaType; +import org.codehaus.jackson.node.JsonNodeFactory; +import org.codehaus.jackson.node.ObjectNode; +import org.codehaus.jackson.node.ArrayNode; import org.codehaus.jackson.map.*; +import org.codehaus.jackson.map.type.TypeFactory; import org.codehaus.jackson.map.introspect.Annotated; import org.codehaus.jackson.map.introspect.BasicBeanDescription; @@ -334,7 +341,7 @@ */ public final static class BooleanSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { final static BooleanSerializer instance = new BooleanSerializer(); @@ -344,13 +351,23 @@ { jgen.writeBoolean(value.booleanValue()); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "boolean"); + objectNode.put("optional", true); //(ryan) it may not, in fact, be optional, but there's no way to tell whether we're referencing a boolean or java.lang.Boolean. + return objectNode; + } } /** * This is the special serializer for regular {@link java.lang.String}s. */ public final static class StringSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { @Override public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) @@ -358,6 +375,16 @@ { jgen.writeString(value); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "string"); + objectNode.put("optional", true); + return objectNode; + } } /** @@ -368,7 +395,7 @@ */ @Deprecated public final static class StringLikeSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { public final static StringLikeSerializer instance = new StringLikeSerializer(); @@ -383,6 +410,16 @@ { jgen.writeString(value.toString()); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "string"); + objectNode.put("optional", true); + return objectNode; + } } /** @@ -390,7 +427,7 @@ * we can just store the name. */ public final static class ClassSerializer - extends JsonSerializer> + extends JsonSerializer> implements SchemaAware { @Override public void serialize(Class value, JsonGenerator jgen, SerializerProvider provider) @@ -398,6 +435,16 @@ { jgen.writeString(value.getName()); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "string"); + objectNode.put("optional", true); + return objectNode; + } } /* @@ -407,7 +454,7 @@ */ public final static class IntegerSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { @Override public void serialize(Integer value, JsonGenerator jgen, SerializerProvider provider) @@ -415,6 +462,16 @@ { jgen.writeNumber(value.intValue()); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "integer"); + objectNode.put("optional", true); + return objectNode; + } } /** @@ -423,7 +480,7 @@ * by calling {@link java.lang.Number#intValue}. */ public final static class IntLikeSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { final static IntLikeSerializer instance = new IntLikeSerializer(); @@ -433,10 +490,20 @@ { jgen.writeNumber(value.intValue()); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "integer"); + objectNode.put("optional", true); + return objectNode; + } } public final static class LongSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { final static LongSerializer instance = new LongSerializer(); @@ -446,10 +513,20 @@ { jgen.writeNumber(value.longValue()); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "number"); + objectNode.put("optional", true); + return objectNode; + } } public final static class FloatSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { final static FloatSerializer instance = new FloatSerializer(); @@ -459,10 +536,20 @@ { jgen.writeNumber(value.floatValue()); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "number"); + objectNode.put("optional", true); + return objectNode; + } } public final static class DoubleSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { final static DoubleSerializer instance = new DoubleSerializer(); @@ -472,6 +559,16 @@ { jgen.writeNumber(value.doubleValue()); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "number"); + objectNode.put("optional", true); + return objectNode; + } } /** @@ -479,7 +576,7 @@ * types of {@link Number}s (custom types). */ public final static class NumberSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { public final static NumberSerializer instance = new NumberSerializer(); @@ -490,6 +587,16 @@ // We'll have to use fallback "untyped" number write method jgen.writeNumber(value.toString()); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "number"); + objectNode.put("optional", true); + return objectNode; + } } @@ -501,6 +608,7 @@ public final static class EnumSerializer extends JsonSerializer> + implements SchemaAware { @Override public void serialize(Enum value, JsonGenerator jgen, SerializerProvider provider) @@ -508,6 +616,26 @@ { jgen.writeString(provider.getConfig().getAnnotationIntrospector().findEnumValue(value)); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "string"); + if (typeHint != null) { + JavaType type = TypeFactory.instance._fromType(typeHint); + if (type.isEnumType()) { + EnumSet enumSet = EnumSet.allOf((Class) type.getRawClass()); + ArrayNode arrayNode = JsonNodeFactory.instance.arrayNode(); + for (Enum enumValue : enumSet) { + arrayNode.add(provider.getConfig().getAnnotationIntrospector().findEnumValue(enumValue)); + } + } + } + objectNode.put("optional", true); + return objectNode; + } } /** @@ -516,7 +644,7 @@ * and json. */ public final static class CalendarSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { public final static CalendarSerializer instance = new CalendarSerializer(); @Override @@ -525,6 +653,17 @@ { provider.defaultSerializeDateValue(value.getTimeInMillis(), jgen); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "string"); + //todo: (ryan) add a format for the date in the schema? + objectNode.put("optional", true); + return objectNode; + } } /** @@ -532,7 +671,7 @@ * potentially more readable Strings. */ public final static class UtilDateSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { public final static UtilDateSerializer instance = new UtilDateSerializer(); @Override @@ -541,6 +680,17 @@ { provider.defaultSerializeDateValue(value, jgen); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "string"); + //todo: (ryan) add a format for the date in the schema? + objectNode.put("optional", true); + return objectNode; + } } /** @@ -549,7 +699,7 @@ * that should not be used by plain SQL date. */ public final static class SqlDateSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { @Override public void serialize(java.sql.Date value, JsonGenerator jgen, SerializerProvider provider) @@ -557,10 +707,21 @@ { jgen.writeString(value.toString()); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "string"); + //todo: (ryan) add a format for the date in the schema? + objectNode.put("optional", true); + return objectNode; + } } public final static class SqlTimeSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { @Override public void serialize(java.sql.Time value, JsonGenerator jgen, SerializerProvider provider) @@ -568,6 +729,16 @@ { jgen.writeString(value.toString()); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "string"); + objectNode.put("optional", true); + return objectNode; + } } /** @@ -576,7 +747,7 @@ * This is the default serializer for nulls. */ public final static class NullSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { public final static NullSerializer instance = new NullSerializer(); @@ -588,10 +759,19 @@ { jgen.writeNull(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "null"); + return objectNode; + } } public final static class SerializableSerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { final static SerializableSerializer instance = new SerializableSerializer(); @@ -603,5 +783,45 @@ { value.serialize(jgen, provider); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + String schemaType = "any"; + String objectProperties = null; + String itemDefinition = null; + if (typeHint != null) { + Class rawClass = TypeFactory.fromType(typeHint).getRawClass(); + if (rawClass.isAnnotationPresent(JsonSerializableSchema.class)) { + JsonSerializableSchema schemaInfo = rawClass.getAnnotation(JsonSerializableSchema.class); + schemaType = schemaInfo.schemaType(); + if (!"##irrelevant".equals(schemaInfo.schemaObjectPropertiesDefinition())) { + objectProperties = schemaInfo.schemaObjectPropertiesDefinition(); + } + if (!"##irrelevant".equals(schemaInfo.schemaItemDefinition())) { + itemDefinition = schemaInfo.schemaItemDefinition(); + } + } + } + objectNode.put("type", schemaType); + if (objectProperties != null) { + try { + objectNode.put("properties", new ObjectMapper().readValue(objectProperties, JsonNode.class)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + if (itemDefinition != null) { + try { + objectNode.put("items", new ObjectMapper().readValue(itemDefinition, JsonNode.class)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + objectNode.put("optional", true); + return objectNode; + } } } Index: src/java/org/codehaus/jackson/map/ser/BeanPropertyWriter.java =================================================================== --- src/java/org/codehaus/jackson/map/ser/BeanPropertyWriter.java (revision 372) +++ src/java/org/codehaus/jackson/map/ser/BeanPropertyWriter.java (working copy) @@ -1,13 +1,13 @@ package org.codehaus.jackson.map.ser; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.map.JsonSerializer; import org.codehaus.jackson.map.SerializerProvider; -import org.codehaus.jackson.map.annotate.OutputProperties; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Type; + /** * Base bean property handler class, which implements common parts of * reflection-based functionality for accessing a property value @@ -76,6 +76,13 @@ */ public abstract Object get(Object bean) throws Exception; + /** + * Get the generic property type of this property writer. + * + * @return The property type, or null if not found. + */ + public abstract Type getGenericPropertyType(); + /* ////////////////////////////////////////////////////////////// // Intermediate classes @@ -116,6 +123,12 @@ public String toString() { return "property '"+getName()+"' (via method "+_accessorMethod.getDeclaringClass().getName()+"#"+_accessorMethod.getName()+"))"; } + + @Override + public Type getGenericPropertyType() + { + return _accessorMethod.getGenericReturnType(); + } } /** @@ -149,6 +162,12 @@ } @Override + public Type getGenericPropertyType() + { + return _field.getGenericType(); + } + + @Override public String toString() { return "property '"+getName()+"' (field "+_field.getDeclaringClass().getName()+"#"+_field.getName()+"))"; } Index: src/java/org/codehaus/jackson/map/ser/ArraySerializers.java =================================================================== --- src/java/org/codehaus/jackson/map/ser/ArraySerializers.java (revision 372) +++ src/java/org/codehaus/jackson/map/ser/ArraySerializers.java (working copy) @@ -2,9 +2,18 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Type; +import java.lang.reflect.GenericArrayType; import org.codehaus.jackson.*; +import org.codehaus.jackson.schema.SchemaAware; +import org.codehaus.jackson.schema.JsonSchema; +import org.codehaus.jackson.type.JavaType; +import org.codehaus.jackson.node.ObjectNode; +import org.codehaus.jackson.node.JsonNodeFactory; import org.codehaus.jackson.map.*; +import org.codehaus.jackson.map.type.TypeFactory; +import org.codehaus.jackson.map.type.ArrayType; /** * Dummy container class to group standard array serializer implementations. @@ -26,7 +35,7 @@ * Generic serializer for Object arrays (Object[]). */ public final static class ObjectArraySerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { public final static ObjectArraySerializer instance = new ObjectArraySerializer(); @@ -79,10 +88,31 @@ } jgen.writeEndArray(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "array"); + if (typeHint != null) { + JavaType javaType = TypeFactory.instance._fromType(typeHint); + if (javaType.isArrayType()) { + Class componentType = ((ArrayType) javaType).getComponentType().getRawClass(); + JsonSerializer ser = provider.findValueSerializer(componentType); + JsonNode schemaNode = (ser instanceof SchemaAware) ? + ((SchemaAware) ser).getSchema(provider, null) : + JsonSchema.getDefaultSchemaNode(); + o.put("items", schemaNode); + } + } + o.put("optional", true); + return o; + } } public final static class StringArraySerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { @Override public void serialize(String[] value, JsonGenerator jgen, SerializerProvider provider) @@ -111,10 +141,22 @@ } jgen.writeEndArray(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "array"); + ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); + itemSchema.put("type", "string"); + o.put("items", itemSchema); + o.put("optional", true); + return o; + } } public final static class BooleanArraySerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { @Override public void serialize(boolean[] value, JsonGenerator jgen, SerializerProvider provider) @@ -126,6 +168,18 @@ } jgen.writeEndArray(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "array"); + ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); + itemSchema.put("type", "boolean"); + o.put("items", itemSchema); + o.put("optional", true); + return o; + } } /** @@ -134,7 +188,7 @@ * as base64 encoded bytes (using default base64 encoding). */ public final static class ByteArraySerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { @Override public void serialize(byte[] value, JsonGenerator jgen, SerializerProvider provider) @@ -142,10 +196,22 @@ { jgen.writeBinary(value); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "array"); + ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); + itemSchema.put("type", "string"); //binary values written as strings? + o.put("items", itemSchema); + o.put("optional", true); + return o; + } } public final static class ShortArraySerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { @SuppressWarnings("cast") @Override @@ -158,6 +224,18 @@ } jgen.writeEndArray(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "array"); + ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); + itemSchema.put("type", "integer"); //no "short" type defined by json + o.put("items", itemSchema); + o.put("optional", true); + return o; + } } /** @@ -166,7 +244,7 @@ * Strings, not arrays of entries. */ public final static class CharArraySerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { @Override public void serialize(char[] value, JsonGenerator jgen, SerializerProvider provider) @@ -174,11 +252,23 @@ { jgen.writeString(value, 0, value.length); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "array"); + ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); + itemSchema.put("type", "string"); + o.put("items", itemSchema); + o.put("optional", true); + return o; + } } public final static class IntArraySerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { @Override public void serialize(int[] value, JsonGenerator jgen, SerializerProvider provider) @@ -190,10 +280,22 @@ } jgen.writeEndArray(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "array"); + ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); + itemSchema.put("type", "integer"); + o.put("items", itemSchema); + o.put("optional", true); + return o; + } } public final static class LongArraySerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { @Override public void serialize(long[] value, JsonGenerator jgen, SerializerProvider provider) @@ -205,10 +307,22 @@ } jgen.writeEndArray(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "array"); + ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); + itemSchema.put("type", "number"); + o.put("items", itemSchema); + o.put("optional", true); + return o; + } } public final static class FloatArraySerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { @Override public void serialize(float[] value, JsonGenerator jgen, SerializerProvider provider) @@ -220,10 +334,22 @@ } jgen.writeEndArray(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "array"); + ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); + itemSchema.put("type", "number"); + o.put("items", itemSchema); + o.put("optional", true); + return o; + } } public final static class DoubleArraySerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { @Override public void serialize(double[] value, JsonGenerator jgen, SerializerProvider provider) @@ -235,5 +361,17 @@ } jgen.writeEndArray(); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + ObjectNode o = JsonNodeFactory.instance.objectNode(); + o.put("type", "array"); + ObjectNode itemSchema = JsonNodeFactory.instance.objectNode(); + itemSchema.put("type", "number"); + o.put("items", itemSchema); + o.put("optional", true); + return o; + } } } Index: src/java/org/codehaus/jackson/map/ser/StdKeySerializer.java =================================================================== --- src/java/org/codehaus/jackson/map/ser/StdKeySerializer.java (revision 372) +++ src/java/org/codehaus/jackson/map/ser/StdKeySerializer.java (working copy) @@ -1,11 +1,17 @@ package org.codehaus.jackson.map.ser; import java.io.IOException; +import java.lang.reflect.Type; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.schema.SchemaAware; +import org.codehaus.jackson.node.ObjectNode; +import org.codehaus.jackson.node.JsonNodeFactory; import org.codehaus.jackson.map.JsonSerializer; import org.codehaus.jackson.map.SerializerProvider; +import org.codehaus.jackson.map.JsonMappingException; /** * Specialized serializer that can be used as the generic key @@ -13,7 +19,7 @@ * Objects. */ public final class StdKeySerializer - extends JsonSerializer + extends JsonSerializer implements SchemaAware { final static StdKeySerializer instace = new StdKeySerializer(); @@ -25,4 +31,13 @@ ((String) value) : value.toString(); jgen.writeFieldName(keyStr); } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "string"); + return objectNode; + } } Index: src/java/org/codehaus/jackson/map/ObjectMapper.java =================================================================== --- src/java/org/codehaus/jackson/map/ObjectMapper.java (revision 372) +++ src/java/org/codehaus/jackson/map/ObjectMapper.java (working copy) @@ -5,6 +5,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.codehaus.jackson.*; +import org.codehaus.jackson.schema.JsonSchema; import org.codehaus.jackson.map.deser.StdDeserializationContext; import org.codehaus.jackson.map.deser.StdDeserializerProvider; import org.codehaus.jackson.map.introspect.BasicClassIntrospector; @@ -13,6 +14,7 @@ import org.codehaus.jackson.map.ser.BeanSerializerFactory; import org.codehaus.jackson.map.type.TypeFactory; import org.codehaus.jackson.node.NullNode; +import org.codehaus.jackson.node.ObjectNode; import org.codehaus.jackson.type.JavaType; import org.codehaus.jackson.type.TypeReference; @@ -580,6 +582,18 @@ } /** + * Generate the {@link http://json-schema.org/ Json-schema} for the specified class. + * + * @param t The class. + * @return The json-schema. + */ + public JsonSchema generateJsonSchema(Class t) + throws JsonMappingException + { + return _serializerProvider.generateJsonSchema(t, _getUnsharedSConfig(), _serializerFactory); + } + + /** * Method called to configure the generator as necessary and then * call write functionality */ Index: src/java/org/codehaus/jackson/map/JsonSerializableSchema.java =================================================================== --- src/java/org/codehaus/jackson/map/JsonSerializableSchema.java (revision 0) +++ src/java/org/codehaus/jackson/map/JsonSerializableSchema.java (revision 0) @@ -0,0 +1,43 @@ +package org.codehaus.jackson.map; + +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Retention; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Interface that can be implemented by objects that know how to + * serialize themselves to Json, using {@link JsonGenerator} + * (and {@link SerializerProvider} if necessary). + *

+ * Note that implementing this interface binds implementing object + * closely to Jackson API, and that it is often not necessary to do + * so -- if class is a bean, it can be serialized without + * implementing this interface. * @author Ryan Heaton + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface JsonSerializableSchema { + + /** + * The schema type for this JsonSerializable instance. + * Possible values: "string", "number", "boolean", "object", "array", "null", "any" + * + * @return The schema type for this JsonSerializable instance. + */ + String schemaType() default "any"; + + /** + * If the schema type is "object", the node that defines the properties of the object. + * + * @return The node representing the schema properties, or "##irrelevant" if irrelevant. + */ + String schemaObjectPropertiesDefinition() default "##irrelevant"; + + /** + * If the schema type if "array", the node that defines the schema for the items in the array. + * + * @return The schema for the items in the array, or "##irrelevant" if irrelevant. + */ + String schemaItemDefinition() default "##irrelevant"; +} Property changes on: src/java/org/codehaus/jackson/map/JsonSerializableSchema.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Date Revision Added: svn:eol-style + native Index: src/java/org/codehaus/jackson/map/SerializerProvider.java =================================================================== --- src/java/org/codehaus/jackson/map/SerializerProvider.java (revision 372) +++ src/java/org/codehaus/jackson/map/SerializerProvider.java (working copy) @@ -4,6 +4,7 @@ import java.util.Date; import org.codehaus.jackson.*; +import org.codehaus.jackson.schema.JsonSchema; /** * Abstract class that defines API used by {@link ObjectMapper} and @@ -44,6 +45,19 @@ throws IOException, JsonGenerationException; /** + * Generate the {@link http://json-schema.org/ json-schema}. + * + * @param type The type. + * @param config The config. + * @param jsf The serializer factory. + * @return The config. + */ + public JsonSchema generateJsonSchema(Class type, SerializationConfig config, SerializerFactory jsf) + throws JsonMappingException { + throw new UnsupportedOperationException(); + } + + /** * Method that can be called to see if this serializer provider * can find a serializer for an instance of given class. *