diff --git a/release-notes/CREDITS b/release-notes/CREDITS index 943badb6..1c91220a 100644 --- a/release-notes/CREDITS +++ b/release-notes/CREDITS @@ -22,3 +22,14 @@ Izek Greenfield (igreenfield@github) #64: Add support to oneOf (2.6.0) +Sebastien Briquet (sebfz1@github) + +#67: Unable to deserialize (extended/custom) Schema + (2.6.0) + + +David Semke (dsemke@github) + +#69: Add support for @Pattern annotations in String schemas + (2.6.0) + diff --git a/release-notes/VERSION b/release-notes/VERSION index 53c8c1a4..3bf66542 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -9,6 +9,10 @@ Project: jackson-module-jsonSchema #60: Add `readonly` property to `JsonSchema` #64: Add support to oneOf (contributed by Izek K, igreenfield@github) +#67: Unable to deserialize (extended/custom) Schema + (reported by Sebastian B) +#69: Add support for @Pattern annotations in String schemas + (contributed by David S) - Added `JsonSchemaGenerator(ObjectWriter)` to allow use of (re-)configured `ObjectWriter` instead of `ObjectMapper` which can not be configured. diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchema.java index 11ccd996..25726a51 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchema.java @@ -318,27 +318,27 @@ public String getDescription() { } @JsonIgnore - public abstract JsonFormatTypes getType(); + public abstract JsonFormatTypes 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 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 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}. diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchemaIdResolver.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchemaIdResolver.java index 58ff1149..8a30b00a 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchemaIdResolver.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchemaIdResolver.java @@ -1,32 +1,30 @@ package com.fasterxml.jackson.module.jsonSchema; +import java.util.Arrays; + import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; + import com.fasterxml.jackson.databind.DatabindContext; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase; -import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.module.jsonSchema.types.*; +/** + * Type id resolver needed to support polymorphic (de)serialization of all kinds of + * {@link JsonSchema} instances. + * Note that to support custom types, you will need to sub-lcass this resolver + * and override at least {@link #idFromValue(Object)}, {@link #idFromValueAndType(Object, Class)} and + * {@link #typeFromId(String)} methods; as well as associate this resolver using + * {@link JsonTypeInfo} annotation on all custom {@link JsonSchema} implementation classes. + */ public class JsonSchemaIdResolver extends TypeIdResolverBase { - /* This is Wrong: should not use defaultInstance() for anything. - * But has to work for now... - */ - 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); - public JsonSchemaIdResolver() { } - + @Override public String idFromValue(Object value) { - if ( value instanceof JsonSchema) { + if (value instanceof JsonSchema) { return ((JsonSchema)value).getType().value(); } return null; @@ -38,19 +36,33 @@ public String idFromValueAndType(Object value, Class suggestedType) { } @Override - public JavaType typeFromId(DatabindContext context, String id) { - switch (JsonFormatTypes.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; - } + public JavaType typeFromId(DatabindContext ctxt, String id) + { + JsonFormatTypes stdType = JsonFormatTypes.forValue(id); + if (stdType != null) { + switch (stdType) { + case ARRAY: + return ctxt.constructType(ArraySchema.class); + case BOOLEAN: + return ctxt.constructType(BooleanSchema.class); + case INTEGER: + return ctxt.constructType(IntegerSchema.class); + case NULL: + return ctxt.constructType(NullSchema.class); + case NUMBER: + return ctxt.constructType(NumberSchema.class); + case OBJECT: + return ctxt.constructType(ObjectSchema.class); + case STRING: + return ctxt.constructType(StringSchema.class); + case ANY: + default: + return ctxt.constructType(AnySchema.class); + } + } + // Not a standard type; should use a custom sub-type impl + throw new IllegalArgumentException("Can not resolve JsonSchema 'type' id of \""+id + +"\", not recognized as one of standard values: "+Arrays.asList(JsonFormatTypes.values())); } @Override @@ -65,4 +77,4 @@ public void init(JavaType baseType) { } public String idFromBaseType() { return null; } - } \ No newline at end of file + } diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/CustomSchemaReadTest.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/CustomSchemaReadTest.java new file mode 100644 index 00000000..2353a6db --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/CustomSchemaReadTest.java @@ -0,0 +1,55 @@ +package com.fasterxml.jackson.module.jsonSchema; + +import com.fasterxml.jackson.databind.DatabindContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver; +import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; + +public class CustomSchemaReadTest extends SchemaTestBase +{ + @JsonTypeIdResolver(MyResolver.class) + public static class MySchema extends ObjectSchema { + } + + static class MyResolver extends JsonSchemaIdResolver + { + @Override + public String idFromValue(Object value) { + if (value instanceof MySchema) { + return "CUSTOM"; + } + return super.idFromValue(value); + } + + @Override + public String idFromValueAndType(Object value, Class suggestedType) { + if (value instanceof MySchema) { + return "CUSTOM"; + } + return super.idFromValueAndType(value, suggestedType); + } + + @Override + public JavaType typeFromId(DatabindContext ctxt, String id) { + if ("CUSTOM".equals(id)) { + return ctxt.constructType(MySchema.class); + } + return super.typeFromId(ctxt, id); + } + } + + // [module-jsonSchema#67] + public void testSchema() throws Exception + { + String input = "{ \"type\" : \"CUSTOM\" , \"id\" : \"7a2e8538-196b-423e-b714-13515848ec0c\" , \"description\" : \"My Schema\" , \"title\" : \"my-json-schema\" , \"properties\" : { \"myarray\" : { \"type\" : \"array\" , \"required\" : true , \"title\" : \"my property #2\" , \"items\" : { \"type\" : \"string\"} , \"maxItems\" : 5} , \"mystring\" : { \"type\" : \"string\" , \"required\" : true , \"title\" : \"my property #1\" , \"format\" : \"REGEX\" , \"pattern\" : \"\\\\w+\"} , \"myobject\" : { \"type\" : \"object\" , \"required\" : true , \"title\" : \"my property #3\" , \"properties\" : { \"subprop\" : { \"type\" : \"string\" , \"required\" : true , \"title\" : \"sub property #1\" , \"format\" : \"REGEX\" , \"pattern\" : \"\\\\w{3}\"}}}}}"; + + ObjectMapper mapper = new ObjectMapper(); + + // ObjectSchema schema = mapper.readValue(json, ObjectSchema.class); // works + MySchema schema = mapper.readValue(input, MySchema.class); // fails + + String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema); + assertNotNull(json); + } +}