Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package com.fasterxml.jackson.module.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;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
import com.fasterxml.jackson.module.jsonSchema.JsonSchema;

import java.io.IOException;
import java.util.Map;

/*
* 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
Expand All @@ -27,6 +34,7 @@ public class ArraySchema extends ContainerTypeSchema
* see {@link Items}
*/
@JsonProperty
@JsonDeserialize(using = ItemsDeserializer.class)
protected ArraySchema.Items items;

/**This attribute defines the maximum number of values in an array*/
Expand Down Expand Up @@ -159,10 +167,14 @@ public static Items jsonCreator(Map<String,Object> props) {
public static class ArrayItems extends ArraySchema.Items {
@JsonProperty
private JsonSchema[] jsonSchemas;


public ArrayItems(JsonSchema[] jsonSchemas) {
this.jsonSchemas = jsonSchemas;
}

/* (non-Javadoc)
* @see com.fasterxml.jackson.databind.jsonSchema.types.ArraySchema.Items#asArrayItems()
*/
* @see com.fasterxml.jackson.databind.jsonSchema.types.ArraySchema.Items#asArrayItems()
*/
@Override
public ArrayItems asArrayItems() { return this; }

Expand All @@ -189,6 +201,24 @@ public JsonSchema[] getJsonSchemas() {
@Override
public boolean isArrayItems() { return true; }
}

public static class ItemsDeserializer extends JsonDeserializer<Items> {

@Override
public Items deserialize(JsonParser parser,
DeserializationContext context) throws IOException, JsonProcessingException {
ObjectCodec mapper = parser.getCodec();
Copy link
Member

Choose a reason for hiding this comment

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

I assume it is legal to use array, or a single item here?
This works, I can probably simplify it after merge a bit.

JsonNode node = parser.readValueAs(JsonNode.class);

if (node.isArray()) {
JsonSchema[] schemas = mapper.treeToValue(node, JsonSchema[].class);

return new ArrayItems(schemas);
}
else
return new SingleItems(mapper.treeToValue(node, JsonSchema.class));
}
}

/**
* This attribute defines the allowed items in an instance array, and
Expand All @@ -205,21 +235,6 @@ public static abstract class Items {

public SingleItems asSingleItems() { return null; }
public ArrayItems asArrayItems() { return null; }

@JsonCreator
public static Items jsonCreator(Map<String,Object> 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(JsonFormatTypes.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);
}

}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.fasterxml.jackson.module.jsonSchema.types;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonValueFormat;

import java.io.IOException;

public class JsonValueFormatDeserializer extends JsonDeserializer<JsonValueFormat> {
@Override
public JsonValueFormat deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String string = jsonParser.getValueAsString();

for (JsonValueFormat f : JsonValueFormat.values()) {
if (f.toString().equals(string))
return f;
}

throw new JsonMappingException("Expected JsonValueFormat but found " + string);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.fasterxml.jackson.module.jsonSchema.types;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonValueFormat;

import java.io.IOException;

public class JsonValueFormatSerializer extends JsonSerializer<JsonValueFormat> {

@Override
public void serialize(JsonValueFormat jsonValueFormat,
JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeString(jsonValueFormat.toString());
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.fasterxml.jackson.module.jsonSchema.types;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
Expand All @@ -9,7 +8,7 @@
* This type represents an JSON reference to a {@link com.fasterxml.jackson.module.jsonSchema.JsonSchema}.
* @author adb
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, include = JsonTypeInfo.As.PROPERTY, property = "type")
public class ReferenceSchema extends SimpleTypeSchema
{
@JsonProperty
Expand All @@ -20,7 +19,6 @@ public ReferenceSchema(String ref) {
}

@Override
@JsonIgnore
public JsonFormatTypes getType() {
return JsonFormatTypes.OBJECT;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.fasterxml.jackson.module.jsonSchema.types;

import java.util.*;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonValueFormat;
import com.fasterxml.jackson.module.jsonSchema.JsonSchema;

import java.util.LinkedHashSet;
import java.util.Set;

/**
* This class represents a {@link JsonSchema}
* A primitive type.
Expand Down Expand Up @@ -37,6 +40,8 @@ of enum values uses the same algorithm as defined in "uniqueItems"
* expressed as an URI, and this URI MAY reference a schema of that
*/
@JsonProperty
@JsonDeserialize(using = JsonValueFormatDeserializer.class)
@JsonSerialize(using = JsonValueFormatSerializer.class)
protected JsonValueFormat format;

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public JavaType typeFromId(DatabindContext ctxt, String 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}\"}}}}}";
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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -59,7 +60,7 @@ public void testListCyclic() throws Exception
String json = MAPPER.writeValueAsString(schema);
String EXP = "{\"type\":\"object\"," +
"\"id\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestCyclic:ListLoop\"," +
"\"properties\":{\"list\":{\"type\":\"array\",\"items\":{\"$ref\":\"" +
"\"properties\":{\"list\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"$ref\":\"" +
"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestCyclic:ListLoop\"}}}}";

assertEquals(aposToQuotes(EXP), json);
Expand All @@ -73,7 +74,7 @@ public void testMapCyclic() throws Exception
String json = MAPPER.writeValueAsString(schema);
String EXP = "{\"type\":\"object\"," +
"\"id\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestCyclic:MapLoop\"," +
"\"properties\":{\"map\":{\"type\":\"object\",\"additionalProperties\":{" +
"\"properties\":{\"map\":{\"type\":\"object\",\"additionalProperties\":{\"type\":\"object\"," +
"\"$ref\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestCyclic:MapLoop\"}}}}";

assertEquals(aposToQuotes(EXP), json);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.JsonNode;

import java.util.*;

import com.fasterxml.jackson.databind.JsonSerializable;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper;
import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema;
import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema;

import java.io.IOException;
import java.util.*;

/**
* Trivial test to ensure {@link JsonSchema} can be also deserialized
*/
Expand All @@ -29,6 +29,11 @@ static class SchemableBasic
public JsonSerializable someSerializable;
}

@JsonPropertyOrder(alphabetic=true)
static class CompoundProperty {
public String a, b;
}

static class SchemableArrays
{
public char[] nameBuffer;
Expand All @@ -41,12 +46,22 @@ static class SchemableArrays
public float[] floats;
public double[] doubles;
public Object[] objects;
public CompoundProperty[] basics;
}

static class SchemabeLists
{
public ArrayList<String> extra2;
public List<String> extra;
public List<CompoundProperty> basics;
// TODO this generates a $ref which can't be read
// public ArrayList<CompoundProperty> basics2;
}

static class GeneratesRef {
public CompoundProperty[] a;
// this property serializes as $ref to previous definition of CompoundProperty
public CompoundProperty[] b;
}

static class SchemabeIterableOverObjects {
Expand Down Expand Up @@ -84,6 +99,10 @@ public void testReadListTypes() throws Exception {
_testSimple(SchemabeLists.class);
}

public void testReadArray$ref() throws Exception {
_testSimple(GeneratesRef.class);
}

public void testReadIterables() throws Exception {
_testSimple(SchemabeIterableOverObjects.class);
}
Expand All @@ -108,31 +127,43 @@ public void testAdditionalItems() throws Exception {
_testSimple("SchemableArrays - additionalItems", jsonSchema);
}

static class SchemableEnumSet {
public EnumSet<SchemaEnum> testEnums;
}
static class SchemableEnumMap {
public EnumMap<SchemaEnum, List<String>> whatever;
}

public void testStructuredEnumTypes() throws Exception {
_testSimple(SchemableEnumSet.class);
_testSimple(SchemableEnumMap.class);
}

public void _testSimple(Class<?> type) throws Exception
{
// Create a schema
SchemaFactoryWrapper visitor = new SchemaFactoryWrapper();
MAPPER.acceptJsonFormatVisitor(MAPPER.constructType(type), visitor);
JsonSchema jsonSchema = visitor.finalSchema();
assertNotNull(jsonSchema);

_testSimple(type.getSimpleName(), jsonSchema);
}

public void _testSimple(String name, JsonSchema jsonSchema) throws Exception
{
String schemaStr = MAPPER.writeValueAsString(jsonSchema);
assertNotNull(schemaStr);
JsonSchema result = MAPPER.readValue(schemaStr, JsonSchema.class);
String resultStr = MAPPER.writeValueAsString(result);
JsonNode node = MAPPER.readTree(schemaStr);
JsonNode finalNode = MAPPER.readTree(resultStr);
private void _testSimple(String name, JsonSchema jsonSchema) throws java.io.IOException {
// Need to do this twice so that $ref schemas
JsonSchema reread = writeAndRead(jsonSchema);
JsonSchema rereadAgain = writeAndRead(reread);

String json1 = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(node);
String json2 = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(finalNode);

// assertEquals(node, finalNode);
assertEquals("Schemas for " + name + " differ",
json1, json2);
assertEquals("Schemas for " + name + " differ", reread, rereadAgain);
}

private JsonSchema writeAndRead(JsonSchema jsonSchema) throws IOException {
String asString = MAPPER.writeValueAsString(jsonSchema);

assertNotNull(asString);

return MAPPER.readValue(asString, JsonSchema.class);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package com.fasterxml.jackson.module.jsonSchema;

import java.util.Date;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator;

import java.util.Date;

public class TestTypeGeneration extends SchemaTestBase
{
Expand All @@ -29,7 +27,7 @@ public void testCorrectType() throws Exception
String json = MAPPER.writeValueAsString(jsonSchema).replace('"', '\'');
final String EXP = "{'type':'object'," +
"'id':'urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestTypeGeneration:Issue14Bean'," +
"'properties':{'date':{'type':'integer','format':'UTC_MILLISEC'}}}";
"'properties':{'date':{'type':'integer','format':'utc-millisec'}}}";
assertEquals(EXP, json);
}

Expand Down
Loading