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 6 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
103 changes: 73 additions & 30 deletions src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchema.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
package com.fasterxml.jackson.module.jsonSchema;

import com.fasterxml.jackson.annotation.*;
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 +48,7 @@
* "id":{
* "type":"number",
* "description":"Product identifier",
* "required":true
* "required":true
* },
* "name":{
* "description":"Name of the product",
Expand Down Expand Up @@ -73,6 +88,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,12 +171,16 @@ 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
private Boolean required = null;
/**
* 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("required")
private Boolean requiredBoolean = null;

/**
* This attribute indicates if the instance is not modifiable.
Expand All @@ -161,8 +195,6 @@ public abstract class JsonSchema
*/
private String description;

protected JsonSchema() { }

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

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

public String getId() {
return id;
}

Expand All @@ -299,8 +335,8 @@ public JsonSchema[] getExtends() {
return extendsextends;
}

public Boolean getRequired() {
return required;
public Boolean getRequiredBoolean() {
Copy link
Member

Choose a reason for hiding this comment

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

Why would the type be included in accessor name? Is that for schema v4?

Copy link
Author

@ammerritt ammerritt Nov 4, 2017

Choose a reason for hiding this comment

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

Since I added the v4 version of required into the ObjectSchema I couldn't have 2 getRequired methods retuning different types. So I changed this one and that is the best I came up with at the time. I probably should put this back to getRequired and rename the one in ObjectSchema to requiredFields and annotate it to be @JsonProperty("required"). Thoughts?

return requiredBoolean;
}

public Boolean getReadonly() {
Expand Down Expand Up @@ -441,6 +477,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 @@ -455,8 +494,11 @@ public void setId(String id) {
this.id = id;
}

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

public void setReadonly(Boolean readonly){
Expand All @@ -478,33 +520,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 +565,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(getRequiredBoolean(), that.getRequiredBoolean()) : 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
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