diff --git a/pom.xml b/pom.xml index 47f047f892..ed111343cf 100644 --- a/pom.xml +++ b/pom.xml @@ -225,6 +225,9 @@ com.fasterxml.jackson.databind.exc, com.fasterxml.jackson.databind.ext, com.fasterxml.jackson.databind.introspect, com.fasterxml.jackson.databind.jsonschema, +com.fasterxml.jackson.databind.jsonschema.factories, +com.fasterxml.jackson.databind.jsonschema.types, +com.fasterxml.jackson.databind.jsonFormatVisitors, com.fasterxml.jackson.databind.jsontype, com.fasterxml.jackson.databind.jsontype.impl, com.fasterxml.jackson.databind.module, diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index 87fe70ca7a..de686d47f9 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -19,8 +19,12 @@ import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; import com.fasterxml.jackson.databind.cfg.MapperConfig; import com.fasterxml.jackson.databind.deser.*; +import com.fasterxml.jackson.databind.introspect.BasicClassIntrospector; +import com.fasterxml.jackson.databind.introspect.ClassIntrospector; +import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; +import com.fasterxml.jackson.databind.introspect.VisibilityChecker; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.introspect.*; -import com.fasterxml.jackson.databind.jsonschema.JsonSchema; import com.fasterxml.jackson.databind.jsontype.*; import com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver; import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; @@ -2321,7 +2325,7 @@ public ObjectReader reader(JsonNodeFactory f) * pass specific schema object to {@link JsonParser} used for * reading content. * - * @param schema Schema to pass to parser + * @param schema JsonSchema to pass to parser */ public ObjectReader reader(FormatSchema schema) { return new ObjectReader(this, getDeserializationConfig(), null, null, @@ -2454,7 +2458,7 @@ protected Object _convert(Object fromValue, JavaType toValueType) /* /********************************************************** - /* Extended Public API: JSON Schema generation + /* Extended Public API: JSON JsonSchema generation /********************************************************** */ @@ -2463,10 +2467,18 @@ protected Object _convert(Object fromValue, JavaType toValueType) * instance for specified class. * * @param t The class to generate schema for - * @return Constructed JSON schema. */ - public JsonSchema generateJsonSchema(Class t) throws JsonMappingException { - return _serializerProvider(getSerializationConfig()).generateJsonSchema(t); + public void acceptJsonFormatVisitor(Class t, JsonFormatVisitorWrapper visitor) throws JsonMappingException { + if (t == null) { + throw new IllegalArgumentException("class must be provided"); + } + + if (visitor == null) { + return; + } + DefaultSerializerProvider provider = _serializerProvider(getSerializationConfig()); + visitor.setProvider(provider); + provider.acceptJsonFormatVisitor(t, visitor); } /* diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java b/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java index fd967eccc8..04f881e2f1 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java @@ -1,7 +1,6 @@ package com.fasterxml.jackson.databind.ext; import java.io.IOException; -import java.lang.reflect.Type; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -12,10 +11,12 @@ import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.ser.std.CalendarSerializer; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; @@ -62,9 +63,8 @@ public void serialize(XMLGregorianCalendar value, JsonGenerator jgen, Serializer CalendarSerializer.instance.serialize(value.toGregorianCalendar(), jgen, provider); } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { - return CalendarSerializer.instance.getSchema(provider, typeHint); + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { + CalendarSerializer.instance.acceptJsonFormatVisitor(visitor, null); } } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/DOMSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/DOMSerializer.java index 2c16f6d67e..7824b334ac 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ext/DOMSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ext/DOMSerializer.java @@ -1,6 +1,7 @@ package com.fasterxml.jackson.databind.ext; import java.io.IOException; + import org.w3c.dom.Node; import org.w3c.dom.bootstrap.DOMImplementationRegistry; import org.w3c.dom.ls.DOMImplementationLS; @@ -8,8 +9,9 @@ import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.ser.std.StdSerializer; public class DOMSerializer @@ -39,9 +41,8 @@ public void serialize(Node value, JsonGenerator jgen, SerializerProvider provide } @Override - public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - // Well... it is serialized as String - return createSchemaNode("string", true); + visitor.expectAnyFormat(typeHint); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonAnyFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonAnyFormatVisitor.java new file mode 100644 index 0000000000..612b2d39cf --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonAnyFormatVisitor.java @@ -0,0 +1,5 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +public interface JsonAnyFormatVisitor { + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonArrayFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonArrayFormatVisitor.java new file mode 100644 index 0000000000..02e42268a4 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonArrayFormatVisitor.java @@ -0,0 +1,12 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.jsonschema.types.SchemaType; + +public interface JsonArrayFormatVisitor extends JsonFormatVisitorWithSerializerProvider { + + void itemsFormat(JavaType contentType); + + void itemsFormat(SchemaType format); + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonBooleanFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonBooleanFormatVisitor.java new file mode 100644 index 0000000000..5002eb13db --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonBooleanFormatVisitor.java @@ -0,0 +1,5 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +public interface JsonBooleanFormatVisitor extends JsonValueFormatVisitor { + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorAware.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorAware.java new file mode 100644 index 0000000000..7397651842 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorAware.java @@ -0,0 +1,18 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Marker interface for schema-aware serializers. + */ +public interface JsonFormatVisitorAware +{ + /** + * Get the representation of the schema to which this serializer will conform. + * @param typeHint TODO + * + * @return Json-schema for this serializer. + */ + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint); +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWithSerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWithSerializerProvider.java new file mode 100644 index 0000000000..0e495e9182 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWithSerializerProvider.java @@ -0,0 +1,16 @@ +/** + * + */ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +import com.fasterxml.jackson.databind.SerializerProvider; + +/** + * @author jphelan + * + */ +public interface JsonFormatVisitorWithSerializerProvider { + + public SerializerProvider getProvider(); + public abstract void setProvider(SerializerProvider provider); +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWrapper.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWrapper.java new file mode 100644 index 0000000000..4e37d1df59 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWrapper.java @@ -0,0 +1,16 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +import com.fasterxml.jackson.databind.JavaType; + +public interface JsonFormatVisitorWrapper extends JsonFormatVisitorWithSerializerProvider{ + + public JsonObjectFormatVisitor expectObjectFormat(JavaType convertedType); + public JsonArrayFormatVisitor expectArrayFormat(JavaType convertedType); + public JsonStringFormatVisitor expectStringFormat(JavaType convertedType); + public JsonNumberFormatVisitor expectNumberFormat(JavaType convertedType); + public JsonIntegerFormatVisitor expectIntegerFormat(JavaType convertedType); + public JsonBooleanFormatVisitor expectBooleanFormat(JavaType convertedType); + public JsonNullFormatVisitor expectNullFormat(JavaType convertedType); + public JsonAnyFormatVisitor expectAnyFormat(JavaType convertedType); + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonIntegerFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonIntegerFormatVisitor.java new file mode 100644 index 0000000000..cc5781e830 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonIntegerFormatVisitor.java @@ -0,0 +1,5 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +public interface JsonIntegerFormatVisitor extends JsonValueFormatVisitor { + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNullFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNullFormatVisitor.java new file mode 100644 index 0000000000..8df98d811f --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNullFormatVisitor.java @@ -0,0 +1,5 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +public interface JsonNullFormatVisitor { + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNumberFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNumberFormatVisitor.java new file mode 100644 index 0000000000..77f25c898e --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNumberFormatVisitor.java @@ -0,0 +1,6 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + + +public interface JsonNumberFormatVisitor extends JsonValueFormatVisitor{ + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonObjectFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonObjectFormatVisitor.java new file mode 100644 index 0000000000..54b295f0a6 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonObjectFormatVisitor.java @@ -0,0 +1,22 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; + +public interface JsonObjectFormatVisitor extends JsonFormatVisitorWithSerializerProvider { + + public void property(BeanPropertyWriter writer); + + public void optionalProperty(BeanPropertyWriter writer); + + public void property(String name, JsonFormatVisitorAware handler, JavaType propertyTypeHint); + + public void optionalProperty(String name, JsonFormatVisitorAware handler, + JavaType propertyTypeHint); + + public void property(String name); + + public void optionalProperty(String name); + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonStringFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonStringFormatVisitor.java new file mode 100644 index 0000000000..3fea32d52b --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonStringFormatVisitor.java @@ -0,0 +1,5 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +public interface JsonStringFormatVisitor extends JsonValueFormatVisitor { + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonValueFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonValueFormatVisitor.java new file mode 100644 index 0000000000..069f613507 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonValueFormatVisitor.java @@ -0,0 +1,12 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +import java.util.Set; + +import com.fasterxml.jackson.databind.jsonschema.types.JsonValueFormat; + +public interface JsonValueFormatVisitor { + + void format(JsonValueFormat format); + + void enumTypes(Set enums); +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSchema.java deleted file mode 100644 index e79a68a818..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSchema.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.fasterxml.jackson.databind.jsonschema; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; - -/** - * Container for a logical JSON Schema instance. - * Internally schema data is stored as a JSON Tree - * (instance of {@link JsonNode} is the root - * of schema document) - * - * @author Ryan Heaton - * @see JSON Schema - */ -public class JsonSchema -{ - private final ObjectNode schema; - - /** - * Main constructor for schema instances. - *

- * This is the creator constructor used by Jackson itself when - * deserializing instances. It is so-called delegating creator, - * meaning that its argument will be bound by Jackson before - * constructor gets called. - */ - @JsonCreator - public JsonSchema(ObjectNode schema) - { - this.schema = schema; - } - - /** - * Method for accessing root JSON object of the contained schema. - *

- * Note: this method is specified with {@link JsonValue} annotation - * to represent serialization to use; same as if explicitly - * serializing returned object. - * - * @return Root node of the schema tree - */ - @JsonValue - public ObjectNode getSchemaNode() - { - return schema; - } - - @Override - public String toString() - { - return this.schema.toString(); - } - - @Override - public boolean equals(Object o) - { - if (o == this) return true; - if (o == null) return false; - if (!(o instanceof JsonSchema)) return false; - - JsonSchema other = (JsonSchema) o; - if (schema == null) { - return other.schema == null; - } - return schema.equals(other.schema); - } - - /** - * Get the default schema node. - * - * @return The default schema node. - */ - public static JsonNode getDefaultSchemaNode() - { - ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); - objectNode.put("type", "any"); - // "required" is false by default, no need to include - //objectNode.put("required", false); - return objectNode; - } - -} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java deleted file mode 100644 index a2935693b5..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.fasterxml.jackson.databind.jsonschema; - -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Retention; -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; - -import com.fasterxml.jackson.annotation.JacksonAnnotation; - -/** - * Annotation that can be used to define JSON Schema definition for - * the annotated class. - *

- * Note that annotation is often not needed: for example, regular - * Jackson beans that Jackson can introspect can be used without - * annotations, to produce JSON schema definition. - * - * @author Ryan Heaton - * @author Tatu Saloranta - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@JacksonAnnotation -public @interface JsonSerializableSchema -{ - /** - * Marker value used to indicate that property has "no value"; - * needed because annotations can not have null as default - * value. - */ - public final static String NO_VALUE = "##irrelevant"; - - /** - * Property that can be used to indicate id of the type when - * generating JSON Schema; empty String indicates that no id - * is defined. - */ - public String id() default ""; - - /** - * The schema type for this JsonSerializable instance. - * Possible values: "string", "number", "boolean", "object", "array", "null", "any" - * - * @return The schema type for this JsonSerializable instance. - */ - public String schemaType() default "any"; - - /** - * If the schema type is "object", JSON definition of properties of the object as - * a String. - * - * @return The node representing the schema properties, or "##irrelevant" if irrelevant. - */ - public String schemaObjectPropertiesDefinition() default NO_VALUE; - - /** - * If the schema type if "array", JSON definition of the schema for item types contained. - * - * @return The schema for the items in the array, or "##irrelevant" if irrelevant. - */ - public String schemaItemDefinition() default NO_VALUE; -} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/SchemaAware.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/SchemaAware.java deleted file mode 100644 index f3505cd37d..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/jsonschema/SchemaAware.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.fasterxml.jackson.databind.jsonschema; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.SerializerProvider; - -import java.lang.reflect.Type; - -/** - * Marker interface for schema-aware serializers. - */ -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 Json-schema for this serializer. - */ - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException; - - /** - * Get the representation of the schema to which this serializer will conform. - * - * @param provider The serializer provider. - * @param isOptional Is the type optional - * @param typeHint A hint about the type. - * @return Json-schema for this serializer. - */ - public JsonNode getSchema(SerializerProvider provider, Type typeHint, boolean isOptional) - throws JsonMappingException; -} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/AnySchemaFactory.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/AnySchemaFactory.java new file mode 100644 index 0000000000..36143fa453 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/AnySchemaFactory.java @@ -0,0 +1,34 @@ +package com.fasterxml.jackson.databind.jsonschema.factories; + +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonAnyFormatVisitor; +import com.fasterxml.jackson.databind.jsonschema.types.AnySchema; +import com.fasterxml.jackson.databind.jsonschema.types.JsonSchema; + +public class AnySchemaFactory extends SchemaFactory implements + JsonAnyFormatVisitor { + + protected SchemaFactory parent; + protected AnySchema anySchema; + + public AnySchemaFactory(SchemaFactory parent) { + this.parent = parent; + setProvider(parent.getProvider()); + anySchema = new AnySchema(); + } + + /** + * @param provider + */ + public AnySchemaFactory(SerializerProvider provider) { + parent = null; + setProvider(provider); + anySchema = new AnySchema(); + } + + public JsonSchema getSchema() { + return anySchema; + } + + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/ArraySchemaFactory.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/ArraySchemaFactory.java new file mode 100644 index 0000000000..8303d1e144 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/ArraySchemaFactory.java @@ -0,0 +1,68 @@ +package com.fasterxml.jackson.databind.jsonschema.factories; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorAware; +import com.fasterxml.jackson.databind.jsonschema.types.ArraySchema; +import com.fasterxml.jackson.databind.jsonschema.types.JsonSchema; +import com.fasterxml.jackson.databind.jsonschema.types.SchemaType; + +public class ArraySchemaFactory extends SchemaFactory + implements JsonArrayFormatVisitor { + + protected SchemaFactory parent; + protected ArraySchema arraySchema; + protected BeanProperty _property; + + public ArraySchemaFactory(SchemaFactory parent, BeanProperty property) { + this.parent = parent; + setProvider(parent.getProvider()); + arraySchema = new ArraySchema(); + } + + public ArraySchemaFactory(SchemaFactory schemaFactory) { + this(schemaFactory, null); + } + + /** + * @param provider + */ + public ArraySchemaFactory(SerializerProvider provider) { + parent = null; + setProvider(provider); + arraySchema = new ArraySchema(); + } + + public void itemsFormat(JavaType contentType) { + // An array of object matches any values, thus we leave the schema empty. + if (contentType.getRawClass() != Object.class) { + + JsonSerializer ser; + try { + ser = getProvider().findValueSerializer(contentType, _property); + if (ser instanceof JsonFormatVisitorAware) { + SchemaFactoryProvider visitor = new SchemaFactoryProvider(); + visitor.setProvider(provider); + ((JsonFormatVisitorAware) ser).acceptJsonFormatVisitor(visitor, contentType); + arraySchema.setItemsSchema(visitor.finalSchema()); + } + } catch (JsonMappingException e) { + //TODO: log error + } + } + } + + public void itemsFormat(SchemaType format) { + arraySchema.setItemsSchema(JsonSchema.minimalForFormat(format)); + } + + public JsonSchema getSchema() { + return arraySchema; + } + + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/BooleanSchemaFactory.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/BooleanSchemaFactory.java new file mode 100644 index 0000000000..b084a3699d --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/BooleanSchemaFactory.java @@ -0,0 +1,30 @@ +package com.fasterxml.jackson.databind.jsonschema.factories; + +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonBooleanFormatVisitor; +import com.fasterxml.jackson.databind.jsonschema.types.BooleanSchema; +import com.fasterxml.jackson.databind.jsonschema.types.ValueTypeSchema; + +public class BooleanSchemaFactory extends ValueTypeSchemaFactory implements + JsonBooleanFormatVisitor { + + protected BooleanSchema booleanSchema; + + public BooleanSchemaFactory(SchemaFactory parent) { + super(parent); + booleanSchema = new BooleanSchema(); + } + + /** + * @param provider + */ + public BooleanSchemaFactory(SerializerProvider provider) { + super(provider); + booleanSchema = new BooleanSchema(); + } + + public ValueTypeSchema getValueSchema() { + return booleanSchema; + } + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/IntegerSchemaFactory.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/IntegerSchemaFactory.java new file mode 100644 index 0000000000..93d308b3d1 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/IntegerSchemaFactory.java @@ -0,0 +1,30 @@ +package com.fasterxml.jackson.databind.jsonschema.factories; + +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor; +import com.fasterxml.jackson.databind.jsonschema.types.IntegerSchema; +import com.fasterxml.jackson.databind.jsonschema.types.ValueTypeSchema; + +public class IntegerSchemaFactory extends ValueTypeSchemaFactory implements + JsonIntegerFormatVisitor { + + protected IntegerSchema integerSchema; + + public IntegerSchemaFactory(SchemaFactory parent) { + super(parent); + integerSchema = new IntegerSchema(); + } + + /** + * @param provider + */ + public IntegerSchemaFactory(SerializerProvider provider) { + super(provider); + integerSchema = new IntegerSchema(); + } + + public ValueTypeSchema getValueSchema() { + return integerSchema; + } + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/NullSchemaFactory.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/NullSchemaFactory.java new file mode 100644 index 0000000000..8963fd11d4 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/NullSchemaFactory.java @@ -0,0 +1,33 @@ +package com.fasterxml.jackson.databind.jsonschema.factories; + +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonNullFormatVisitor; +import com.fasterxml.jackson.databind.jsonschema.types.JsonSchema; +import com.fasterxml.jackson.databind.jsonschema.types.NullSchema; + +public class NullSchemaFactory extends SchemaFactory implements + JsonNullFormatVisitor { + + protected SchemaFactory parent; + protected NullSchema nullSchema; + + public NullSchemaFactory(SchemaFactory parent) { + this.parent = parent; + setProvider(parent.getProvider()); + nullSchema = new NullSchema(); + } + + /** + * @param provider + */ + public NullSchemaFactory(SerializerProvider provider) { + parent = null; + setProvider(provider); + nullSchema = new NullSchema(); + } + + public JsonSchema getSchema() { + return nullSchema; + } + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/NumberSchemaFactory.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/NumberSchemaFactory.java new file mode 100644 index 0000000000..a0cabeb5eb --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/NumberSchemaFactory.java @@ -0,0 +1,33 @@ +package com.fasterxml.jackson.databind.jsonschema.factories; + +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonNumberFormatVisitor; +import com.fasterxml.jackson.databind.jsonschema.types.NumberSchema; +import com.fasterxml.jackson.databind.jsonschema.types.ValueTypeSchema; + +public class NumberSchemaFactory extends ValueTypeSchemaFactory implements + JsonNumberFormatVisitor { + + protected NumberSchema numberSchema; + + public NumberSchemaFactory(SchemaFactory parent) { + super(parent); + this.parent = parent; + numberSchema = new NumberSchema(); + } + + /** + * @param provider + */ + public NumberSchemaFactory(SerializerProvider provider) { + super(provider); + numberSchema = new NumberSchema(); + } + + @Override + protected ValueTypeSchema getValueSchema() { + return numberSchema; + } + + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/ObjectSchemaFactory.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/ObjectSchemaFactory.java new file mode 100644 index 0000000000..0ada476b38 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/ObjectSchemaFactory.java @@ -0,0 +1,96 @@ +package com.fasterxml.jackson.databind.jsonschema.factories; + + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorAware; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.jsonschema.types.JsonSchema; +import com.fasterxml.jackson.databind.jsonschema.types.ObjectSchema; +import com.fasterxml.jackson.databind.jsonschema.types.SchemaType; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; + +public class ObjectSchemaFactory extends SchemaFactory + implements JsonObjectFormatVisitor { + + protected SchemaFactory parent; + protected ObjectSchema objectSchema; + + public ObjectSchemaFactory(SchemaFactory parent) { + this.parent = parent; + setProvider(parent.getProvider()); + objectSchema = new ObjectSchema(); + } + + /** + * @param provider + */ + public ObjectSchemaFactory(SerializerProvider provider) { + parent = null; + setProvider(provider); + objectSchema = new ObjectSchema(); + } + + public JsonSchema getSchema() { + return objectSchema; + } + + private JsonSerializer getSer(BeanPropertyWriter writer) { + JsonSerializer ser = writer.getSerializer(); + if (ser == null) { + Class serType = writer.getPropertyType(); + try { + return getProvider().findValueSerializer(serType, writer); + } catch (JsonMappingException e) { + // TODO: log error + } + } + return ser; + } + + protected JsonSchema propertySchema(BeanPropertyWriter writer) { + SchemaFactoryProvider visitor = new SchemaFactoryProvider(); + visitor.setProvider(provider); + JsonSerializer ser = getSer(writer); + if (ser != null && ser instanceof JsonFormatVisitorAware) { + ((JsonFormatVisitorAware)ser).acceptJsonFormatVisitor(visitor, writer.getType()); + } else { + visitor.expectAnyFormat(writer.getType()); + } + return visitor.finalSchema(); + } + + protected JsonSchema propertySchema(JsonFormatVisitorAware handler, JavaType propertyTypeHint) { + SchemaFactoryProvider visitor = new SchemaFactoryProvider(); + visitor.setProvider(provider); + handler.acceptJsonFormatVisitor(visitor, propertyTypeHint); + return visitor.finalSchema(); + } + + public void property(BeanPropertyWriter writer) { + objectSchema.putProperty(writer.getName(), propertySchema(writer)); + } + + public void optionalProperty(BeanPropertyWriter writer) { + objectSchema.putOptionalProperty(writer.getName(), propertySchema(writer)); + } + + public void property(String name, JsonFormatVisitorAware handler, JavaType propertyTypeHint) { + objectSchema.putProperty(name, propertySchema(handler, propertyTypeHint)); + } + + public void optionalProperty(String name, JsonFormatVisitorAware handler, JavaType propertyTypeHint) { + objectSchema.putOptionalProperty(name, propertySchema(handler, propertyTypeHint)); + } + + public void property(String name) { + objectSchema.putProperty(name, JsonSchema.minimalForFormat(SchemaType.ANY)); + } + + public void optionalProperty(String name) { + objectSchema.putOptionalProperty(name, JsonSchema.minimalForFormat(SchemaType.ANY)); + } + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/SchemaFactory.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/SchemaFactory.java new file mode 100644 index 0000000000..e7b6e3af62 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/SchemaFactory.java @@ -0,0 +1,26 @@ +package com.fasterxml.jackson.databind.jsonschema.factories; + +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonschema.types.JsonSchema; + +public abstract class SchemaFactory { + + + protected SerializerProvider provider; + + + /** + * {@link SchemaFactory#provider} + * @param provider the provider to set + */ + public void setProvider(SerializerProvider provider) { + this.provider = provider; + } + + public SerializerProvider getProvider() { + return provider; + } + + public abstract JsonSchema getSchema(); + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/SchemaFactoryProvider.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/SchemaFactoryProvider.java new file mode 100644 index 0000000000..d1b5e184e4 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/SchemaFactoryProvider.java @@ -0,0 +1,89 @@ +package com.fasterxml.jackson.databind.jsonschema.factories; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonAnyFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonBooleanFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonNullFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonNumberFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor; +import com.fasterxml.jackson.databind.jsonschema.factories.*; +import com.fasterxml.jackson.databind.jsonschema.types.JsonSchema; + +/** + * @author jphelan + * + */ +public class SchemaFactoryProvider implements JsonFormatVisitorWrapper{ + + protected SerializerProvider provider; + private SchemaFactory delegate; + + /** + * {@link SchemaFactory#provider} + * @param provider the provider to set + */ + public void setProvider(SerializerProvider provider) { + this.provider = provider; + } + + public SerializerProvider getProvider() { + return provider; + } + + + public JsonAnyFormatVisitor expectAnyFormat(JavaType convertedType) { + delegate = new AnySchemaFactory(provider); + return (JsonAnyFormatVisitor) delegate; + } + + public JsonArrayFormatVisitor expectArrayFormat(JavaType convertedType) { + delegate = new ArraySchemaFactory(provider); + return (JsonArrayFormatVisitor) delegate; + } + + public JsonBooleanFormatVisitor expectBooleanFormat(JavaType convertedType) { + delegate = new BooleanSchemaFactory(provider); + return (JsonBooleanFormatVisitor) delegate; + } + + public JsonSchema finalSchema() { + assert delegate != null : "SchemaFactory must envoke a delegate method before it can return a JsonSchema."; + if (delegate == null) { + return null; + } else { + return delegate.getSchema(); + } + + } + + public JsonIntegerFormatVisitor expectIntegerFormat(JavaType convertedType) { + delegate = new IntegerSchemaFactory(provider); + return (JsonIntegerFormatVisitor) delegate; + } + + public JsonNullFormatVisitor expectNullFormat(JavaType convertedType) { + delegate = new NullSchemaFactory(provider); + return (JsonNullFormatVisitor) delegate; + } + + public JsonNumberFormatVisitor expectNumberFormat(JavaType convertedType) { + delegate = new NumberSchemaFactory(provider); + return (JsonNumberFormatVisitor) delegate; + } + + public JsonObjectFormatVisitor expectObjectFormat(JavaType convertedType) { + delegate = new ObjectSchemaFactory(provider); + return (JsonObjectFormatVisitor) delegate; + } + + public JsonStringFormatVisitor expectStringFormat(JavaType convertedType) { + delegate = new StringSchemaFactory(provider); + return (JsonStringFormatVisitor) delegate; + } + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/StringSchemaFactory.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/StringSchemaFactory.java new file mode 100644 index 0000000000..c1bca67e60 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/StringSchemaFactory.java @@ -0,0 +1,30 @@ +package com.fasterxml.jackson.databind.jsonschema.factories; + +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor; +import com.fasterxml.jackson.databind.jsonschema.types.StringSchema; +import com.fasterxml.jackson.databind.jsonschema.types.ValueTypeSchema; + +public class StringSchemaFactory extends ValueTypeSchemaFactory + implements JsonStringFormatVisitor{ + + protected StringSchema stringSchema; + + public StringSchemaFactory(SchemaFactory parent) { + super(parent); + stringSchema = new StringSchema(); + } + + /** + * @param provider + */ + public StringSchemaFactory(SerializerProvider provider) { + super(provider); + stringSchema = new StringSchema(); + } + + public ValueTypeSchema getValueSchema() { + return stringSchema; + } + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/ValueTypeSchemaFactory.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/ValueTypeSchemaFactory.java new file mode 100644 index 0000000000..9a052704d1 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/factories/ValueTypeSchemaFactory.java @@ -0,0 +1,44 @@ +package com.fasterxml.jackson.databind.jsonschema.factories; + +import java.util.Set; + +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonValueFormatVisitor; +import com.fasterxml.jackson.databind.jsonschema.types.JsonSchema; +import com.fasterxml.jackson.databind.jsonschema.types.JsonValueFormat; +import com.fasterxml.jackson.databind.jsonschema.types.ValueTypeSchema; + +public abstract class ValueTypeSchemaFactory extends SchemaFactory implements + JsonValueFormatVisitor { + + protected SchemaFactory parent; + + protected ValueTypeSchemaFactory(SchemaFactory parent) { + this.parent = parent; + setProvider(parent.getProvider()); + } + + /** + * @param provider + */ + public ValueTypeSchemaFactory(SerializerProvider provider) { + parent = null; + setProvider(provider); + } + + public JsonSchema getSchema() { + return getValueSchema(); + } + protected abstract ValueTypeSchema getValueSchema(); + + public void format(JsonValueFormat format) { + getValueSchema().setFormat(format); + + } + + public void enumTypes(Set enums) { + getValueSchema().setEnums(enums); + + } + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/package-info.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/package-info.java deleted file mode 100644 index 443dcf48f9..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/jsonschema/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Classes needed for JSON schema support (currently just ability - * to generate schemas using serialization part of data mapping) - */ -package com.fasterxml.jackson.databind.jsonschema; diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/AnySchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/AnySchema.java new file mode 100644 index 0000000000..7f197a8c4e --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/AnySchema.java @@ -0,0 +1,73 @@ +package com.fasterxml.jackson.databind.jsonschema.types; + +import java.util.HashSet; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * This class represents a {@link JsonSchema} of type any + * @author jphelan + * + */ +public class AnySchema extends SimpleTypeSchema { + + /** + * This provides an enumeration of all possible values that are valid + for the instance property. This MUST be an array, and each item in + the array represents a possible value for the instance value. If + this attribute is defined, the instance value MUST be one of the + values in the array in order for the schema to be valid. Comparison + of enum values uses the same algorithm as defined in "uniqueItems" + (Section 5.15). + */ + @JsonProperty + private Set enums; + + @JsonIgnore + private final SchemaType type = SchemaType.ANY; + + //instance initializer block + { + enums = new HashSet(); + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#asAnySchema() + */ + @Override + public AnySchema asAnySchema() { return this; } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof AnySchema) { + AnySchema that = ((AnySchema)obj); + return super.equals(obj) && + enums == null ? that.enums == null : enums.equals(that.enums); + } else { + return false; + } + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#isAnySchema() + */ + @Override + public boolean isAnySchema() { return true; } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#getType() + */ + @Override + public SchemaType getType() { + return type; + } + + public void setEnums(Set enums) { + this.enums = enums; + } +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/ArraySchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/ArraySchema.java new file mode 100644 index 0000000000..f0cfeba286 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/ArraySchema.java @@ -0,0 +1,384 @@ +package com.fasterxml.jackson.databind.jsonschema.types; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; + +/* + * This attribute defines the allowed items in an instance array, and + MUST be a jsonSchema or an array of jsonSchemas. The default value is an + empty jsonSchema which allows any value for items in the instance array. + */ +public class ArraySchema extends ContainerTypeSchema { + + + /** + * see {@link AdditionalItems} + */ + @JsonProperty + private ArraySchema.AdditionalItems additionalItems; + + /** + * see {@link Items} + */ + @JsonProperty + private ArraySchema.Items items; + + /**This attribute defines the maximum number of values in an array*/ + @JsonProperty + private Integer maxItems; + + /**This attribute defines the minimum number of values in an array*/ + @JsonProperty + private Integer minItems; + + @JsonIgnore + private final SchemaType type = SchemaType.ARRAY; + + /** + * This attribute indicates that all items in an array instance MUST be + unique (contains no two identical values). + + Two instance are consider equal if they are both of the same type + and: + + are null; or are booleans/numbers/strings and have the same value; or + + are arrays, contains the same number of items, and each item in + the array is equal to the corresponding item in the other array; + or + + are objects, contains the same property names, and each property + in the object is equal to the corresponding property in the other + object. + */ + @JsonProperty + private Boolean uniqueItems = null; + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#asArraySchema() + */ + @Override + public ArraySchema asArraySchema() { return this; } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof ArraySchema) { + ArraySchema that = (ArraySchema) obj; + return getAdditionalItems() == null ? that.getAdditionalItems() == null : + getAdditionalItems().equals(that.getAdditionalItems()) && + getItems() == null ? that.getItems() == null : + getItems().equals(that.getItems()) && + getMaxItems() == null ? that.getMaxItems() == null : + getMaxItems().equals(that.getMaxItems()) && + getMinItems() == null ? that.getMinItems() == null : + getMinItems().equals(that.getMinItems()) && + getUniqueItems() == null ? that.getUniqueItems() == null : + getUniqueItems().equals(that.getUniqueItems()) && + super.equals(obj); + } else { + return false; + } + } + + /** + * {@link ArraySchema#additionalItems} + * @return the additionalItems + */ + public ArraySchema.AdditionalItems getAdditionalItems() { + return additionalItems; + } + + /** + * {@link ArraySchema#items} + * @return the items + */ + public ArraySchema.Items getItems() { + return items; + } + /** + * {@link ArraySchema#maxItems} + * @return the maxItems + */ + public Integer getMaxItems() { + return maxItems; + } + + + /** + * {@link ArraySchema#minItems} + * @return the minItems + */ + public Integer getMinItems() { + return minItems; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#getType() + */ + @Override + public SchemaType getType() { + return type; + } + + /** + * {@link ArraySchema#uniqueItems} + * @return the uniqueItems + */ + public Boolean getUniqueItems() { + return uniqueItems; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#isArraySchema() + */ + @Override + public boolean isArraySchema() { return true; } + + /** + * {@link ArraySchema#additionalItems} + * @param additionalItems the additionalItems to set + */ + public void setAdditionalItems(ArraySchema.AdditionalItems additionalItems) { + this.additionalItems = additionalItems; + } + + /** + * {@link ArraySchema#items} + * @param items the items to set + */ + public void setItems(ArraySchema.Items items) { + this.items = items; + } + + /** + * Convenience method to set the json schema for the {@link ArraySchema#items} + * field + * @param jsonSchema + */ + public void setItemsSchema(JsonSchema jsonSchema) { + items = new SingleItems(jsonSchema); + } + /** + * {@link ArraySchema#maxItems} + * @param maxItems the maxItems to set + */ + public void setMaxItems(Integer maxItems) { + this.maxItems = maxItems; + } + + /** + * {@link ArraySchema#minItems} + * @param minItems the minItems to set + */ + public void setMinItems(Integer minItems) { + this.minItems = minItems; + } + + /** + * {@link ArraySchema#uniqueItems} + * @param uniqueItems the uniqueItems to set + */ + public void setUniqueItems(Boolean uniqueItems) { + this.uniqueItems = uniqueItems; + } + + /** + * This provides a definition for additional items in an array instance + when tuple definitions of the items is provided. + */ + public static abstract class AdditionalItems { + + @JsonCreator + public static Items jsonCreator(Map props) { + // not implemented for jsonSchema + return null; + //KNOWN ISSUE: pending https://github.com/FasterXML/jackson-databind/issues/43 + } + } + + /** + * When this attribute value is an array of jsonSchemas and the instance + value is an array, each position in the instance array MUST conform + to the jsonSchema in the corresponding position for this array. This + called tuple typing. When tuple typing is used, additional items are + allowed, disallowed, or constrained by the "additionalItems" + */ + public static class ArrayItems extends ArraySchema.Items { + @JsonProperty + private JsonSchema[] jsonSchemas; + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.ArraySchema.Items#asArrayItems() + */ + @Override + public ArrayItems asArrayItems() { return this; } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof Items) { + ArrayItems that = (ArrayItems) obj; + return getJsonSchemas() == null ? that.getJsonSchemas() == null : + getJsonSchemas().equals(that.getJsonSchemas()); + } else { + return false; + } + } + + /** + * {@link ArraySchema.ArrayItems#jsonSchemas} + * @return the jsonSchemas + */ + public JsonSchema[] getJsonSchemas() { + return jsonSchemas; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.ArraySchema.Items#isArrayItems() + */ + @Override + public boolean isArrayItems() { return true; } + + } + + /** + * This attribute defines the allowed items in an instance array, and + MUST be a jsonSchema or an array of jsonSchemas. The default value is an + empty jsonSchema which allows any value for items in the instance array. + */ + public static abstract class Items { + + @JsonIgnore + public boolean isSingleItems() { return false; } + + @JsonIgnore + public boolean isArrayItems() { return false; } + + public SingleItems asSingleItems() { return null; } + public ArrayItems asArrayItems() { return null; } + + @JsonCreator + public static Items jsonCreator(Map props) { + //for now only support deserialization of singleItems + Object typeFound = props.get("type"); + if (typeFound == null || ! (typeFound instanceof String)) { + return null; + } + String type = (String) typeFound; + JsonSchema schema = JsonSchema.minimalForFormat(SchemaType.forValue(type)); + //KNOWN ISSUE: pending https://github.com/FasterXML/jackson-databind/issues/43 + //only deserialize items as minimal schema for type + return new SingleItems(schema); + } + + } + + /** + * This can be false + to indicate additional items in the array are not allowed + */ + public static class NoAdditionalItems extends AdditionalItems { + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + return obj instanceof NoAdditionalItems; + } + @JsonValue + public Boolean value() { return false; } + } + + /** + * or it can + be a jsonSchema that defines the jsonSchema of the additional items. + */ + public static class SchemaAdditionalItems extends AdditionalItems { + + @JsonIgnore + private JsonSchema jsonSchema; + + public SchemaAdditionalItems(JsonSchema schema) { + jsonSchema = schema; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + return obj instanceof SchemaAdditionalItems && + getJsonSchema() == null ? ((SchemaAdditionalItems)obj).getJsonSchema() == null : + getJsonSchema().equals(((SchemaAdditionalItems)obj).getJsonSchema()); + } + + @JsonValue + public JsonSchema getJsonSchema() { + return jsonSchema; + } + } + + /** + * When this attribute value is a jsonSchema and the instance value is an + array, then all the items in the array MUST be valid according to the + jsonSchema. + */ + public static class SingleItems extends ArraySchema.Items { + @JsonIgnore + private JsonSchema jsonSchema; + + public SingleItems(JsonSchema jsonSchema) { + this.jsonSchema = jsonSchema; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + return obj instanceof SingleItems && + getSchema() == null ? ((SingleItems)obj).getSchema() == null : + getSchema().equals(((SingleItems)obj).getSchema()); + } + + /** + * {@link ArraySchema.SingleItems#jsonSchema} + * @return the jsonSchema + */ + @JsonValue + public JsonSchema getSchema() { + return jsonSchema; + } + + /** + * {@link ArraySchema.SingleItems#jsonSchema} + * @param jsonSchema the jsonSchema to set + */ + public void setSchema(JsonSchema jsonSchema) { + this.jsonSchema = jsonSchema; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.ArraySchema.Items#isSingleItems() + */ + @Override + public boolean isSingleItems() { return true; } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.ArraySchema.Items#asSingleItems() + */ + @Override + public SingleItems asSingleItems() { return this; } + } + + } \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/BooleanSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/BooleanSchema.java new file mode 100644 index 0000000000..2d0bbb9bfa --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/BooleanSchema.java @@ -0,0 +1,34 @@ +package com.fasterxml.jackson.databind.jsonschema.types; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * This class represents a {@link JsonSchema} of type boolean + * @author jphelan + * + */ +public class BooleanSchema extends ValueTypeSchema { + + @JsonIgnore + private final SchemaType type = SchemaType.BOOLEAN; + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#isBooleanSchema() + */ + @Override + public boolean isBooleanSchema() { return true; } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#getType() + */ + @Override + public SchemaType getType() { + return type; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#asBooleanSchema() + */ + @Override + public BooleanSchema asBooleanSchema() { return this; } +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/ContainerTypeSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/ContainerTypeSchema.java new file mode 100644 index 0000000000..c73b14cad6 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/ContainerTypeSchema.java @@ -0,0 +1,74 @@ +package com.fasterxml.jackson.databind.jsonschema.types; + +import java.util.HashSet; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * This class encapsulates the functionality of container type {@link JsonSchema} + * Array and Object + * @author jphelan + * + */ +public abstract class ContainerTypeSchema extends SimpleTypeSchema { + /** + * This provides an enumeration of all possible values that are valid + for the instance property. This MUST be an array, and each item in + the array represents a possible value for the instance value. If + this attribute is defined, the instance value MUST be one of the + values in the array in order for the schema to be valid. Comparison + of enum values uses the same algorithm as defined in "uniqueItems" + (Section 5.15). + */ + @JsonProperty(required = true) + private Set enums; + + //instance initializer block + { + enums = new HashSet(); + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#asContainerSchema() + */ + @Override + public ContainerTypeSchema asContainerSchema() { return this; } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.SimpleTypeSchema#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof ContainerTypeSchema) { + ContainerTypeSchema that = (ContainerTypeSchema)obj; + return getEnums() == null ? that.getEnums() == null : + getEnums().equals(that.getEnums()) && + super.equals(obj); + } else { + return false; + } + } + + /** + * {@link ContainerTypeSchema#enums} + * @return the enums + */ + public Set getEnums() { + return enums; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#isContainerTypeSchema() + */ + @Override + public boolean isContainerTypeSchema() { return true; } + + /** + * {@link ContainerTypeSchema#enums} + * @param enums the enums to set + */ + public void setEnums(Set enums) { + this.enums = enums; + } +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/HyperSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/HyperSchema.java new file mode 100644 index 0000000000..3019549e34 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/HyperSchema.java @@ -0,0 +1,347 @@ + package com.fasterxml.jackson.databind.jsonschema.types; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * This class represents the HyperSchema portion of a {@link JsonSchema} + * It is a skeleton intended as a starting point for customization. + * @author jphelan + * + */ +public class HyperSchema extends JsonSchema { + + + /** + * This attribute indicates that the instance property SHOULD NOT be + changed. Attempts by a user agent to modify the value of this + property are expected to be rejected by a server. + */ + @JsonProperty + private String readOnly; + + /** + * If the instance property value is a string, this attribute defines + that the string SHOULD be interpreted as binary data and decoded + using the encoding named by this jsonSchema property. RFC 2045, Sec 6.1 + [RFC2045] lists the possible values for this property. + */ + @JsonProperty + private String contentEncoding; + + + /** + * This attribute is a URI that defines what the instance's URI MUST + start with in order to validate. The value of the "pathStart" + attribute MUST be resolved as per RFC 3986, Sec 5 [RFC3986], and is + relative to the instance's URI. + + When multiple schemas have been referenced for an instance, the user + agent can determine if this jsonSchema is applicable for a particular + instance by determining if the URI of the instance begins with the + the value of the "pathStart" attribute. If the URI of the instance + does not start with this URI, or if another jsonSchema specifies a + starting URI that is longer and also matches the instance, this + jsonSchema SHOULD NOT be applied to the instance. Any jsonSchema that does + not have a pathStart attribute SHOULD be considered applicable to all + the instances for which it is referenced. + */ + @JsonProperty + private String pathStart; + + /** + * This attribute defines the media type of the instance representations + that this jsonSchema is defining. + */ + @JsonProperty + private String mediaType; + + /** + * This property indicates the fragment resolution protocol to use for + resolving fragment identifiers in URIs within the instance + representations. This applies to the instance object URIs and all + children of the instance object's URIs. The default fragment + resolution protocol is "slash-delimited", which is defined below. + Other fragment resolution protocols MAY be used, but are not defined + in this document. + + The fragment identifier is based on RFC 2396, Sec 5 [RFC2396], and + defines the mechanism for resolving references to entities within a + document. + */ + @JsonProperty + private String fragmentResolution; + /** + * 6.2.1. slash-delimited fragment resolution + + With the slash-delimited fragment resolution protocol, the fragment + identifier is interpreted as a series of property reference tokens + that start with and are delimited by the "/" character (\x2F). Each + property reference token is a series of unreserved or escaped URI + characters. Each property reference token SHOULD be interpreted, + starting from the beginning of the fragment identifier, as a path + reference in the target JSON structure. The final target value of + the fragment can be determined by starting with the root of the JSON + structure from the representation of the resource identified by the + pre-fragment URI. If the target is a JSON object, then the new + target is the value of the property with the name identified by the + next property reference token in the fragment. If the target is a + JSON array, then the target is determined by finding the item in + array the array with the index defined by the next property reference + token (which MUST be a number). The target is successively updated + for each property reference token, until the entire fragment has been + traversed. + + Property names SHOULD be URI-encoded. In particular, any "/" in a + property name MUST be encoded to avoid being interpreted as a + property delimiter. + + For example, for the following JSON representation: + + { + "foo":{ + "anArray":[ + {"prop":44} + ], + "another prop":{ + "baz":"A string" + } + } + } + + The following fragment identifiers would be resolved: + + fragment identifier resolution + ------------------- ---------- + # self, the root of the resource itself + #/foo the object referred to by the foo property + #/foo/another%20prop the object referred to by the "another prop" + property of the object referred to by the + "foo" property + #/foo/another%20prop/baz the string referred to by the value of "baz" + property of the "another prop" property of + the object referred to by the "foo" property + #/foo/anArray/0 the first object in the "anArray" array + + 6.2.2. dot-delimited fragment resolution + + The dot-delimited fragment resolution protocol is the same as slash- + delimited fragment resolution protocol except that the "." character + (\x2E) is used as the delimiter between property names (instead of + "/") and the path does not need to start with a ".". For example, + #.foo and #foo are a valid fragment identifiers for referencing the + value of the foo propery. + */ + + @JsonProperty + private LinkDescriptionObject[] links; + + /** + * A link description object is used to describe link relations. In the + context of a jsonSchema, it defines the link relations of the instances + of the jsonSchema, and can be parameterized by the instance values. The + link description format can be used on its own in regular (non-jsonSchema + documents), and use of this format can be declared by referencing the + normative link description jsonSchema as the the jsonSchema for the data + structure that uses the links. + */ + public class LinkDescriptionObject { + + /** + * The value of the "href" link description property indicates the + target URI of the related resource. The value of the instance + property SHOULD be resolved as a URI-Reference per RFC 3986 [RFC3986] + and MAY be a relative URI. The base URI to be used for relative + resolution SHOULD be the URI used to retrieve the instance object + (not the jsonSchema) when used within a jsonSchema. Also, when links are + used within a jsonSchema, the URI SHOULD be parametrized by the property + values of the instance object, if property values exist for the + corresponding variables in the template (otherwise they MAY be + provided from alternate sources, like user input). + + Instance property values SHOULD be substituted into the URIs where + matching braces ('{', '}') are found surrounding zero or more + characters, creating an expanded URI. Instance property value + substitutions are resolved by using the text between the braces to + denote the property name from the instance to get the value to + substitute. For example, if an href value is defined: + + http://somesite.com/{id} + + Then it would be resolved by replace the value of the "id" property + value from the instance object. If the value of the "id" property + was "45", the expanded URI would be: + + http://somesite.com/45 + + If matching braces are found with the string "@" (no quotes) between + the braces, then the actual instance value SHOULD be used to replace + the braces, rather than a property value. This should only be used + in situations where the instance is a scalar (string, boolean, or + number), and not for objects or arrays. + + */ + @JsonProperty + private String href; + + /** + * The value of the "rel" property indicates the name of the relation to + the target resource. The relation to the target SHOULD be + interpreted as specifically from the instance object that the jsonSchema + (or sub-jsonSchema) applies to, not just the top level resource that + contains the object within its hierarchy. If a resource JSON + representation contains a sub object with a property interpreted as a + link, that sub-object holds the relation with the target. A relation + to target from the top level resource MUST be indicated with the + jsonSchema describing the top level JSON representation. + + Relationship definitions SHOULD NOT be media type dependent, and + users are encouraged to utilize existing accepted relation + definitions, including those in existing relation registries (see RFC + 4287 [RFC4287]). However, we define these relations here for clarity + of normative interpretation within the context of JSON hyper jsonSchema + defined relations: + + self If the relation value is "self", when this property is + encountered in the instance object, the object represents a + resource and the instance object is treated as a full + representation of the target resource identified by the specified + URI. + + full This indicates that the target of the link is the full + representation for the instance object. The object that contains + this link possibly may not be the full representation. + + describedby This indicates the target of the link is the jsonSchema for + the instance object. This MAY be used to specifically denote the + schemas of objects within a JSON object hierarchy, facilitating + polymorphic type data structures. + + root This relation indicates that the target of the link SHOULD be + treated as the root or the body of the representation for the + purposes of user agent interaction or fragment resolution. All + other properties of the instance objects can be regarded as meta- + data descriptions for the data. + + The following relations are applicable for schemas (the jsonSchema as the + "from" resource in the relation): + + instances This indicates the target resource that represents + collection of instances of a jsonSchema. + + create This indicates a target to use for creating new instances of + a jsonSchema. This link definition SHOULD be a submission link with a + non-safe method (like POST). + + For example, if a jsonSchema is defined: + + { + "links": [ + { + "rel": "self" + "href": "{id}" + }, + { + "rel": "up" + "href": "{upId}" + }, + { + "rel": "children" + "href": "?upId={id}" + } + ] + } + + And if a collection of instance resource's JSON representation was + retrieved: + + GET /Resource/ + + [ + { + "id": "thing", + "upId": "parent" + }, + { + "id": "thing2", + "upId": "parent" + } + ] + + This would indicate that for the first item in the collection, its + own (self) URI would resolve to "/Resource/thing" and the first + item's "up" relation SHOULD be resolved to the resource at + "/Resource/parent". The "children" collection would be located at + "/Resource/?upId=thing". + */ + @JsonProperty + private String rel; + + /** + * This property value is a jsonSchema that defines the expected structure + of the JSON representation of the target of the link. + */ + @JsonProperty + private JsonSchema targetSchema; + + /** + * This attribute defines which method can be used to access the target + resource. In an HTTP environment, this would be "GET" or "POST" + (other HTTP methods such as "PUT" and "DELETE" have semantics that + are clearly implied by accessed resources, and do not need to be + defined here). This defaults to "GET". + */ + @JsonProperty + private String method; + + /** + * If present, this property indicates a query media type format that + the server supports for querying or posting to the collection of + instances at the target resource. The query can be suffixed to the + target URI to query the collection with property-based constraints on + the resources that SHOULD be returned from the server or used to post + data to the resource (depending on the method). For example, with + the following jsonSchema: + + { + "links":[ + { + "enctype":"application/x-www-form-urlencoded", + "method":"GET", + "href":"/Product/", + "properties":{ + "name":{"description":"name of the product"} + } + } + ] + } + This indicates that the client can query the server for instances + that have a specific name: + + /Product/?name=Slinky + + If no enctype or method is specified, only the single URI specified + by the href property is defined. If the method is POST, + "application/json" is the default media type. + */ + @JsonProperty + private String enctype; + + /** + * This attribute contains a jsonSchema which defines the acceptable + structure of the submitted request (for a GET request, this jsonSchema + would define the properties for the query string and for a POST + request, this would define the body). + */ + @JsonProperty + private JsonSchema jsonSchema; + + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#getType() + */ + @Override + public SchemaType getType() { + return null; + } +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/IntegerSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/IntegerSchema.java new file mode 100644 index 0000000000..4baaaacb7c --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/IntegerSchema.java @@ -0,0 +1,60 @@ +package com.fasterxml.jackson.databind.jsonschema.types; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * This class represents a {@link JsonSchema} as an integer type + * @author jphelan + * + */ +public class IntegerSchema extends NumberSchema { + + /** + * This attribute defines what value the number instance must be + divisible by with no remainder (the result of the division must be an + integer.) The value of this attribute SHOULD NOT be 0. + */ + @JsonProperty + private Integer divisibleBy; + + @JsonProperty(required = true) + public final SchemaType type = SchemaType.INTEGER; + + @Override + public IntegerSchema asIntegerSchema() { return this; } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.NumberSchema#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof IntegerSchema) { + IntegerSchema that = (IntegerSchema)obj; + return getDivisibleBy() == null ? that.getDivisibleBy() == null : + getDivisibleBy().equals(that.getDivisibleBy()) && + super.equals(obj); + } else { + return false; + } + } + + + /** + * {@link IntegerSchema#divisibleBy} + * @return the divisibleBy + */ + public Integer getDivisibleBy() { + return divisibleBy; + } + + @Override + public boolean isIntegerSchema() { return true; } + + /** + * {@link IntegerSchema#divisibleBy} + * @param divisibleBy the divisibleBy to set + */ + public void setDivisibleBy(Integer divisibleBy) { + this.divisibleBy = divisibleBy; + } +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/JsonSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/JsonSchema.java new file mode 100644 index 0000000000..09482afb0b --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/JsonSchema.java @@ -0,0 +1,612 @@ +package com.fasterxml.jackson.databind.jsonschema.types; + +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * The type wraps the json schema specification at : + * Json JsonSchema + * Draft
JSON (JavaScript Object Notation) JsonSchema defines the + * media type "application/schema+json", a JSON based format for defining the + * structure of JSON data. JSON JsonSchema provides a contract for what JSON data is + * required for a given application and how to interact with it. JSON JsonSchema is + * intended to define validation, documentation, hyperlink navigation, and + * interaction control of JSON data.
+ * + *
JSON (JavaScript Object Notation) JsonSchema is a JSON media type + * for defining the structure of JSON data. JSON JsonSchema provides a contract for + * what JSON data is required for a given application and how to interact with + * it. JSON JsonSchema is intended to define validation, documentation, hyperlink + * navigation, and interaction control of JSON data.
+ * + * An example JSON JsonSchema provided by the JsonSchema draft: + * + *
+ * 	{
+ * 	  "name":"Product",
+ * 	  "properties":{
+ * 	    "id":{
+ * 	      "type":"number",
+ * 	      "description":"Product identifier",
+ * 	      "required":true
+ * 	    },
+ * 	    "name":{
+ * 	      "description":"Name of the product",
+ * 	      "type":"string",
+ * 	      "required":true
+ * 	    },
+ * 	    "price":{
+ * 	      "required":true,
+ * 	      "type": "number",
+ * 	      "minimum":0,
+ * 	      "required":true
+ * 	    },
+ * 	    "tags":{
+ * 	      "type":"array",
+ * 	      "items":{
+ * 	        "type":"string"
+ * 	      }
+ * 	    }
+ * 	  },
+ * 	  "links":[
+ * 	    {
+ * 	      "rel":"full",
+ * 	      "href":"{id}"
+ * 	    },
+ * 	    {
+ * 	      "rel":"comments",
+ * 	      "href":"comments/?id={id}"
+ * 	    }
+ * 	  ]
+ * 	}
+ * 
+ * + * @author jphelan + */ +@JsonInclude(Include.NON_EMPTY) +@JsonTypeInfo(use = Id.CUSTOM, include = As.PROPERTY, property = "type") +@JsonTypeIdResolver(JsonSchema.JsonSchemaIdResolver.class) +public abstract class JsonSchema { + + /** + * This attribute defines a URI of a schema that contains the full + * representation of this schema. When a validator encounters this + * attribute, it SHOULD replace the current schema with the schema + * referenced by the value's URI (if known and available) and re- validate + * the instance. This URI MAY be relative or absolute, and relative URIs + * SHOULD be resolved against the URI of the current schema. + */ + @JsonProperty + private String $ref; + + /** + * This attribute defines a URI of a JSON JsonSchema that is the schema of the + * current schema. When this attribute is defined, a validator SHOULD use + * the schema referenced by the value's URI (if known and available) when + * resolving Hyper JsonSchema (Section 6) links (Section 6.1). + * + * A validator MAY use this attribute's value to determine which version of + * JSON JsonSchema the current schema is written in, and provide the appropriate + * validation features and behavior. Therefore, it is RECOMMENDED that all + * schema authors include this attribute in their schemas to prevent + * conflicts with future JSON JsonSchema specification changes. + */ + @JsonProperty + private String $schema; + + /** + * This attribute takes the same values as the "type" attribute, however if + * the instance matches the type or if this value is an array and the + * instance matches any type or schema in the array, then this instance is + * not valid. + */ + @JsonProperty + private JsonSchema[] disallow; + /** + * The value of this property MUST be another schema which will provide a + * base schema which the current schema will inherit from. The inheritance + * rules are such that any instance that is valid according to the current + * schema MUST be valid according to the referenced schema. This MAY also be + * an array, in which case, the instance MUST be valid for all the schemas + * in the array. A schema that extends another schema MAY define additional + * attributes, constrain existing attributes, or add other constraints. + * + * Conceptually, the behavior of extends can be seen as validating an + * instance against all constraints in the extending schema as well as the + * extended schema(s). More optimized implementations that merge schemas are + * possible, but are not required. An example of using "extends": + * + * { "description":"An adult", "properties":{"age":{"minimum": 21}}, + * "extends":"person" } { "description":"Extended schema", + * "properties":{"deprecated":{"type": "boolean"}}, + * "extends":"http://json-schema.org/draft-03/schema" } + */ + @JsonIgnore + private JsonSchema[] extendsextends; + /** + * This attribute defines the current URI of this schema (this attribute is + * effectively a "self" link). This URI MAY be relative or absolute. If the + * URI is relative it is resolved against the current URI of the parent + * schema it is contained in. If this schema is not contained in any parent + * schema, the current URI of the parent schema is held to be the URI under + * which this schema was addressed. If id is missing, the current URI of a + * schema is defined to be that of the parent schema. The current URI of the + * schema is also used to construct relative references such as for $ref. + */ + @JsonProperty + private String id; + /** + * This attribute indicates if the instance must have a value, and not be + * undefined. This is false by default, making the instance optional. + */ + @JsonProperty + private Boolean required = null; + + + /** + * Attempt to return this JsonSchema as an {@link AnySchema} + * @return this as an AnySchema if possible, or null otherwise + */ + public AnySchema asAnySchema() { + return null; + } + + /** + * Attempt to return this JsonSchema as an {@link ArraySchema} + * @return this as an ArraySchema if possible, or null otherwise + */ + public ArraySchema asArraySchema() { + return null; + } + + /** + * Attempt to return this JsonSchema as a {@link BooleanSchema} + * @return this as a BooleanSchema if possible, or null otherwise + */ + public BooleanSchema asBooleanSchema() { + return null; + } + + /** + * Attempt to return this JsonSchema as a {@link ContainerTypeSchema} + * @return this as an ContainerTypeSchema if possible, or null otherwise + */ + public ContainerTypeSchema asContainerSchema() { + return null; + } + + /** + * Attempt to return this JsonSchema as an {@link IntegerSchema} + * @return this as an IntegerSchema if possible, or null otherwise + */ + public IntegerSchema asIntegerSchema() { + return null; + } + + /** + * Attempt to return this JsonSchema as a {@link NullSchema} + * @return this as a NullSchema if possible, or null otherwise + */ + public NullSchema asNullSchema() { + return null; + } + + /** + * Attempt to return this JsonSchema as a {@link NumberSchema} + * @return this as a NumberSchema if possible, or null otherwise + */ + public NumberSchema asNumberSchema() { + return null; + } + + /** + * Attempt to return this JsonSchema as an {@link ObjectSchema} + * @return this as an ObjectSchema if possible, or null otherwise + */ + public ObjectSchema asObjectSchema() { + return null; + } + + /** + * Attempt to return this JsonSchema as a {@link SimpleTypeSchema} + * @return this as a SimpleTypeSchema if possible, or null otherwise + */ + public SimpleTypeSchema asSimpleTypeSchema() { + return null; + } + + /** + * Attempt to return this JsonSchema as a {@link StringSchema} + * @return this as a StringSchema if possible, or null otherwise + */ + public StringSchema asStringSchema() { + return null; + } + + /** + * Attempt to return this JsonSchema as an {@link UnionTypeSchema} + * @return this as a UnionTypeSchema if possible, or null otherwise + */ + public UnionTypeSchema asUnionTypeSchema() { + return null; + } + + /** + * Attempt to return this JsonSchema as a {@link ValueTypeSchema} + * @return this as a ValueTypeSchema if possible, or null otherwise + */ + public ValueTypeSchema asValueSchemaSchema() { + return null; + } + + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof JsonSchema) { + JsonSchema that = ((JsonSchema)obj); + return that.getType() == getType() && + getRequired() == null ? that.getRequired() == null : + getRequired().equals(that.getRequired()) && + get$ref() == null ? that.get$ref() == null : + get$ref().equals(that.get$ref()) && + get$schema() == null ? that.get$schema() == null : + get$schema().equals(that.get$schema()) && + getDisallow() == null ? that.getDisallow() == null : + getDisallow().equals(that.getDisallow()) && + getExtends() == null ? that.getExtends() == null : + getExtends().equals(that.getExtends()); + } + return false; + } + + /** + * {@link JsonSchema#$ref} + * + * @return the $ref + */ + public String get$ref() { + return $ref; + } + + /** + * {@link JsonSchema#$schema} + * + * @return the $schema + */ + public String get$schema() { + return $schema; + } + + /** + * {@link JsonSchema#disallow} + * + * @return the disallow + */ + public JsonSchema[] getDisallow() { + return disallow; + } + + /** + * {@link JsonSchema#extendsextends} + * + * @return the extendsextends + */ + @JsonGetter("extends") + public JsonSchema[] getExtends() { + return extendsextends; + } + + /** + * {@link JsonSchema#id} + * + * @return the id + */ + public String getId() { + return id; + } + + /** + * {@link JsonSchema#required} + * + * @return the required + */ + public Boolean getRequired() { + return required; + } + + @JsonIgnore + public abstract SchemaType getType(); + + + /** + * determine if this JsonSchema is an {@link AnySchema}. + * + * @return true if this JsonSchema is an AnySchema, false otherwise + */ + @JsonIgnore + public boolean isAnySchema() { + return false; + } + + /** + * determine if this JsonSchema is an {@link ArraySchema}. + * + * @return true if this JsonSchema is an ArraySchema, false otherwise + */ + @JsonIgnore + public boolean isArraySchema() { + return false; + } + + /** + * determine if this JsonSchema is an {@link BooleanSchema}. + * + * @return true if this JsonSchema is an BooleanSchema, false otherwise + */ + @JsonIgnore + public boolean isBooleanSchema() { + return false; + } + + /** + * determine if this JsonSchema is an {@link ContainerTypeSchema}. + * + * @return true if this JsonSchema is an ContainerTypeSchema, false otherwise + */ + @JsonIgnore + public boolean isContainerTypeSchema() { + return false; + } + + /** + * determine if this JsonSchema is an {@link IntegerSchema}. + * + * @return true if this JsonSchema is an IntegerSchema, false otherwise + */ + @JsonIgnore + public boolean isIntegerSchema() { + return false; + } + + /** + * determine if this JsonSchema is an {@link NullSchema}. + * + * @return true if this JsonSchema is an NullSchema, false otherwise + */ + @JsonIgnore + public boolean isNullSchema() { + return false; + } + + /** + * determine if this JsonSchema is an {@link NumberSchema}. + * + * @return true if this JsonSchema is an NumberSchema, false otherwise + */ + @JsonIgnore + public boolean isNumberSchema() { + return false; + } + + /** + * determine if this JsonSchema is an {@link ObjectSchema}. + * + * @return true if this JsonSchema is an ObjectSchema, false otherwise + */ + @JsonIgnore + public boolean isObjectSchema() { + return false; + } + + /** + * determine if this JsonSchema is an {@link SimpleTypeSchema}. + * + * @return true if this JsonSchema is an SimpleTypeSchema, false otherwise + */ + @JsonIgnore + public boolean isSimpleTypeSchema() { + return false; + } + + /** + * determine if this JsonSchema is an {@link StringSchema}. + * + * @return true if this JsonSchema is an StringSchema, false otherwise + */ + @JsonIgnore + public boolean isStringSchema() { + return false; + } + + /** + * determine if this JsonSchema is an {@link UnionTypeSchema}. + * + * @return true if this JsonSchema is an UnionTypeSchema, false otherwise + */ + @JsonIgnore + public boolean isUnionTypeSchema() { + return false; + } + + + /** + * determine if this JsonSchema is an {@link ValueTypeSchema}. + * + * @return true if this JsonSchema is an ValueTypeSchema, false otherwise + */ + @JsonIgnore + public boolean isValueTypeSchema() { + return false; + } + + /** + * {@link JsonSchema#$ref} + * + * @param $ref + * the $ref to set + */ + public void set$ref(String $ref) { + this.$ref = $ref; + } + + /** + * {@link JsonSchema#$schema} + * + * @param $schema + * the $schema to set + */ + public void set$schema(String $schema) { + this.$schema = $schema; + } + + /** + * {@link JsonSchema#disallow} + * + * @param disallow + * the disallow to set + */ + public void setDisallow(JsonSchema[] disallow) { + this.disallow = disallow; + } + + /** + * {@link JsonSchema#extendsextends} + * + * @param extendsextends + * the extendsextends to set + */ + @JsonSetter("extends") + public void setExtends(JsonSchema[] extendsextends) { + this.extendsextends = extendsextends; + } + + /** + * {@link JsonSchema#id} + * + * @param id + * the id to set + */ + public void setId(String id) { + this.id = id; + } + + /** + * {@link JsonSchema#required} + * + * @param required + * the required to set + */ + public void setRequired(Boolean required) { + this.required = required; + } + + /** + * Create a schema which verifies only that an object is of the given format. + * @param format the format to expect + * @return the schema verifying the given format + */ + public static JsonSchema minimalForFormat(SchemaType format) { + switch (format) { + case ARRAY: + return new ArraySchema(); + case OBJECT: + return new ObjectSchema(); + case BOOLEAN: + return new BooleanSchema(); + case INTEGER: + return new IntegerSchema(); + case NUMBER: + return new NumberSchema(); + case STRING: + return new StringSchema(); + case NULL: + return new NullSchema(); + + default: + return new AnySchema(); + } + } + + public static class JsonSchemaIdResolver implements TypeIdResolver { + + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsontype.TypeIdResolver#idFromValue(java.lang.Object) + */ + public String idFromValue(Object value) { + if ( value instanceof JsonSchema) { + return ((JsonSchema)value).getType().value(); + } else { + return null; + } + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsontype.TypeIdResolver#idFromValueAndType(java.lang.Object, java.lang.Class) + */ + public String idFromValueAndType(Object value, Class suggestedType) { + return idFromValue(value); + } + + private static JavaType any = TypeFactory.defaultInstance().constructType(AnySchema.class); + private static JavaType array = TypeFactory.defaultInstance().constructType(ArraySchema.class); + private static JavaType booleanboolean = TypeFactory.defaultInstance().constructType(BooleanSchema.class); + private static JavaType integer = TypeFactory.defaultInstance().constructType(IntegerSchema.class); + private static JavaType nullnull = TypeFactory.defaultInstance().constructType(NullSchema.class); + private static JavaType number = TypeFactory.defaultInstance().constructType(NumberSchema.class); + private static JavaType object = TypeFactory.defaultInstance().constructType(ObjectSchema.class); + private static JavaType string = TypeFactory.defaultInstance().constructType(StringSchema.class); + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsontype.TypeIdResolver#typeFromId(java.lang.String) + */ + public JavaType typeFromId(String id) { + switch (SchemaType.forValue(id)) { + case ANY: return any; + case ARRAY: return array; + case BOOLEAN: return booleanboolean; + case INTEGER: return integer; + case NULL: return nullnull; + case NUMBER: return number; + case OBJECT: return object; + case STRING: return string; + default: + return null; + } + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsontype.TypeIdResolver#getMechanism() + */ + public Id getMechanism() { + return Id.CUSTOM; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsontype.TypeIdResolver#init(com.fasterxml.jackson.databind.JavaType) + */ + public void init(JavaType baseType) { } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsontype.TypeIdResolver#idFromBaseType() + */ + public String idFromBaseType() { + return null; + } + + } +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/JsonValueFormat.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/JsonValueFormat.java new file mode 100644 index 0000000000..d4d4a2bb3f --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/JsonValueFormat.java @@ -0,0 +1,124 @@ +package com.fasterxml.jackson.databind.jsonschema.types; + +/** + * This enum represents the encoded format for a jsonSchema value type + * @author jphelan + * + */ +public enum JsonValueFormat { + /** + * This SHOULD be a date in ISO 8601 format of YYYY-MM- + DDThh:mm:ssZ in UTC time. This is the recommended form of date/ + timestamp. + */ + DATE_TIME { + @Override + public String toString() { return "date-time"; } + }, + + /** + * This SHOULD be a date in the format of YYYY-MM-DD. It is + recommended that you use the "date-time" format instead of "date" + unless you need to transfer only the date part. + */ + DATE { + @Override + public String toString() { return "date"; } + }, + + /** + * This SHOULD be a time in the format of hh:mm:ss. It is + recommended that you use the "date-time" format instead of "time" + unless you need to transfer only the time part. + */ + TIME { + @Override + public String toString() { return "time"; } + }, + + /** + * This SHOULD be the difference, measured in + milliseconds, between the specified time and midnight, 00:00 of + January 1, 1970 UTC. The value SHOULD be a number (integer or + float). + */ + UTC_MILLISEC { + @Override + public String toString() { return "utc-millisec"; } + }, + + /** + * A regular expression, following the regular expression + specification from ECMA 262/Perl 5. + */ + REGEX { + @Override + public String toString() { return "regex"; } + }, + + /** + * This is a CSS color (like "#FF0000" or "red"), based on CSS + 2.1 [W3C.CR-CSS21-20070719]. + */ + COLOR { + @Override + public String toString() { return "color"; } + }, + + /** + * This is a CSS style definition (like "color: red; background- + color:#FFF"), based on CSS 2.1 [W3C.CR-CSS21-20070719]. + */ + STYLE { + @Override + public String toString() { return "style"; } + }, + + /** + * This SHOULD be a phone number (format MAY follow E.123). + */ + PHONE { + @Override + public String toString() { return "phone"; } + }, + + /** + * This value SHOULD be a URI.. + */ + URI { + @Override + public String toString() { return "uri"; } + }, + + /** + * This SHOULD be an email address. + */ + EMAIL { + @Override + public String toString() { return "email"; } + }, + /** + * This SHOULD be an ip version 4 address. + */ + IP_ADDRESS { + @Override + public String toString() { return "ip-address"; } + }, + + /** + * This SHOULD be an ip version 6 address. + */ + IPV6 { + @Override + public String toString() { return "ipv6"; } + }, + + /** + * This SHOULD be a host-name. + */ + HOST_NAME { + @Override + public String toString() { return "host-name"; } + } + +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/NullSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/NullSchema.java new file mode 100644 index 0000000000..83c5acdad3 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/NullSchema.java @@ -0,0 +1,36 @@ +package com.fasterxml.jackson.databind.jsonschema.types; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * This class represents a {@link JsonSchema} as a null type + * @author jphelan + */ +public class NullSchema extends SimpleTypeSchema { + + @JsonIgnore + private final SchemaType type = SchemaType.NULL; + + @Override + public NullSchema asNullSchema() { return this; } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + return (obj instanceof NullSchema && super.equals(obj)); + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#getType() + */ + @Override + public SchemaType getType() { + return type; + } + + @Override + public boolean isNullSchema() { return true; } + +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/NumberSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/NumberSchema.java new file mode 100644 index 0000000000..c1ff5aa002 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/NumberSchema.java @@ -0,0 +1,135 @@ +package com.fasterxml.jackson.databind.jsonschema.types; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * This class represents a {@link JsonSchema} as a number type + * @author jphelan + * + */ +public class NumberSchema extends ValueTypeSchema { + + /** + * This attribute indicates if the value of the instance (if the + instance is a number) can not equal the number defined by the + "maximum" attribute. + */ + @JsonProperty + private Boolean exclusiveMaximum; + + /** + * This attribute indicates if the value of the instance (if the + instance is a number) can not equal the number defined by the + "minimum" attribute. + */ + @JsonProperty + private Boolean exclusiveMinimum; + + /**This attribute defines the maximum value of the instance property*/ + @JsonProperty + private Double maximum = null; + + /**This attribute defines the minimum value of the instance property*/ + @JsonProperty + private Double minimum = null; + + @JsonIgnore + private final SchemaType type = SchemaType.NUMBER; + + @Override + public NumberSchema asNumberSchema() { return this; } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.ValueTypeSchema#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof NumberSchema) { + NumberSchema that = (NumberSchema)obj; + return getExclusiveMaximum() == null ? that.getExclusiveMaximum() == null : + getExclusiveMaximum().equals(that.getExclusiveMaximum()) && + getExclusiveMinimum() == null ? that.getExclusiveMinimum() == null : + getExclusiveMinimum().equals(that.getExclusiveMinimum()) && + getMaximum() == null ? that.getMaximum() == null : + getMaximum().equals(that.getMaximum()) && + getMinimum() == null ? that.getMinimum() == null : + getMinimum().equals(that.getMinimum()) && + super.equals(obj); + } else { + return false; + } + } + + + /** + * {@link NumberSchema#exclusiveMaximum} + * @return the exclusiveMaximum + */ + public Boolean getExclusiveMaximum() { + return exclusiveMaximum; + } + + /** + * {@link NumberSchema#exclusiveMinimum} + * @return the exclusiveMinimum + */ + public Boolean getExclusiveMinimum() { + return exclusiveMinimum; + } + /** + * {@link NumberSchema#maximum} + * @return the maximum + */ + public Double getMaximum() { + return maximum; + } + /** + * {@link NumberSchema#minimum} + * @return the minimum + */ + public Double getMinimum() { + return minimum; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#getType() + */ + @Override + public SchemaType getType() { + return type; + } + + @Override + public boolean isNumberSchema() { return true; } + /** + * {@link NumberSchema#exclusiveMaximum} + * @param exclusiveMaximum the exclusiveMaximum to set + */ + public void setExclusiveMaximum(Boolean exclusiveMaximum) { + this.exclusiveMaximum = exclusiveMaximum; + } + /** + * {@link NumberSchema#exclusiveMinimum} + * @param exclusiveMinimum the exclusiveMinimum to set + */ + public void setExclusiveMinimum(Boolean exclusiveMinimum) { + this.exclusiveMinimum = exclusiveMinimum; + } + + /** + * {@link NumberSchema#maximum} + * @param maximum the maximum to set + */ + public void setMaximum(Double maximum) { + this.maximum = maximum; + } + + /** + * {@link NumberSchema#minimum} + * @param minimum the minimum to set + */ + public void setMinimum(Double minimum) { + this.minimum = minimum; + } +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/ObjectSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/ObjectSchema.java new file mode 100644 index 0000000000..af65dc45fd --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/ObjectSchema.java @@ -0,0 +1,395 @@ +package com.fasterxml.jackson.databind.jsonschema.types; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * This type represents a {@link JsonSchema} as an object type + * @author jphelan + * + */ +public class ObjectSchema extends ContainerTypeSchema { + + /** + * This attribute defines a jsonSchema for all properties that are not + * explicitly defined in an object type definition. If specified, the value + * MUST be a jsonSchema or a boolean. If false is provided, no additional + * properties are allowed beyond the properties defined in the jsonSchema. The + * default value is an empty jsonSchema which allows any value for additional + * properties. + */ + @JsonProperty + private AdditionalProperties additionalProperties; + + /** + * This attribute is an object that defines the requirements of a property + * on an instance object. If an object instance has a property with the same + * name as a property in this attribute's object, then the instance must be + * valid against the attribute's property value + */ + @JsonProperty + private List dependencies; + + /** + * + This attribute is an object that defines the jsonSchema for a set of property + * names of an object instance. The name of each property of this + * attribute's object is a regular expression pattern in the ECMA 262/Perl 5 + * format, while the value is a jsonSchema. If the pattern matches the name of a + * property on the instance object, the value of the instance's property + * MUST be valid against the pattern name's jsonSchema value. + */ + @JsonProperty + private Map patternProperties; + + /** + * This attribute is an object with property definitions that define the + * valid values of instance object property values. When the instance value + * is an object, the property values of the instance object MUST conform to + * the property definitions in this object. In this object, each property + * definition's value MUST be a jsonSchema, and the property's name MUST be the + * name of the instance property that it defines. The instance property + * value MUST be valid according to the jsonSchema from the property definition. + * Properties are considered unordered, the order of the instance properties + * MAY be in any order. + */ + @JsonProperty + private Map properties; + + @JsonIgnore + private final SchemaType type = SchemaType.OBJECT; + + // instance initializer block + { + dependencies = new ArrayList(); + patternProperties = new HashMap(); + properties = new HashMap(); + } + + public boolean addSchemaDependency(String depender, JsonSchema parentMustMatch) { + return dependencies + .add(new SchemaDependency(depender, parentMustMatch)); + } + + public boolean addSimpleDependency(String depender, String dependsOn) { + return dependencies.add(new SimpleDependency(depender, dependsOn)); + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#asObjectSchema() + */ + @Override + public ObjectSchema asObjectSchema() { + return this; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof ObjectSchema) { + ObjectSchema that = (ObjectSchema) obj; + return getAdditionalProperties() == null ? that.getAdditionalProperties() == null : + getAdditionalProperties().equals(that.getAdditionalProperties()) && + getDependencies() == null ? that.getDependencies() == null : + getDependencies().equals(that.getDependencies()) && + getPatternProperties() == null ? that.getPatternProperties() == null : + getPatternProperties().equals(that.getPatternProperties()) && + getProperties() == null ? that.getProperties() == null : + getProperties().equals(that.getProperties()) && + super.equals(obj); + } else { + return false; + } + + } + + /** + * {@link ObjectSchema#additionalProperties} + * + * @return the additionalProperties + */ + public AdditionalProperties getAdditionalProperties() { + return additionalProperties; + } + + /** + * {@link ObjectSchema#dependencies} + * + * @return the dependencies + */ + public List getDependencies() { + return dependencies; + } + + /** + * {@link ObjectSchema#patternProperties} + * + * @return the patternProperties + */ + public Map getPatternProperties() { + return patternProperties; + } + + /** + * {@link ObjectSchema#properties} + * + * @return the properties + */ + public Map getProperties() { + return properties; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#getType() + */ + @Override + public SchemaType getType() { + return type; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#isObjectSchema() + */ + @Override + public boolean isObjectSchema() { + return true; + } + + public void putOptionalProperty(String name, JsonSchema jsonSchema) { + properties.put(name, jsonSchema); + } + + public JsonSchema putPatternProperty(String regex, JsonSchema value) { + return patternProperties.put(regex, value); + } + + public JsonSchema putProperty(String name, JsonSchema value) { + value.setRequired(true); + return properties.put(name, value); + } + + public void rejectAdditionalProperties() { + additionalProperties = NoAdditionalProperties.instance; + } + + /** + * {@link ObjectSchema#additionalProperties} + * + * @param additionalProperties + * the additionalProperties to set + */ + public void setAdditionalProperties( + AdditionalProperties additionalProperties) { + this.additionalProperties = additionalProperties; + } + + /** + * {@link ObjectSchema#dependencies} + * + * @param dependencies + * the dependencies to set + */ + public void setDependencies(List dependencies) { + this.dependencies = dependencies; + } + + /** + * {@link ObjectSchema#patternProperties} + * + * @param patternProperties + * the patternProperties to set + */ + public void setPatternProperties(Map patternProperties) { + this.patternProperties = patternProperties; + } + + /** + * {@link ObjectSchema#properties} + * + * @param properties + * the properties to set + */ + public void setProperties(Map properties) { + this.properties = properties; + } + + public static abstract class AdditionalProperties { + + @JsonCreator + public AdditionalProperties jsonCreator() { + //KNOWN ISSUE: pending https://github.com/FasterXML/jackson-databind/issues/43 + return null; + } + } + + public static abstract class Dependency { + @JsonCreator + public Dependency jsonCreator() { + //KNOWN ISSUE: pending https://github.com/FasterXML/jackson-databind/issues/43 + return null; + } + } + + public static class NoAdditionalProperties extends AdditionalProperties { + public final Boolean schema = false; + + protected NoAdditionalProperties() { + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + return obj instanceof NoAdditionalProperties; + } + + @JsonValue + public Boolean value() { + return schema; + } + + public static final NoAdditionalProperties instance = new NoAdditionalProperties(); + } + + public static class SchemaAdditionalProperties extends AdditionalProperties { + + @JsonProperty + private JsonSchema jsonSchema; + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + return obj instanceof SchemaAdditionalProperties && + getJsonSchema() == null ? ((SchemaAdditionalProperties)obj).getJsonSchema() == null : + getJsonSchema().equals(((SchemaAdditionalProperties)obj).getJsonSchema()); + } + + /** + * {@link ObjectSchema.SchemaAdditionalProperties#jsonSchema} + * @return the jsonSchema + */ + public JsonSchema getJsonSchema() { + return jsonSchema; + } + + public SchemaAdditionalProperties(JsonSchema jsonSchema) { + this.jsonSchema = jsonSchema; + } + } + + /** + * JsonSchema Dependency If the dependency value is a jsonSchema, then the instance + * object MUST be valid against the jsonSchema. + */ + public static class SchemaDependency extends Dependency { + + @JsonProperty(required = true) + private String depender; + + @JsonProperty(required = true) + private JsonSchema parentMustMatch; + + public SchemaDependency(String depender, JsonSchema parentMustMatch) { + this.depender = depender; + this.parentMustMatch = parentMustMatch; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof SchemaDependency) { + SchemaDependency that = (SchemaDependency) obj; + return getDepender() == null ? that.getDepender() == null : + getDepender().equals(that.getDepender()) && + getParentMustMatch() == null ? that.getParentMustMatch() == null : + getParentMustMatch().equals(that.getParentMustMatch()); + } else { + return false; + } + } + + /** + * {@link ObjectSchema.SchemaDependency#depender} + * @return the depender + */ + public String getDepender() { + return depender; + } + + /** + * {@link ObjectSchema.SchemaDependency#parentMustMatch} + * @return the parentMustMatch + */ + public JsonSchema getParentMustMatch() { + return parentMustMatch; + } + } + + /** + * Simple Dependency If the dependency value is a string, then the instance + * object MUST have a property with the same name as the dependency value. + * If the dependency value is an array of strings, then the instance object + * MUST have a property with the same name as each string in the dependency + * value's array. + */ + public static class SimpleDependency extends Dependency { + + @JsonProperty(required = true) + private String depender; + + @JsonProperty(required = true) + private String dependsOn; + + public SimpleDependency(String depender, String dependsOn) { + this.depender = depender; + this.dependsOn = dependsOn; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof SchemaDependency) { + SimpleDependency that = (SimpleDependency) obj; + return getDepender() == null ? that.getDepender() == null : + getDepender().equals(that.getDepender()) && + getDependsOn() == null ? that.getDependsOn() == null : + getDependsOn().equals(that.getDependsOn()); + } else { + return false; + } + } + + /** + * {@link ObjectSchema.SimpleDependency#depender} + * @return the depender + */ + public String getDepender() { + return depender; + } + + /** + * {@link ObjectSchema.SimpleDependency#dependsOn} + * @return the dependsOn + */ + public String getDependsOn() { + return dependsOn; + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/SchemaType.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/SchemaType.java new file mode 100644 index 0000000000..481c726f5b --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/SchemaType.java @@ -0,0 +1,27 @@ +package com.fasterxml.jackson.databind.jsonschema.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public enum SchemaType { + + STRING, + NUMBER, + INTEGER, + BOOLEAN, + OBJECT, + ARRAY, + NULL, + ANY; + + + @JsonValue + public String value() { + return this.name().toLowerCase(); + } + + @JsonCreator + public static SchemaType forValue(String s) { + return valueOf(s.toUpperCase()); + } +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/SimpleTypeSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/SimpleTypeSchema.java new file mode 100644 index 0000000000..3c8a0db0ac --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/SimpleTypeSchema.java @@ -0,0 +1,128 @@ +package com.fasterxml.jackson.databind.jsonschema.types; + +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; + +/** + * This class encapsulates the functionality of {@link JsonSchema} simple types + * @author jphelan + * + */ +public abstract class SimpleTypeSchema extends JsonSchema { + + /** + * This attribute defines the default value of the instance when the + * instance is undefined. + */ + @JsonIgnore + private String defaultdefault; + /** + * This attribute is a string that provides a full description of the of + * purpose the instance property. + */ + @JsonProperty + private String description; + /** + * This attribute is a string that provides a short description of the + * instance property. + */ + @JsonProperty + private String title; + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#asSimpleTypeSchema() + */ + @Override + public SimpleTypeSchema asSimpleTypeSchema() { + return this; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof SimpleTypeSchema) { + SimpleTypeSchema that = (SimpleTypeSchema)obj; + return getDefault() == null ? that.getDefault() == null : + getDefault().equals(that.getDefault()) && + getDescription() == null ? that.getDescription() == null : + getDescription().equals(that.getDescription()) && + getTitle() == null ? that.getTitle() == null : + getTitle().equals(that.getTitle()) && + super.equals(obj); + } else { + return false; + } + } + + + /** + * {@link SimpleTypeSchema#defaultdefault} + * + * @return the defaultdefault + */ + @JsonGetter("default") + public String getDefault() { + return defaultdefault; + } + + /** + * {@link SimpleTypeSchema#description} + * + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * {@link SimpleTypeSchema#title} + * + * @return the title + */ + public String getTitle() { + return title; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#isSimpleTypeSchema() + */ + @Override + public boolean isSimpleTypeSchema() { + return true; + } + + /** + * {@link SimpleTypeSchema#defaultdefault} + * + * @param defaultdefault + * the defaultdefault to set + */ + @JsonSetter("default") + public void setDefault(String defaultdefault) { + this.defaultdefault = defaultdefault; + } + + /** + * {@link SimpleTypeSchema#description} + * + * @param description + * the description to set + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * {@link SimpleTypeSchema#title} + * + * @param title + * the title to set + */ + public void setTitle(String title) { + this.title = title; + } +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/StringSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/StringSchema.java new file mode 100644 index 0000000000..f17dc25a09 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/StringSchema.java @@ -0,0 +1,131 @@ +package com.fasterxml.jackson.databind.jsonschema.types; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * This represents a {@link JsonSchema} as a String + * @author jphelan + * + */ +public class StringSchema extends ValueTypeSchema { + + /** this defines the maximum length of the string. */ + @JsonProperty + private Integer maxLength; + + /** this defines the minimum length of the string. */ + @JsonProperty + private Integer minLength; + /** + * this provides a regular expression that a string instance MUST match in + * order to be valid. Regular expressions SHOULD follow the regular + * expression specification from ECMA 262/Perl 5 + */ + @JsonProperty + private String pattern; + + @JsonIgnore + private final SchemaType type = SchemaType.STRING; + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#asStringSchema() + */ + @Override + public StringSchema asStringSchema() { + return this; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.ValueTypeSchema#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof StringSchema) { + StringSchema that = (StringSchema)obj; + return getMaxLength() == null ? that.getMaxLength() == null : + getMaxLength().equals(that.getMaxLength()) && + getMinLength() == null ? that.getMinLength() == null : + getMinLength().equals(that.getMinLength()) && + getPattern() == null ? that.getPattern() == null : + getPattern().equals(that.getPattern()) && + super.equals(obj); + } else { + return false; + } + } + + + /** + * {@link StringSchema#maxLength} + * + * @return the maxLength + */ + public Integer getMaxLength() { + return maxLength; + } + + /** + * {@link StringSchema#minLength} + * + * @return the minLength + */ + public Integer getMinLength() { + return minLength; + } + + /** + * {@link StringSchema#pattern} + * + * @return the pattern + */ + public String getPattern() { + return pattern; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#getType() + */ + @Override + public SchemaType getType() { + return type; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#isStringSchema() + */ + @Override + public boolean isStringSchema() { + return true; + } + + /** + * {@link StringSchema#maxLength} + * + * @param maxLength + * the maxLength to set + */ + public void setMaxLength(Integer maxLength) { + this.maxLength = maxLength; + } + + /** + * {@link StringSchema#minLength} + * + * @param minLength + * the minLength to set + */ + public void setMinLength(Integer minLength) { + this.minLength = minLength; + } + + /** + * {@link StringSchema#pattern} + * + * @param pattern + * the pattern to set + */ + public void setPattern(String pattern) { + this.pattern = pattern; + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/UnionTypeSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/UnionTypeSchema.java new file mode 100644 index 0000000000..9f9d4ef751 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/UnionTypeSchema.java @@ -0,0 +1,73 @@ +package com.fasterxml.jackson.databind.jsonschema.types; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * This class represents a {@link JsonSchema} as a Union Type Schema: + * "An array of two or more simple type definitions. Each + item in the array MUST be a simple type definition or a schema. + The instance value is valid if it is of the same type as one of + the simple type definitions, or valid by one of the schemas, in + the array." + + * @author jphelan + * + */ +public class UnionTypeSchema extends JsonSchema { + + @JsonProperty + private ValueTypeSchema[] elements; + + @Override + public UnionTypeSchema asUnionTypeSchema() { + return this; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof UnionTypeSchema) { + UnionTypeSchema that = (UnionTypeSchema) obj; + return getElements() == null ? that.getElements() == null : + getElements().equals(that.getElements()) && + super.equals(obj); + } else { + return false; + } + } + + /** + * {@link UnionTypeSchema#elements} + * + * @return the elements + */ + public ValueTypeSchema[] getElements() { + return elements; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#getType() + */ + @Override + public SchemaType getType() { + return null; + } + + @Override + public boolean isUnionTypeSchema() { + return true; + } + + /** + * {@link UnionTypeSchema#elements} + * + * @param elements + * the elements to set + */ + public void setElements(ValueTypeSchema[] elements) { + assert elements.length >= 2 : "Union Type Schemas must contain two or more Simple Type Schemas"; + this.elements = elements; + } +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/ValueTypeSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/ValueTypeSchema.java new file mode 100644 index 0000000000..0358b10c55 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/types/ValueTypeSchema.java @@ -0,0 +1,104 @@ +package com.fasterxml.jackson.databind.jsonschema.types; + +import java.util.HashSet; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * This class represents a {@link JsonSchema} + * A primitive type. + */ +public abstract class ValueTypeSchema extends SimpleTypeSchema { + + /** + * This provides an enumeration of all possible values that are valid + for the instance property. This MUST be an array, and each item in + the array represents a possible value for the instance value. If + this attribute is defined, the instance value MUST be one of the + values in the array in order for the schema to be valid. Comparison + of enum values uses the same algorithm as defined in "uniqueItems" + (Section 5.15). + */ + @JsonProperty + private Set enums; + + /** + * This property defines the type of data, content type, or microformat to + * be expected in the instance property values. A format attribute MAY be + * one of the values listed below, and if so, SHOULD adhere to the semantics + * describing for the format. A format SHOULD only be used to give meaning + * to primitive types (string, integer, number, or boolean). Validators MAY + * (but are not required to) validate that the instance values conform to a + * format. + * + * Additional custom formats MAY be created. These custom formats MAY be + * expressed as an URI, and this URI MAY reference a schema of that + */ + @JsonProperty + private JsonValueFormat format; + + //instance initializer block + { + enums = new HashSet(); + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#asValueSchemaSchema() + */ + @Override + public ValueTypeSchema asValueSchemaSchema() { return this; } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof ValueTypeSchema) { + ValueTypeSchema that = (ValueTypeSchema) obj; + return getEnums() == null ? that.getEnums() == null : + getEnums().equals(that.getEnums()) && + getFormat() == null ? that.getFormat() == null : + getFormat().equals(that.getFormat()) && + super.equals(obj); + } + return false; + } + + /** + * {@link ValueTypeSchema#enums} + * @return the enums + */ + public Set getEnums() { + return enums; + } + /** + * {@link ValueTypeSchema#format} + * @return the format + */ + public JsonValueFormat getFormat() { + return format; + } + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.jsonschema.types.JsonSchema#isValueTypeSchema() + */ + @Override + public boolean isValueTypeSchema() { return true; } + + /** + * {@link ValueTypeSchema#enums} + * @param enums the enums to set + */ + public void setEnums(Set enums) { + this.enums = enums; + } + + /** + * {@link ValueTypeSchema#format} + * @param format the format to set + */ + public void setFormat(JsonValueFormat format) { + this.format = format; + } +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyFilter.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyFilter.java index e2171111c5..938aaa1c25 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyFilter.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyFilter.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; import com.fasterxml.jackson.databind.node.ObjectNode; /** @@ -38,6 +39,6 @@ public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider * processing * */ - public void depositSchemaProperty(BeanPropertyWriter writer, ObjectNode propertiesNode, + public void depositSchemaProperty(BeanPropertyWriter writer, JsonObjectFormatVisitor objectVisitor, SerializerProvider provider); } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java index c54d97eb8e..81af6124ac 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java @@ -13,9 +13,8 @@ import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; import com.fasterxml.jackson.databind.introspect.Annotated; import com.fasterxml.jackson.databind.introspect.ObjectIdInfo; -import com.fasterxml.jackson.databind.jsonschema.JsonSchema; -import com.fasterxml.jackson.databind.jsonschema.SchemaAware; -import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorAware; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.ser.impl.WritableObjectId; import com.fasterxml.jackson.databind.util.ClassUtil; @@ -233,12 +232,11 @@ public void serializeValue(JsonGenerator jgen, Object value, JavaType rootType, /** * The method to be called by {@link ObjectMapper} and {@link ObjectWriter} - * to generate JSON schema for - * given type. + TODO:fillme * * @param type The type for which to generate schema */ - public JsonSchema generateJsonSchema(Class type) + public void acceptJsonFormatVisitor(Class type, JsonFormatVisitorWrapper visitor) throws JsonMappingException { if (type == null) { @@ -248,16 +246,12 @@ public JsonSchema generateJsonSchema(Class type) * type information it needs is accessible via "untyped" serializer) */ JsonSerializer ser = findValueSerializer(type, null); - JsonNode schemaNode = (ser instanceof SchemaAware) ? - ((SchemaAware) ser).getSchema(this, 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"); + if (ser instanceof JsonFormatVisitorAware) { + ((JsonFormatVisitorAware) ser).acceptJsonFormatVisitor(visitor, constructType(type)); + } else { + visitor.expectAnyFormat(constructType(type)); } - return new JsonSchema((ObjectNode) schemaNode); } - /** * Method that can be called to see if this serializer provider * can find a serializer for an instance of given class. diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java index f62d2d19e4..1ce8bd889f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java @@ -1,13 +1,12 @@ package com.fasterxml.jackson.databind.ser.impl; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.ser.std.StdSerializer; /** @@ -35,9 +34,8 @@ public void serialize(Object value, JsonGenerator jgen, SerializerProvider provi } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return null; + ; } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java index fce74ea756..6311e0278f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java @@ -7,6 +7,8 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; +import com.fasterxml.jackson.databind.jsonschema.types.SchemaType; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.ser.std.StaticListSerializerBase; @@ -44,9 +46,11 @@ public IndexedStringListSerializer(JsonSerializer ser) { } - @Override protected JsonNode contentSchema() { - return createSchemaNode("string", true); - } + @Override + protected void acceptContentVisitor(JsonArrayFormatVisitor visitor) { + visitor.itemsFormat(SchemaType.STRING); + + } /* /********************************************************** @@ -147,4 +151,5 @@ private final void serializeUsingCustom(List value, JsonGenerator jgen, wrapAndThrow(provider, e, value, i); } } + } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleBeanPropertyFilter.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleBeanPropertyFilter.java index e5f7283568..e6f9cd873f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleBeanPropertyFilter.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleBeanPropertyFilter.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; import com.fasterxml.jackson.databind.ser.BeanPropertyFilter; import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase; @@ -79,9 +79,9 @@ public void serializeAsField(Object bean, JsonGenerator jgen, } public void depositSchemaProperty(BeanPropertyWriter writer, - ObjectNode propertiesNode, SerializerProvider provider) { + JsonObjectFormatVisitor objectVisitor, SerializerProvider provider) { if (_propertiesToInclude.contains(writer.getName())) { - BeanSerializerBase.depositSchemaProperty(writer, propertiesNode, provider); + BeanSerializerBase.depositSchemaProperty(writer, objectVisitor); } } } @@ -111,9 +111,9 @@ public void serializeAsField(Object bean, JsonGenerator jgen, } public void depositSchemaProperty(BeanPropertyWriter writer, - ObjectNode propertiesNode, SerializerProvider provider) { + JsonObjectFormatVisitor objectVisitor, SerializerProvider provider) { if (!_propertiesToExclude.contains(writer.getName())) { - BeanSerializerBase.depositSchemaProperty(writer, propertiesNode, provider); + BeanSerializerBase.depositSchemaProperty(writer, objectVisitor); } } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java index 8e69ad5b64..aa559275e5 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java @@ -1,13 +1,14 @@ package com.fasterxml.jackson.databind.ser.impl; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonschema.types.SchemaType; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.ContainerSerializer; @@ -169,10 +170,8 @@ private void serializeContentsSlow(String[] value, JsonGenerator jgen, Serialize } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - ObjectNode o = createSchemaNode("array", true); - o.put("items", createSchemaNode("string")); - return o; + visitor.expectArrayFormat(null).itemsFormat(SchemaType.STRING); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java index 3fbc406ad2..3fbf7d4395 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java @@ -7,6 +7,8 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; +import com.fasterxml.jackson.databind.jsonschema.types.SchemaType; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.ser.std.StaticListSerializerBase; @@ -44,9 +46,11 @@ protected StringCollectionSerializer(JsonSerializer ser) _serializer = (JsonSerializer) ser; } - @Override protected JsonNode contentSchema() { - return createSchemaNode("string", true); - } + @Override + protected void acceptContentVisitor(JsonArrayFormatVisitor visitor) { + visitor.itemsFormat(SchemaType.STRING); + + } /* /********************************************************** diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java index 392d09acb7..d434bc74e4 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java @@ -1,11 +1,11 @@ package com.fasterxml.jackson.databind.ser.impl; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.std.StdSerializer; @@ -42,8 +42,8 @@ public final void serializeWithType(Object value, JsonGenerator jgen, Serializer } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { - return null; + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { + visitor.expectAnyFormat(typeHint); } protected void failForEmpty(Object value) throws JsonMappingException diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java index 15073c3057..7801c82743 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java @@ -2,11 +2,13 @@ import java.io.IOException; -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.ser.*; +import com.fasterxml.jackson.databind.ser.ContainerSerializer; /** * Intermediate base class for serializers used for various diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java index 813bf99fd0..335dbe4f71 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java @@ -4,13 +4,18 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.jsonschema.JsonSchema; -import com.fasterxml.jackson.databind.jsonschema.SchemaAware; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.ContainerSerializer; import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; @@ -166,24 +171,24 @@ protected abstract void serializeContents(T value, JsonGenerator jgen, Serialize throws IOException, JsonGenerationException; @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { /* 15-Jan-2010, tatu: This should probably be rewritten, given that * more information about content type is actually being explicitly * passed. So there should be less need to try to re-process that * information. */ - ObjectNode o = createSchemaNode("array", true); + //ObjectNode o = createSchemaNode("array", true); + JsonArrayFormatVisitor arrayVisitor = + visitor.expectArrayFormat(typeHint); JavaType contentType = null; if (typeHint != null) { - JavaType javaType = provider.constructType(typeHint); - contentType = javaType.getContentType(); + contentType = typeHint.getContentType(); if (contentType == null) { // could still be parametrized (Iterators) if (typeHint instanceof ParameterizedType) { Type[] typeArgs = ((ParameterizedType) typeHint).getActualTypeArguments(); if (typeArgs.length == 1) { - contentType = provider.constructType(typeArgs[0]); + contentType = visitor.getProvider().constructType(typeArgs[0]); } } } @@ -193,19 +198,8 @@ public JsonNode getSchema(SerializerProvider provider, Type typeHint) } if (contentType != null) { JsonNode schemaNode = null; - // 15-Oct-2010, tatu: We can't serialize plain Object.class; but what should it produce here? Untyped? - if (contentType.getRawClass() != Object.class) { - JsonSerializer ser = provider.findValueSerializer(contentType, _property); - if (ser instanceof SchemaAware) { - schemaNode = ((SchemaAware) ser).getSchema(provider, null); - } - } - if (schemaNode == null) { - schemaNode = JsonSchema.getDefaultSchemaNode(); - } - o.put("items", schemaNode); + arrayVisitor.itemsFormat(contentType); } - return o; } protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java index 4fdc52dc8b..7a3c834f80 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java @@ -1,25 +1,38 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.ObjectIdGenerator; import com.fasterxml.jackson.annotation.ObjectIdGenerators; -import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.io.SerializedString; - -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.AnnotationIntrospector; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.introspect.Annotated; import com.fasterxml.jackson.databind.introspect.AnnotatedMember; import com.fasterxml.jackson.databind.introspect.ObjectIdInfo; -import com.fasterxml.jackson.databind.jsonschema.JsonSchema; -import com.fasterxml.jackson.databind.jsonschema.JsonSerializableSchema; -import com.fasterxml.jackson.databind.jsonschema.SchemaAware; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorAware; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.ser.*; +import com.fasterxml.jackson.databind.ser.AnyGetterWriter; +import com.fasterxml.jackson.databind.ser.BeanPropertyFilter; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.BeanSerializerBuilder; +import com.fasterxml.jackson.databind.ser.ContainerSerializer; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.fasterxml.jackson.databind.ser.FilterProvider; +import com.fasterxml.jackson.databind.ser.ResolvableSerializer; import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter; import com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator; import com.fasterxml.jackson.databind.ser.impl.WritableObjectId; @@ -35,7 +48,7 @@ public abstract class BeanSerializerBase extends StdSerializer implements ContextualSerializer, ResolvableSerializer, - SchemaAware + JsonFormatVisitorAware { final protected static BeanPropertyWriter[] NO_PROPS = new BeanPropertyWriter[0]; @@ -626,45 +639,49 @@ protected BeanPropertyFilter findFilter(SerializerProvider provider) return filter; } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - ObjectNode o = createSchemaNode("object", true); - // [JACKSON-813]: Add optional JSON Schema id attribute, if found - // NOTE: not optimal, does NOT go through AnnotationIntrospector etc: - JsonSerializableSchema ann = _handledType.getAnnotation(JsonSerializableSchema.class); - if (ann != null) { - String id = ann.id(); - if (id != null && id.length() > 0) { - o.put("id", id); - } - } + //deposit your output format + JsonObjectFormatVisitor objectVisitor = visitor.expectObjectFormat(typeHint); - //todo: should the classname go in the title? - //o.put("title", _className); - ObjectNode propertiesNode = o.objectNode(); - final BeanPropertyFilter filter; if (_propertyFilterId != null) { - filter = findFilter(provider); - } else { - filter = null; - } + try { + BeanPropertyFilter filter = findFilter(visitor.getProvider()); + for (int i = 0; i < _props.length; i++) { + BeanPropertyWriter prop = _props[i]; + filter.depositSchemaProperty(prop, objectVisitor, visitor.getProvider()); + } + return; + } catch (JsonMappingException e) { + // TODO Auto-generated catch block + + } + } for (int i = 0; i < _props.length; i++) { BeanPropertyWriter prop = _props[i]; - if (filter != null) { - filter.depositSchemaProperty(prop, propertiesNode, provider); - continue; - } - depositSchemaProperty(prop, propertiesNode, provider); + JavaType propType = prop.getSerializationType(); + BeanSerializerBase.depositSchemaProperty(prop, objectVisitor); } - o.put("properties", propertiesNode); - return o; } /** + * Attempt to add the output of the given {@link BeanPropertyWriter} in the given {@link ObjectNode}. + * Otherwise, add the default schema {@link JsonNode} in place of the writer's output + * + * @param writer Bean property serializer to use to create schema value + * @param propertiesNode Node which the given property would exist within + */ + public static void depositSchemaProperty(BeanPropertyWriter writer, JsonObjectFormatVisitor objectVisitor) { + if (isPropertyRequired(writer, objectVisitor.getProvider())) { + objectVisitor.property(writer); + } else { + objectVisitor.optionalProperty(writer); + } + } + + /** * Determines if a bean property is required, as determined by * {@link com.fasterxml.jackson.databind.AnnotationIntrospector#hasRequiredMarker}. *

@@ -678,45 +695,5 @@ public static boolean isPropertyRequired(final BeanPropertyWriter prop, final Se return (value == null) ? false : value.booleanValue(); } - /** - * Attempt to add the output of the given {@link BeanPropertyWriter} in the given {@link ObjectNode}. - * Otherwise, add the default schema {@link JsonNode} in place of the writer's output - * - * @param writer Bean property serializer to use to create schema value - * @param propertiesNode Node which the given property would exist within - * @param provider Provider that can be used for accessing dynamic aspects of serialization - * processing - * - * {@link BeanPropertyFilter#depositSchemaProperty(BeanPropertyWriter, ObjectNode, SerializerProvider)} - */ - public static void depositSchemaProperty(BeanPropertyWriter writer, ObjectNode propertiesNode, SerializerProvider provider) - { - JavaType propType = writer.getSerializationType(); - // 03-Dec-2010, tatu: SchemaAware REALLY should use JavaType, but alas it doesn't... - Type hint = (propType == null) ? writer.getGenericPropertyType() : propType.getRawClass(); - JsonNode schemaNode; - // Maybe it already has annotated/statically configured serializer? - JsonSerializer ser = writer.getSerializer(); - - try { - if (ser == null) { // nope - Class serType = writer.getRawSerializationType(); - if (serType == null) { - serType = writer.getPropertyType(); - } - ser = provider.findValueSerializer(serType, writer); - } - boolean isOptional = !BeanSerializerBase.isPropertyRequired(writer, provider); - if (ser instanceof SchemaAware) { - schemaNode = ((SchemaAware) ser).getSchema(provider, hint, isOptional) ; - } else { - schemaNode = JsonSchema.getDefaultSchemaNode(); - } - } catch (JsonMappingException e) { - schemaNode = JsonSchema.getDefaultSchemaNode(); - // TODO: handle in better way (why not throw?) - } - propertiesNode.put(writer.getName(), schemaNode); - } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java index 56c456fd6d..3d87cc4118 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java @@ -1,14 +1,13 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; - -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; /** * Serializer used for primitive boolean, as well as java.util.Boolean @@ -41,8 +40,8 @@ public void serialize(Boolean value, JsonGenerator jgen, SerializerProvider prov } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("boolean", !_forPrimitive); + visitor.expectBooleanFormat(typeHint); } } \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/CalendarSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/CalendarSerializer.java index 847a93b298..eb4c77f702 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/CalendarSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/CalendarSerializer.java @@ -4,8 +4,8 @@ import java.text.DateFormat; import java.util.Calendar; -import com.fasterxml.jackson.core.*; - +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java index 2e85d39277..06e1b89a58 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java @@ -4,7 +4,8 @@ import java.util.Collection; import java.util.Iterator; -import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonSerializer; diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/DateSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/DateSerializer.java index 8834edd22d..6ea25e0921 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/DateSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/DateSerializer.java @@ -2,11 +2,11 @@ import java.io.IOException; import java.text.DateFormat; -import java.util.*; +import java.util.Date; -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; /** diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java index 59a2d5e650..424cebed8a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java @@ -1,7 +1,6 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Locale; @@ -10,8 +9,15 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonschema.types.JsonValueFormat; import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.util.StdDateFormat; @@ -100,17 +106,20 @@ public boolean isEmpty(T value) { protected abstract long _timestamp(T value); - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { //todo: (ryan) add a format for the date in the schema? boolean asNumber = _useTimestamp; if (!asNumber) { if (_customFormat == null) { - asNumber = provider.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + asNumber = visitor.getProvider().isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); } } - return createSchemaNode(asNumber ? "number" : "string", true); + if (asNumber) { + visitor.expectNumberFormat(typeHint).format(JsonValueFormat.UTC_MILLISEC); + } else { + visitor.expectStringFormat(typeHint).format(JsonValueFormat.DATE_TIME); + } } /* diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumMapSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumMapSerializer.java index faf39db6ee..92fdbb6913 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumMapSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumMapSerializer.java @@ -3,15 +3,20 @@ import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.*; +import java.util.EnumMap; +import java.util.Map; -import com.fasterxml.jackson.core.*; - - -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; -import com.fasterxml.jackson.databind.jsonschema.JsonSchema; -import com.fasterxml.jackson.databind.jsonschema.SchemaAware; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorAware; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -250,27 +255,31 @@ protected void serializeContentsUsing(EnumMap,?> value, JsonGe @SuppressWarnings("unchecked") @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - ObjectNode o = createSchemaNode("object", true); + JsonObjectFormatVisitor objectVisitor = visitor.expectObjectFormat(typeHint); if (typeHint instanceof ParameterizedType) { Type[] typeArgs = ((ParameterizedType) typeHint).getActualTypeArguments(); if (typeArgs.length == 2) { - JavaType enumType = provider.constructType(typeArgs[0]); - JavaType valueType = provider.constructType(typeArgs[1]); + JavaType enumType = visitor.getProvider().constructType(typeArgs[0]); + JavaType valueType = visitor.getProvider().constructType(typeArgs[1]); ObjectNode propsNode = JsonNodeFactory.instance.objectNode(); Class> enumClass = (Class>) enumType.getRawClass(); for (Enum enumValue : enumClass.getEnumConstants()) { - JsonSerializer ser = provider.findValueSerializer(valueType.getRawClass(), _property); - JsonNode schemaNode = (ser instanceof SchemaAware) ? - ((SchemaAware) ser).getSchema(provider, null) : - JsonSchema.getDefaultSchemaNode(); - propsNode.put(provider.getConfig().getAnnotationIntrospector().findEnumValue((Enum)enumValue), schemaNode); + JsonSerializer ser; + String name = visitor.getProvider().getConfig().getAnnotationIntrospector().findEnumValue((Enum)enumValue); + try { + ser = visitor.getProvider().findValueSerializer(valueType.getRawClass(), _property); + if (ser instanceof JsonFormatVisitorAware) { + objectVisitor.property(name, (JsonFormatVisitorAware) ser, valueType); + } + continue; + } catch (JsonMappingException e) { + //TODO: log error + } + objectVisitor.property(name); } - o.put("properties", propsNode); } } - return o; } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java index 3b908ee264..8573d172c0 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java @@ -1,19 +1,27 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; +import java.util.HashSet; +import java.util.Set; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat.Shape; -import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.io.SerializedString; - - -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.AnnotationIntrospector; +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.introspect.Annotated; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor; import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.util.EnumValues; @@ -141,23 +149,23 @@ public final void serialize(Enum en, JsonGenerator jgen, SerializerProvider p } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { // [JACKSON-684]: serialize as index? - if (_serializeAsIndex(provider)) { - return createSchemaNode("integer", true); - } - ObjectNode objectNode = createSchemaNode("string", true); - if (typeHint != null) { - JavaType type = provider.constructType(typeHint); - if (type.isEnumType()) { - ArrayNode enumNode = objectNode.putArray("enum"); - for (SerializedString value : _values.values()) { - enumNode.add(value.getValue()); - } - } - } - return objectNode; + if (visitor.getProvider().isEnabled(SerializationFeature.WRITE_ENUMS_USING_INDEX)) { + visitor.expectIntegerFormat(typeHint); + } else { + JsonStringFormatVisitor stringVisitor = visitor.expectStringFormat(typeHint); + if (typeHint != null) { + if (typeHint.isEnumType()) { + Set enums = new HashSet(); + for (SerializedString value : _values.values()) { + enums.add(value.getValue()); + } + stringVisitor.enumTypes(enums); + } + } + } } /* diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java index e1edbf6c4b..db7cd7597e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java @@ -3,9 +3,12 @@ import java.io.IOException; import java.util.EnumSet; -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; public class EnumSetSerializer diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java index 834a2e7488..8371f775ab 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java @@ -3,8 +3,8 @@ import java.io.IOException; import java.net.InetAddress; -import com.fasterxml.jackson.core.*; - +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java index aa65e10cf0..962864a839 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java @@ -3,8 +3,8 @@ import java.io.IOException; import java.util.Iterator; -import com.fasterxml.jackson.core.*; - +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonSerializer; diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java index 364957a53e..4557b8543c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java @@ -4,14 +4,19 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.lang.reflect.Type; -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; -import com.fasterxml.jackson.databind.jsonschema.JsonSchema; -import com.fasterxml.jackson.databind.jsonschema.SchemaAware; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorAware; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.BeanSerializer; import com.fasterxml.jackson.databind.ser.ContextualSerializer; @@ -31,7 +36,7 @@ @JacksonStdImpl public class JsonValueSerializer extends StdSerializer - implements ContextualSerializer, SchemaAware + implements ContextualSerializer, JsonFormatVisitorAware { protected final Method _accessorMethod; @@ -230,12 +235,13 @@ public void serializeWithType(Object bean, JsonGenerator jgen, SerializerProvide } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return (_valueSerializer instanceof SchemaAware) ? - ((SchemaAware) _valueSerializer).getSchema(provider, null) : - JsonSchema.getDefaultSchemaNode(); + if (_valueSerializer instanceof JsonFormatVisitorAware) { + ((JsonFormatVisitorAware) _valueSerializer).acceptJsonFormatVisitor(visitor, null); + } else { + visitor.expectAnyFormat(typeHint); + } } protected boolean isNaturalTypeWithStdHandling(Class rawType, JsonSerializer ser) diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java index 127d4a373d..95e8cadf9b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java @@ -1,16 +1,23 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; -import java.util.*; - -import com.fasterxml.jackson.core.*; - - -import com.fasterxml.jackson.databind.*; +import java.util.HashSet; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.AnnotationIntrospector; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.ContainerSerializer; import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; @@ -466,12 +473,9 @@ protected void serializeTypedFields(Map value, JsonGenerator jgen, Serializ } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - ObjectNode o = createSchemaNode("object", true); - //(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 "Entries" can't be determined. - return o; + visitor.expectObjectFormat(null); } /* diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NonTypedScalarSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NonTypedScalarSerializerBase.java index 16050e55b3..2d4da7f72c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NonTypedScalarSerializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NonTypedScalarSerializerBase.java @@ -2,8 +2,8 @@ import java.io.IOException; -import com.fasterxml.jackson.core.*; - +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NullSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NullSerializer.java index 75fcc0bc09..ec127f4b5b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NullSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NullSerializer.java @@ -1,12 +1,13 @@ package com.fasterxml.jackson.databind.ser.std; -import java.lang.reflect.Type; import java.io.IOException; -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; /** * This is a simple dummy serializer that will just output literal @@ -31,9 +32,8 @@ public void serialize(Object value, JsonGenerator jgen, SerializerProvider provi } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("null"); + visitor.expectNullFormat(typeHint); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java index 81d0bbb1eb..d33e01a5c8 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java @@ -1,14 +1,17 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Map; -import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; /** * Container class for serializers used for handling standard JDK-provided types. @@ -71,9 +74,9 @@ public void serialize(Integer value, JsonGenerator jgen, SerializerProvider prov } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("integer", true); + visitor.expectIntegerFormat(typeHint); } } @@ -98,9 +101,9 @@ public void serialize(Number value, JsonGenerator jgen, SerializerProvider provi } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("integer", true); + visitor.expectIntegerFormat(typeHint); } } @@ -120,9 +123,9 @@ public void serialize(Long value, JsonGenerator jgen, SerializerProvider provide } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("number", true); + visitor.expectNumberFormat(typeHint); } } @@ -142,9 +145,9 @@ public void serialize(Float value, JsonGenerator jgen, SerializerProvider provid } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("number", true); + visitor.expectNumberFormat(typeHint); } } @@ -171,9 +174,9 @@ public void serialize(Double value, JsonGenerator jgen, SerializerProvider provi } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("number", true); + visitor.expectNumberFormat(typeHint); } } @@ -219,9 +222,9 @@ public void serialize(Number value, JsonGenerator jgen, SerializerProvider provi } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("number", true); + visitor.expectNumberFormat(typeHint); } } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java index 29b301c0a7..7733aede29 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java @@ -2,20 +2,20 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Type; -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; -import com.fasterxml.jackson.databind.jsonschema.JsonSchema; -import com.fasterxml.jackson.databind.jsonschema.SchemaAware; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.ContainerSerializer; import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; -import com.fasterxml.jackson.databind.type.ArrayType; /** * Generic serializer for Object arrays (Object[]). @@ -295,27 +295,9 @@ public void serializeTypedContents(Object[] value, JsonGenerator jgen, Serialize } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - ObjectNode o = createSchemaNode("array", true); - if (typeHint != null) { - JavaType javaType = provider.constructType(typeHint); - if (javaType.isArrayType()) { - Class componentType = ((ArrayType) javaType).getContentType().getRawClass(); - // 15-Oct-2010, tatu: We can't serialize plain Object.class; but what should it produce here? Untyped? - if (componentType == Object.class) { - o.put("items", JsonSchema.getDefaultSchemaNode()); - } else { - JsonSerializer ser = provider.findValueSerializer(componentType, _property); - JsonNode schemaNode = (ser instanceof SchemaAware) ? - ((SchemaAware) ser).getSchema(provider, null) : - JsonSchema.getDefaultSchemaNode(); - o.put("items", schemaNode); - } - } - } - return o; + visitor.expectArrayFormat(typeHint).itemsFormat(_elementType); } protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/RawSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/RawSerializer.java index 75af722333..99850927c1 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/RawSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/RawSerializer.java @@ -1,12 +1,14 @@ package com.fasterxml.jackson.databind.ser.std; -import java.lang.reflect.Type; import java.io.IOException; -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; /** @@ -44,9 +46,8 @@ public void serializeWithType(T value, JsonGenerator jgen, SerializerProvider pr } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - // type not really known, but since it is a JSON string: - return createSchemaNode("string", true); + visitor.expectStringFormat(typeHint); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java index d1c2633fb1..98011dd66c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java @@ -1,22 +1,17 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import java.util.concurrent.atomic.AtomicReference; -import com.fasterxml.jackson.core.*; - - -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonSerializable; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; -import com.fasterxml.jackson.databind.jsonschema.JsonSerializableSchema; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.type.TypeFactory; /** * Generic handler for types that implement {@link JsonSerializable}. @@ -51,56 +46,8 @@ public final void serializeWithType(JsonSerializable value, JsonGenerator jgen, } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - ObjectNode objectNode = createObjectNode(); - String schemaType = "any"; - String objectProperties = null; - String itemDefinition = null; - if (typeHint != null) { - Class rawClass = TypeFactory.rawClass(typeHint); - if (rawClass.isAnnotationPresent(JsonSerializableSchema.class)) { - JsonSerializableSchema schemaInfo = rawClass.getAnnotation(JsonSerializableSchema.class); - schemaType = schemaInfo.schemaType(); - if (!JsonSerializableSchema.NO_VALUE.equals(schemaInfo.schemaObjectPropertiesDefinition())) { - objectProperties = schemaInfo.schemaObjectPropertiesDefinition(); - } - if (!JsonSerializableSchema.NO_VALUE.equals(schemaInfo.schemaItemDefinition())) { - itemDefinition = schemaInfo.schemaItemDefinition(); - } - } - } - /* 19-Mar-2012, tatu: geez, this is butt-ugly abonimation of code... - * really, really should not require back ref to an ObjectMapper. - */ - objectNode.put("type", schemaType); - if (objectProperties != null) { - try { - objectNode.put("properties", _getObjectMapper().readTree(objectProperties)); - } catch (IOException e) { - throw new JsonMappingException("Failed to parse @JsonSerializableSchema.schemaObjectPropertiesDefinition value"); - } - } - if (itemDefinition != null) { - try { - objectNode.put("items", _getObjectMapper().readTree(itemDefinition)); - } catch (IOException e) { - throw new JsonMappingException("Failed to parse @JsonSerializableSchema.schemaItemDefinition value"); - } - } - // always optional, no need to specify: - //objectNode.put("required", false); - return objectNode; + visitor.expectAnyFormat(typeHint); } - - private final static synchronized ObjectMapper _getObjectMapper() - { - ObjectMapper mapper = _mapperReference.get(); - if (mapper == null) { - mapper = new ObjectMapper(); - _mapperReference.set(mapper); - } - return mapper; } -} diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlDateSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlDateSerializer.java index a5d6fb56cd..fdbd2a0759 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlDateSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlDateSerializer.java @@ -1,14 +1,14 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; - -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonschema.types.JsonValueFormat; /** * Compared to regular {@link java.util.Date} serialization, we do use String @@ -29,9 +29,8 @@ public void serialize(java.sql.Date value, JsonGenerator jgen, SerializerProvide } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - //todo: (ryan) add a format for the date in the schema? - return createSchemaNode("string", true); + visitor.expectStringFormat(typeHint).format(JsonValueFormat.DATE_TIME); } } \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlTimeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlTimeSerializer.java index 8e12b425be..2800bcc679 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlTimeSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlTimeSerializer.java @@ -1,14 +1,14 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; - -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonschema.types.JsonValueFormat; @JacksonStdImpl public class SqlTimeSerializer @@ -24,8 +24,8 @@ public void serialize(java.sql.Time value, JsonGenerator jgen, SerializerProvide } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("string", true); + visitor.expectStringFormat(typeHint).format(JsonValueFormat.DATE_TIME); } } \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java index fece1eb39b..1aff32344f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java @@ -1,11 +1,10 @@ package com.fasterxml.jackson.databind.ser.std; -import java.lang.reflect.Type; -import java.util.*; +import java.util.Collection; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; /** * Intermediate base class for Lists, Collections and Arrays @@ -24,11 +23,9 @@ public boolean isEmpty(T value) { } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - ObjectNode o = createSchemaNode("array", true); - o.put("items", contentSchema()); - return o; + acceptContentVisitor(visitor.expectArrayFormat(typeHint)); } /* @@ -37,5 +34,5 @@ public JsonNode getSchema(SerializerProvider provider, Type typeHint) /********************************************************** */ - protected abstract JsonNode contentSchema(); + protected abstract void acceptContentVisitor(JsonArrayFormatVisitor visitor); } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java index c89cf48c97..beac71ed3e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java @@ -1,15 +1,19 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import java.util.HashMap; -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonschema.types.SchemaType; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.ContainerSerializer; import com.fasterxml.jackson.databind.type.TypeFactory; @@ -129,11 +133,9 @@ public void serializeContents(boolean[] value, JsonGenerator jgen, SerializerPro } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - ObjectNode o = createSchemaNode("array", true); - o.put("items", createSchemaNode("boolean")); - return o; + visitor.expectArrayFormat(typeHint).itemsFormat(SchemaType.BOOLEAN); } } @@ -175,12 +177,9 @@ public void serializeWithType(byte[] value, JsonGenerator jgen, SerializerProvid } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - ObjectNode o = createSchemaNode("array", true); - ObjectNode itemSchema = createSchemaNode("string"); //binary values written as strings? - o.put("items", itemSchema); - return o; + visitor.expectArrayFormat(typeHint).itemsFormat(SchemaType.STRING); } } @@ -241,12 +240,9 @@ public void serializeContents(short[] value, JsonGenerator jgen, SerializerProvi } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - //no "short" type defined by json - ObjectNode o = createSchemaNode("array", true); - o.put("items", createSchemaNode("integer")); - return o; + visitor.expectArrayFormat(typeHint).itemsFormat(SchemaType.INTEGER); } } @@ -308,13 +304,9 @@ private final void _writeArrayContents(JsonGenerator jgen, char[] value) } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - ObjectNode o = createSchemaNode("array", true); - ObjectNode itemSchema = createSchemaNode("string"); - itemSchema.put("type", "string"); - o.put("items", itemSchema); - return o; + visitor.expectArrayFormat(typeHint).itemsFormat(SchemaType.STRING); } } @@ -367,11 +359,9 @@ public void serializeContents(int[] value, JsonGenerator jgen, SerializerProvide } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - ObjectNode o = createSchemaNode("array", true); - o.put("items", createSchemaNode("integer")); - return o; + visitor.expectArrayFormat(typeHint).itemsFormat(SchemaType.INTEGER); } } @@ -433,11 +423,9 @@ public void serializeContents(long[] value, JsonGenerator jgen, SerializerProvid } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - ObjectNode o = createSchemaNode("array", true); - o.put("items", createSchemaNode("number", true)); - return o; + visitor.expectArrayFormat(typeHint).itemsFormat(SchemaType.NUMBER); } } @@ -500,11 +488,9 @@ public void serializeContents(float[] value, JsonGenerator jgen, SerializerProvi } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - ObjectNode o = createSchemaNode("array", true); - o.put("items", createSchemaNode("number")); - return o; + visitor.expectArrayFormat(typeHint).itemsFormat(SchemaType.NUMBER); } } @@ -557,11 +543,9 @@ public void serializeContents(double[] value, JsonGenerator jgen, SerializerProv } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - ObjectNode o = createSchemaNode("array", true); - o.put("items", createSchemaNode("number")); - return o; + visitor.expectArrayFormat(typeHint).itemsFormat(SchemaType.NUMBER); } } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdContainerSerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdContainerSerializers.java index 5785481faa..c3533e1d50 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdContainerSerializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdContainerSerializers.java @@ -1,11 +1,12 @@ package com.fasterxml.jackson.databind.ser.std; -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.ContainerSerializer; import com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer; import com.fasterxml.jackson.databind.ser.impl.IteratorSerializer; -import com.fasterxml.jackson.databind.ser.std.CollectionSerializer; /** * Dummy container class to group standard container serializers: serializers diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java index 0f2a52080f..0270ae8c28 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java @@ -1,13 +1,23 @@ package com.fasterxml.jackson.databind.ser.std; -import java.io.*; -import java.lang.reflect.Type; -import java.util.*; -import java.util.concurrent.atomic.*; - -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.*; +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.Currency; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.ser.BasicSerializerFactory; import com.fasterxml.jackson.databind.util.Provider; @@ -77,9 +87,9 @@ public void serialize(AtomicBoolean value, JsonGenerator jgen, SerializerProvide } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("boolean", true); + visitor.expectBooleanFormat(typeHint); } } @@ -96,9 +106,9 @@ public void serialize(AtomicInteger value, JsonGenerator jgen, SerializerProvide } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("integer", true); + visitor.expectIntegerFormat(typeHint); } } @@ -115,9 +125,9 @@ public void serialize(AtomicLong value, JsonGenerator jgen, SerializerProvider p } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("integer", true); + visitor.expectIntegerFormat(typeHint); } } @@ -134,9 +144,9 @@ public void serialize(AtomicReference value, JsonGenerator jgen, SerializerPr } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("any", true); + visitor.expectAnyFormat(typeHint); } } @@ -163,9 +173,9 @@ public void serialize(File value, JsonGenerator jgen, SerializerProvider provide } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("string", true); + visitor.expectStringFormat(typeHint); } } @@ -186,9 +196,9 @@ public void serialize(Class value, JsonGenerator jgen, SerializerProvider pro } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("string", true); + visitor.expectStringFormat(typeHint); } } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java index f8401f3d9b..1d64986d84 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java @@ -1,15 +1,13 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import java.util.Date; -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; /** * Specialized serializer that can be used as the generic key @@ -35,9 +33,8 @@ public void serialize(Object value, JsonGenerator jgen, SerializerProvider provi } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("string"); + visitor.expectStringFormat(typeHint); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java index fe73e800b8..bcb91e89d9 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java @@ -4,8 +4,8 @@ import java.util.Calendar; import java.util.Date; -import com.fasterxml.jackson.core.*; - +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java index aa1ff3e6d0..1387e3d813 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java @@ -1,13 +1,12 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; public abstract class StdScalarSerializer @@ -44,9 +43,8 @@ public void serializeWithType(T value, JsonGenerator jgen, SerializerProvider pr } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("string", true); + visitor.expectAnyFormat(typeHint); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java index ba3e3b8b79..abd487dc21 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java @@ -2,24 +2,27 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Type; -import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; -import com.fasterxml.jackson.databind.jsonschema.SchemaAware; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorAware; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; /** * Base class used by all standard serializers, and can also * be used for custom serializers (in fact, this is the recommended * base class to use). - * Provides convenience methods for implementing {@link SchemaAware} + * Provides convenience methods for implementing {@link JsonFormatVisitorAware} */ public abstract class StdSerializer extends JsonSerializer - implements SchemaAware + implements JsonFormatVisitorAware { /** * Nominal type supported, usually declared type of @@ -71,57 +74,19 @@ public abstract void serialize(T value, JsonGenerator jgen, SerializerProvider p /* /********************************************************** - /* Helper methods for JSON Schema generation + /* Helper methods for JSON JsonSchema generation /********************************************************** */ /** - * Default implementation simply claims type is "string"; usually + * Default implementation specifies no format. This behavior is usually * overriden by custom serializers. */ // @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException - { - return createSchemaNode("string"); - } - - /** - * Default implementation simply claims type is "string"; usually - * overriden by custom serializers. - */ -// @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint, boolean isOptional) - throws JsonMappingException - { - ObjectNode schema = (ObjectNode) getSchema(provider, typeHint); - if (!isOptional) { - schema.put("required", !isOptional); - } - return schema; - } - - protected ObjectNode createObjectNode() { - return JsonNodeFactory.instance.objectNode(); + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { + visitor.expectAnyFormat(typeHint); } - - protected ObjectNode createSchemaNode(String type) - { - ObjectNode schema = createObjectNode(); - schema.put("type", type); - return schema; - } - - protected ObjectNode createSchemaNode(String type, boolean isOptional) - { - ObjectNode schema = createSchemaNode(type); - // as per [JACKSON-563]. Note that 'required' defaults to false - if (!isOptional) { - schema.put("required", !isOptional); - } - return schema; - } - + /* /********************************************************** /* Helper methods for exception handling diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StringSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StringSerializer.java index eb142f5d15..ec2911e0be 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StringSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StringSerializer.java @@ -1,13 +1,13 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; /** * This is the special serializer for regular {@link java.lang.String}s. @@ -37,8 +37,8 @@ public void serialize(String value, JsonGenerator jgen, SerializerProvider provi } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("string", true); + visitor.expectStringFormat(typeHint); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/TimeZoneSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/TimeZoneSerializer.java index 71f2bf581a..17eaa67c1c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/TimeZoneSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/TimeZoneSerializer.java @@ -3,8 +3,8 @@ import java.io.IOException; import java.util.TimeZone; -import com.fasterxml.jackson.core.*; - +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java index bb65ca3721..1b5acabc65 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java @@ -1,14 +1,13 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; /** @@ -74,10 +73,9 @@ public void serializeWithType(Object value, JsonGenerator jgen, SerializerProvid } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { - return createSchemaNode("string", true); + visitor.expectStringFormat(typeHint); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java index 73eb789dd6..07a8f2edfd 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java @@ -1,13 +1,13 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.util.TokenBuffer; @@ -53,12 +53,12 @@ public final void serializeWithType(TokenBuffer value, JsonGenerator jgen, Seria } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { /* 01-Jan-2010, tatu: Not 100% sure what we should say here: * type is basically not known. This seems closest * approximation */ - return createSchemaNode("any", true); + visitor.expectAnyFormat(typeHint); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestGenerateJsonSchema.java b/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestGenerateJsonSchema.java index 58c9534788..43c0f4dd89 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestGenerateJsonSchema.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestGenerateJsonSchema.java @@ -1,13 +1,17 @@ package com.fasterxml.jackson.databind.jsonschema; import java.util.Collection; +import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.annotation.JsonFilter; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.jsonschema.factories.SchemaFactory; +import com.fasterxml.jackson.databind.jsonschema.factories.SchemaFactoryProvider; +import com.fasterxml.jackson.databind.jsonschema.types.ObjectSchema; +import com.fasterxml.jackson.databind.jsonschema.types.JsonSchema; +import com.fasterxml.jackson.databind.jsonschema.types.ArraySchema.Items; import com.fasterxml.jackson.databind.ser.FilterProvider; import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; @@ -30,7 +34,7 @@ public static class SimpleBean private String property2; private String[] property3; private Collection property4; - @JsonProperty(required=true) + @JsonProperty(required = true) private String property5; public int getProperty1() @@ -88,7 +92,7 @@ public class TrivialBean { public String name; } - @JsonSerializableSchema(id="myType") + //@JsonSerializableSchema(id="myType") public class BeanWithId { public String value; } @@ -107,7 +111,10 @@ public class BeanWithId { public void testGeneratingJsonSchema() throws Exception { - JsonSchema jsonSchema = MAPPER.generateJsonSchema(SimpleBean.class); + ObjectMapper m = new ObjectMapper(); + SchemaFactoryProvider visitor = new SchemaFactoryProvider(); + m.acceptJsonFormatVisitor(SimpleBean.class, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); assertNotNull(jsonSchema); @@ -116,33 +123,45 @@ public void testGeneratingJsonSchema() assertFalse(jsonSchema.equals(null)); assertFalse(jsonSchema.equals("foo")); - // other basic things - assertNotNull(jsonSchema.toString()); - assertNotNull(JsonSchema.getDefaultSchemaNode()); - - ObjectNode root = jsonSchema.getSchemaNode(); - assertEquals("object", root.get("type").asText()); - assertEquals(false, root.path("required").booleanValue()); - JsonNode propertiesSchema = root.get("properties"); - assertNotNull(propertiesSchema); - JsonNode property1Schema = propertiesSchema.get("property1"); - assertNotNull(property1Schema); - assertEquals("integer", property1Schema.get("type").asText()); - assertEquals(false, property1Schema.path("required").booleanValue()); - JsonNode property2Schema = propertiesSchema.get("property2"); - assertNotNull(property2Schema); - assertEquals("string", property2Schema.get("type").asText()); - assertEquals(false, property2Schema.path("required").booleanValue()); - JsonNode property3Schema = propertiesSchema.get("property3"); - assertNotNull(property3Schema); - assertEquals("array", property3Schema.get("type").asText()); - assertEquals(false, property3Schema.path("required").booleanValue()); - assertEquals("string", property3Schema.get("items").get("type").asText()); - JsonNode property4Schema = propertiesSchema.get("property4"); - assertNotNull(property4Schema); - assertEquals("array", property4Schema.get("type").asText()); - assertEquals(false, property4Schema.path("required").booleanValue()); - assertEquals("number", property4Schema.get("items").get("type").asText()); + assertTrue(jsonSchema.isObjectSchema()); + ObjectSchema object = jsonSchema.asObjectSchema(); + assertNotNull(object); + Map properties = object.getProperties(); + assertNotNull(properties); + JsonSchema prop1 = properties.get("property1"); + assertNotNull(prop1); + assertTrue(prop1.isIntegerSchema()); + assertNull(prop1.getRequired()); + + JsonSchema prop2 = properties.get("property2"); + assertNotNull(prop2); + assertTrue(prop2.isStringSchema()); + assertNull(prop2.getRequired()); + + JsonSchema prop3 = properties.get("property3"); + assertNotNull(prop3); + assertTrue(prop3.isArraySchema()); + assertNull(prop3.getRequired()); + Items items = prop3.asArraySchema().getItems(); + assertTrue(items.isSingleItems()); + JsonSchema itemType = items.asSingleItems().getSchema(); + assertNotNull(itemType); + assertTrue(itemType.isStringSchema()); + + JsonSchema prop4 = properties.get("property4"); + assertNotNull(prop4); + assertTrue(prop4.isArraySchema()); + assertNull(prop4.getRequired()); + items = prop4.asArraySchema().getItems(); + assertTrue(items.isSingleItems()); + itemType = items.asSingleItems().getSchema(); + assertNotNull(itemType); + assertTrue(itemType.isNumberSchema()); + + JsonSchema prop5 = properties.get("property5"); + assertNotNull(prop5); + assertTrue(prop5.getRequired()); + } @JsonFilter("filteredBean") @@ -167,10 +186,19 @@ protected static class FilteredBean { public void testGeneratingJsonSchemaWithFilters() throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.setFilters(secretFilterProvider); - JsonSchema schema = mapper.generateJsonSchema(FilteredBean.class); - JsonNode node = schema.getSchemaNode().get("properties"); - assertTrue(node.has("obvious")); - assertFalse(node.has("secret")); + SchemaFactoryProvider visitor = new SchemaFactoryProvider(); + mapper.acceptJsonFormatVisitor(FilteredBean.class, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + assertNotNull(jsonSchema); + assertTrue(jsonSchema.isObjectSchema()); + ObjectSchema object = jsonSchema.asObjectSchema(); + assertNotNull(object); + Map properties = object.getProperties(); + assertNotNull(properties); + JsonSchema obvious = properties.get("obvious"); + assertNotNull(obvious); + assertTrue(obvious.isStringSchema()); + assertNull(properties.get("secret")); } /** @@ -180,14 +208,16 @@ public void testGeneratingJsonSchemaWithFilters() throws Exception { public void testSchemaSerialization() throws Exception { - JsonSchema jsonSchema = MAPPER.generateJsonSchema(SimpleBean.class); - Map result = writeAndMap(MAPPER, jsonSchema); - assertNotNull(result); - // no need to check out full structure, just basics... - assertEquals("object", result.get("type")); - // only add 'required' if it is true... - assertNull(result.get("required")); - assertNotNull(result.get("properties")); + SchemaFactoryProvider visitor = new SchemaFactoryProvider(); + MAPPER.acceptJsonFormatVisitor(SimpleBean.class, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + Map result = writeAndMap(MAPPER, jsonSchema); + assertNotNull(result); + // no need to check out full structure, just basics... + assertEquals("object", result.get("type")); + // only add 'required' if it is true... + assertNull(result.get("required")); + assertNotNull(result.get("properties")); } public void testInvalidCall() @@ -195,7 +225,9 @@ public void testInvalidCall() { // not ok to pass null try { - MAPPER.generateJsonSchema(null); + SchemaFactoryProvider visitor = new SchemaFactoryProvider(); + MAPPER.acceptJsonFormatVisitor(null, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); fail("Should have failed"); } catch (IllegalArgumentException iae) { verifyException(iae, "class must be provided"); @@ -207,19 +239,30 @@ public void testInvalidCall() */ public void testThatObjectsHaveNoItems() throws Exception { - JsonSchema jsonSchema = MAPPER.generateJsonSchema(TrivialBean.class); - String json = jsonSchema.toString().replaceAll("\"", "'"); + SchemaFactoryProvider visitor = new SchemaFactoryProvider(); + MAPPER.acceptJsonFormatVisitor(TrivialBean.class, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + Map result = writeAndMap(MAPPER, jsonSchema); // can we count on ordering being stable? I think this is true with current ObjectNode impl // as perh [JACKSON-563]; 'required' is only included if true - assertEquals("{'type':'object','properties':{'name':{'type':'string'}}}", - json); + assertFalse(result.containsKey("items")); + } - public void testSchemaId() throws Exception + @SuppressWarnings({ "unchecked", "rawtypes", "serial" }) + public void testSchemaId() throws Exception { - JsonSchema jsonSchema = MAPPER.generateJsonSchema(BeanWithId.class); - String json = jsonSchema.toString().replaceAll("\"", "'"); - assertEquals("{'type':'object','id':'myType','properties':{'value':{'type':'string'}}}", - json); + SchemaFactoryProvider visitor = new SchemaFactoryProvider(); + MAPPER.acceptJsonFormatVisitor(BeanWithId.class, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + Map result = writeAndMap(MAPPER, jsonSchema); + + assertEquals(new HashMap() {{ + put("type", "object"); + put("properties", + new HashMap(){{ put("value", + new HashMap() {{ put("type", "string");}} + );}} + );}}, result); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestReadJsonSchema.java b/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestReadJsonSchema.java index ef3513b63d..e05e887912 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestReadJsonSchema.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestReadJsonSchema.java @@ -3,7 +3,9 @@ import java.util.*; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.jsonschema.JsonSchema; +import com.fasterxml.jackson.databind.jsonschema.factories.SchemaFactory; +import com.fasterxml.jackson.databind.jsonschema.factories.SchemaFactoryProvider; +import com.fasterxml.jackson.databind.jsonschema.types.JsonSchema; /** * Trivial test to ensure {@link JsonSchema} can be also deserialized @@ -50,12 +52,14 @@ static class Schemable { public void testDeserializeSimple() throws Exception { ObjectMapper mapper = new ObjectMapper(); - JsonSchema schema = mapper.generateJsonSchema(Schemable.class); - assertNotNull(schema); + SchemaFactoryProvider visitor = new SchemaFactoryProvider(); + mapper.acceptJsonFormatVisitor(Schemable.class, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + assertNotNull(jsonSchema); - String schemaStr = mapper.writeValueAsString(schema); + String schemaStr = mapper.writeValueAsString(jsonSchema); assertNotNull(schemaStr); JsonSchema result = mapper.readValue(schemaStr, JsonSchema.class); - assertEquals("Trying to read from '"+schemaStr+"'", schema, result); + assertEquals("Trying to read from '"+schemaStr+"'", jsonSchema, result); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestSimpleModule.java b/src/test/java/com/fasterxml/jackson/databind/module/TestSimpleModule.java index 1400401357..a92ccd5d71 100644 --- a/src/test/java/com/fasterxml/jackson/databind/module/TestSimpleModule.java +++ b/src/test/java/com/fasterxml/jackson/databind/module/TestSimpleModule.java @@ -1,13 +1,13 @@ package com.fasterxml.jackson.databind.module; import java.io.IOException; -import java.lang.reflect.Type; import java.util.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.module.SimpleDeserializers; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.module.SimpleSerializers; @@ -52,8 +52,8 @@ public void serialize(CustomBean value, JsonGenerator jgen, SerializerProvider p } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { - return null; + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { + visitor.expectAnyFormat(typeHint); } } @@ -86,8 +86,8 @@ public void serialize(SimpleEnum value, JsonGenerator jgen, SerializerProvider p } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { - return null; + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { + visitor.expectAnyFormat(typeHint); } }