blob: 569566c386f16986abe6badd699791170330e88e [file] [log] [blame]
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<Float> 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<Float> getProperty4()
-+ {
-+ return property4;
-+ }
-+
-+ public void setProperty4(Collection<Float> 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<String, JsonNode> _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<Object> 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<Object> 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<Object> 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<Object> 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<Object> 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<Object> 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<? extends Enum> enumClass = (Class<? extends Enum>) enumType.getRawClass();
-+ ObjectNode propsNode = JsonNodeFactory.instance.objectNode();
-+ for (Object enumValue : EnumSet.allOf(enumClass)) {
-+ JsonSerializer<Object> 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<Object> 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<? extends Enum> enumSet = EnumSet.allOf((Class<? extends Enum>) 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<Object> 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).
-- *<p>
-- * 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.
- *<p>
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<Float> 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<Float> getProperty4()
+ {
+ return property4;
+ }
+
+ public void setProperty4(Collection<Float> 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<String, JsonNode> _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<Object,Object> 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<Object> 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<Object> 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<List<?>>
+ extends JsonSerializer<List<?>> 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<Object> 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<Collection<?>>
+ extends JsonSerializer<Collection<?>> 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<Object> 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<Iterator<?>>
+ extends JsonSerializer<Iterator<?>> 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<Object> 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<Iterable<?>>
+ extends JsonSerializer<Iterable<?>> 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<Object> 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<EnumSet<? extends Enum<?>>>
+ extends JsonSerializer<EnumSet<? extends Enum<?>>> 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<Object> 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<Map<?,?>>
+ extends JsonSerializer<Map<?,?>> 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<EnumMap<? extends Enum<?>, ?>>
+ extends JsonSerializer<EnumMap<? extends Enum<?>, ?>> implements SchemaAware
{
@Override
public void serialize(EnumMap<? extends Enum<?>,?> 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<? extends Enum> enumClass = (Class<? extends Enum>) enumType.getRawClass();
+ ObjectNode propsNode = JsonNodeFactory.instance.objectNode();
+ for (Object enumValue : EnumSet.allOf(enumClass)) {
+ JsonSerializer<Object> 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<Object>
- 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<Object> 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.
- *<p>
+ * <p/>
* 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<Object>
- implements ResolvableSerializer
+ extends JsonSerializer<Object>
+ 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<Object> 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<Object> 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<Object>
+ extends JsonSerializer<Object> 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<Boolean>
+ extends JsonSerializer<Boolean> 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<String>
+ extends JsonSerializer<String> 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<T>
- extends JsonSerializer<T>
+ extends JsonSerializer<T> implements SchemaAware
{
public final static StringLikeSerializer<Object> instance = new StringLikeSerializer<Object>();
@@ -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<Class<?>>
+ extends JsonSerializer<Class<?>> 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<Integer>
+ extends JsonSerializer<Integer> 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<Number>
+ extends JsonSerializer<Number> 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<Long>
+ extends JsonSerializer<Long> 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<Float>
+ extends JsonSerializer<Float> 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<Double>
+ extends JsonSerializer<Double> 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<Number>
+ extends JsonSerializer<Number> 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<Enum<?>>
+ 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<? extends Enum> enumSet = EnumSet.allOf((Class<? extends Enum>) 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<Calendar>
+ extends JsonSerializer<Calendar> 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<java.util.Date>
+ extends JsonSerializer<java.util.Date> 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<java.sql.Date>
+ extends JsonSerializer<java.sql.Date> 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<java.sql.Time>
+ extends JsonSerializer<java.sql.Time> 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<Object>
+ extends JsonSerializer<Object> 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<JsonSerializable>
+ extends JsonSerializer<JsonSerializable> 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 (<code>Object[]</code>).
*/
public final static class ObjectArraySerializer
- extends JsonSerializer<Object[]>
+ extends JsonSerializer<Object[]> 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<Object> 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<String[]>
+ extends JsonSerializer<String[]> 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<boolean[]>
+ extends JsonSerializer<boolean[]> 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<byte[]>
+ extends JsonSerializer<byte[]> 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<short[]>
+ extends JsonSerializer<short[]> 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<char[]>
+ extends JsonSerializer<char[]> 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<int[]>
+ extends JsonSerializer<int[]> 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<long[]>
+ extends JsonSerializer<long[]> 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<float[]>
+ extends JsonSerializer<float[]> 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<double[]>
+ extends JsonSerializer<double[]> 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<Object>
+ extends JsonSerializer<Object> 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).
+ *<p>
+ * 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.
*<p>