Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring: Validation Groups, v3 and v4 Required support, Chaining of processors #125

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ target
*.ipr
*.iws
.idea
/bin/
25 changes: 21 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jsonSchema</artifactId>
<name>jackson-module-jsonSchema</name>
<version>2.9.3-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<packaging>bundle</packaging>
<description>Add-on module for Jackson (http://jackson.codehaus.org) to support
JSON Schema (http://tools.ietf.org/html/draft-zyp-json-schema-03) version 3 generation.
JSON Schema http://json-schema.org/ Currently v3 and start of v4 support.
</description>
<url>https://github.com/FasterXML/jackson-module-jsonSchema</url>
<scm>
Expand Down Expand Up @@ -49,15 +49,32 @@ JSON Schema (http://tools.ietf.org/html/draft-zyp-json-schema-03) version 3 gene
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
<version>2.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.4.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b08</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
115 changes: 91 additions & 24 deletions src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchema.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
package com.fasterxml.jackson.module.jsonSchema;

import com.fasterxml.jackson.annotation.*;
import java.util.LinkedHashMap;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
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.BeanProperty;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
import com.fasterxml.jackson.module.jsonSchema.types.*;
import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion;
import com.fasterxml.jackson.module.jsonSchema.types.AnySchema;
import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema;
import com.fasterxml.jackson.module.jsonSchema.types.BooleanSchema;
import com.fasterxml.jackson.module.jsonSchema.types.ContainerTypeSchema;
import com.fasterxml.jackson.module.jsonSchema.types.IntegerSchema;
import com.fasterxml.jackson.module.jsonSchema.types.NullSchema;
import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema;
import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema;
import com.fasterxml.jackson.module.jsonSchema.types.SimpleTypeSchema;
import com.fasterxml.jackson.module.jsonSchema.types.StringSchema;
import com.fasterxml.jackson.module.jsonSchema.types.UnionTypeSchema;
import com.fasterxml.jackson.module.jsonSchema.types.ValueTypeSchema;

/**
* The type wraps the json schema specification at :
Expand All @@ -33,7 +53,7 @@
* "id":{
* "type":"number",
* "description":"Product identifier",
* "required":true
* "required":true
* },
* "name":{
* "description":"Name of the product",
Expand Down Expand Up @@ -73,6 +93,21 @@
@JsonTypeIdResolver(JsonSchemaIdResolver.class)
public abstract class JsonSchema
{
@JsonIgnore
protected JsonSchemaVersion version;

protected JsonSchema() {
//jackson deserialization only
}

protected JsonSchema(JsonSchemaVersion version) {
this.version = version;
}

protected JsonSchema(JsonSchemaVersion version, boolean set$Schema) {
this.version = version;
}

/**
* 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
Expand Down Expand Up @@ -141,11 +176,15 @@ public abstract class JsonSchema
*/
private JsonSchema[] extendsextends;

/**
* This attribute indicates if the instance must have a value, and not be
* undefined. This is false by default, making the instance optional.
*/
@JsonProperty
/**
* This attribute indicates if the instance must have a value, and not be
* undefined. This is false by default, making the instance optional.
* Available in Draft V3 spec ONLY.
*
* @deprecated Since 2.9 - Use setRequired on ObjectSchema from Draft V4 onwards.
*/
@Deprecated
@JsonProperty
private Boolean required = null;

/**
Expand All @@ -161,7 +200,10 @@ public abstract class JsonSchema
*/
private String description;

protected JsonSchema() { }
/**
* Map to hold items that are not part of the official spec but may want to be added.
*/
private Map<String, String> nonStandardProperties = new LinkedHashMap<>();

/**
* Attempt to return this JsonSchema as an {@link AnySchema}
Expand Down Expand Up @@ -279,7 +321,11 @@ public ValueTypeSchema asValueTypeSchema() {
return null;
}

public String getId() {
public JsonSchemaVersion getVersion() {
return version;
}

public String getId() {
return id;
}

Expand Down Expand Up @@ -311,6 +357,15 @@ public String getDescription() {
return description;
}

@JsonAnyGetter
public Map<String, String> getNonStandardProperties() {
return nonStandardProperties;
}

public String getNonStandardProperty(String propertyName) {
return nonStandardProperties.get(propertyName);
}

@JsonIgnore
public abstract JsonFormatTypes getType();

Expand Down Expand Up @@ -441,6 +496,9 @@ public boolean isValueTypeSchema() {

public void set$schema(String $schema) {
this.$schema = $schema;
if (version == null) {
this.version = JsonSchemaVersion.fromSchemaString($schema).orElse(null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why doesn't method just return null?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that version isn't part of the JsonSchema so it is annotated with JsonIgnore. So when deserializing the version isn't set. So what this is doing is deriving the version from the schema. Thoughts on a better way to do this? Perhaps with a custom deserializer?

Copy link
Author

@ammerritt ammerritt Nov 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fromSchemaString returns an Optional so that you can set it to a default value if you wanted.

IE
JsonSchemaVersion.fromSchemaString($schema).orElse(JsonSchemaVersion.DRAFT_V4);
OR in the future maybe:
JsonSchemaVersion.fromSchemaString($schema).orElse(JsonSchemaVersion.DEFAULT); where DEFAULT could be the primarily or most recent supported version

}
}

public void setDisallow(JsonSchema[] disallow) {
Expand All @@ -456,6 +514,9 @@ public void setId(String id) {
}

public void setRequired(Boolean required) {
if (!JsonSchemaVersion.DRAFT_V3.equals(version)) {
throw new RuntimeException("You can only set the required boolean on Draft V3. You have: " + version);
}
this.required = required;
}

Expand All @@ -467,6 +528,11 @@ public void setDescription(String description) {
this.description = description;
}

@JsonAnySetter
public void addNonStandardProperty(String key, String value) {
nonStandardProperties.put(key, value);
}

/**
* Override this to add information specific to the property of bean
* For example, bean validation annotations could be used to specify
Expand All @@ -478,33 +544,34 @@ public void enrichWithBeanProperty(BeanProperty beanProperty) {
}

/**
* 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(JsonFormatTypes format)
* Create a schema which verifies only that an object is of the given format.
* @param jsonVersion
* @param format the format to expect
* @return the schema verifying the given format
*/
public static JsonSchema minimalForFormat(JsonSchemaVersion jsonVersion, JsonFormatTypes format)
{
if (format != null) {
switch (format) {
case ARRAY:
return new ArraySchema();
return new ArraySchema(jsonVersion);
case OBJECT:
return new ObjectSchema();
return new ObjectSchema(jsonVersion);
case BOOLEAN:
return new BooleanSchema();
return new BooleanSchema(jsonVersion);
case INTEGER:
return new IntegerSchema();
return new IntegerSchema(jsonVersion);
case NUMBER:
return new NumberSchema();
return new NumberSchema(jsonVersion);
case STRING:
return new StringSchema();
return new StringSchema(jsonVersion);
case NULL:
return new NullSchema();
return new NullSchema(jsonVersion);
case ANY:
default:
}
}
return new AnySchema();
return new AnySchema(jsonVersion);
}

@Override
Expand All @@ -522,7 +589,7 @@ protected boolean _equals(JsonSchema that)

// 27-Apr-2015, tatu: Should not need to check type explicitly
// && equals(getType(), getType())
&& equals(getRequired(), that.getRequired())
&& ((JsonSchemaVersion.DRAFT_V3.equals(version)) ? equals(getRequired(), that.getRequired()) : true)
&& equals(getReadonly(), that.getReadonly())
&& equals(get$ref(), that.get$ref())
&& equals(get$schema(), that.get$schema())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.fasterxml.jackson.module.jsonSchema.annotation;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({ METHOD, FIELD, PARAMETER, TYPE })
@Retention(RUNTIME)
public @interface JsonSchemaTitle {
String value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.fasterxml.jackson.module.jsonSchema.annotation;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
* Meant to add items to the schema that are not currently part of the schema.
*
* @author amerritt
*/
@Target({ METHOD, FIELD, PARAMETER, TYPE })
@Retention(RUNTIME)
public @interface NonStandardProperties {
NonStandardProperty[] value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.fasterxml.jackson.module.jsonSchema.annotation;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
* Meant to add items to the schema that are not currently part of the schema.
*
* @author amerritt
*/
@Target({ METHOD, FIELD, PARAMETER, TYPE })
@Retention(RUNTIME)
@Repeatable(NonStandardProperties.class)
public @interface NonStandardProperty {
String name();
String value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
import com.fasterxml.jackson.module.jsonSchema.annotation.JsonHyperSchema;
import com.fasterxml.jackson.module.jsonSchema.annotation.Link;
import com.fasterxml.jackson.module.jsonSchema.factories.*;
import com.fasterxml.jackson.module.jsonSchema.factories.ArrayVisitor;
import com.fasterxml.jackson.module.jsonSchema.factories.ObjectVisitor;
import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper;
import com.fasterxml.jackson.module.jsonSchema.factories.VisitorContext;
import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory;
import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion;
import com.fasterxml.jackson.module.jsonSchema.types.LinkDescriptionObject;
import com.fasterxml.jackson.module.jsonSchema.types.ReferenceSchema;
import com.fasterxml.jackson.module.jsonSchema.types.SimpleTypeSchema;
Expand All @@ -23,27 +28,37 @@
public class HyperSchemaFactoryWrapper extends SchemaFactoryWrapper {

private boolean ignoreDefaults = true;
private JsonSchemaVersion version;

private static class HyperSchemaFactoryWrapperFactory extends WrapperFactory {
public HyperSchemaFactoryWrapperFactory(JsonSchemaVersion version) {
super(version);
}

@Override
public SchemaFactoryWrapper getWrapper(SerializerProvider p) {
return new HyperSchemaFactoryWrapper(p);
HyperSchemaFactoryWrapper hsfw = new HyperSchemaFactoryWrapper();
hsfw.setProvider(p);
return hsfw;
};

@Override
public SchemaFactoryWrapper getWrapper(SerializerProvider p, VisitorContext rvc)
{
return new HyperSchemaFactoryWrapper(p)
.setVisitorContext(rvc);
HyperSchemaFactoryWrapper hsfw = new HyperSchemaFactoryWrapper();
hsfw.setVisitorContext(rvc);
hsfw.setProvider(p);
return hsfw;
}
};

public HyperSchemaFactoryWrapper() {
super(new HyperSchemaFactoryWrapperFactory());
this(JsonSchemaVersion.DRAFT_V3);
}

public HyperSchemaFactoryWrapper(SerializerProvider p) {
super(p, new HyperSchemaFactoryWrapperFactory());
public HyperSchemaFactoryWrapper(JsonSchemaVersion version) {
super(new HyperSchemaFactoryWrapperFactory(version));
this.version = version;
}

@Override
Expand Down Expand Up @@ -112,7 +127,7 @@ private JsonSchema fetchSchema(Class<?> targetSchema) {
if (visitorContext != null) {
String seenSchemaUri = visitorContext.getSeenSchemaUri(targetType);
if (seenSchemaUri != null) {
return new ReferenceSchema(seenSchemaUri);
return new ReferenceSchema(version, seenSchemaUri);
}
}
HyperSchemaFactoryWrapper targetVisitor = new HyperSchemaFactoryWrapper();
Expand Down
Loading