Skip to content

Commit ccb1061

Browse files
committed
Merge pull request #81 from fivetran/deserialize
Deserialize ArrayItems and JsonValueFormat correctly
2 parents 7e58c27 + 87da47e commit ccb1061

File tree

11 files changed

+148
-127
lines changed

11 files changed

+148
-127
lines changed

src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ArraySchema.java

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
package com.fasterxml.jackson.module.jsonSchema.types;
22

3-
import java.util.Map;
4-
53
import com.fasterxml.jackson.annotation.JsonCreator;
64
import com.fasterxml.jackson.annotation.JsonIgnore;
75
import com.fasterxml.jackson.annotation.JsonProperty;
86
import com.fasterxml.jackson.annotation.JsonValue;
7+
import com.fasterxml.jackson.core.JsonParser;
8+
import com.fasterxml.jackson.core.JsonProcessingException;
9+
import com.fasterxml.jackson.core.ObjectCodec;
10+
import com.fasterxml.jackson.databind.DeserializationContext;
11+
import com.fasterxml.jackson.databind.JsonDeserializer;
12+
import com.fasterxml.jackson.databind.JsonNode;
913
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
1014
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
1115
import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
1216

17+
import java.io.IOException;
18+
import java.util.Map;
19+
1320
/*
1421
* This attribute defines the allowed items in an instance array, and
1522
MUST be a jsonSchema or an array of jsonSchemas. The default value is an
@@ -27,6 +34,7 @@ public class ArraySchema extends ContainerTypeSchema
2734
* see {@link Items}
2835
*/
2936
@JsonProperty
37+
@JsonDeserialize(using = ItemsDeserializer.class)
3038
protected ArraySchema.Items items;
3139

3240
/**This attribute defines the maximum number of values in an array*/
@@ -159,10 +167,14 @@ public static Items jsonCreator(Map<String,Object> props) {
159167
public static class ArrayItems extends ArraySchema.Items {
160168
@JsonProperty
161169
private JsonSchema[] jsonSchemas;
162-
170+
171+
public ArrayItems(JsonSchema[] jsonSchemas) {
172+
this.jsonSchemas = jsonSchemas;
173+
}
174+
163175
/* (non-Javadoc)
164-
* @see com.fasterxml.jackson.databind.jsonSchema.types.ArraySchema.Items#asArrayItems()
165-
*/
176+
* @see com.fasterxml.jackson.databind.jsonSchema.types.ArraySchema.Items#asArrayItems()
177+
*/
166178
@Override
167179
public ArrayItems asArrayItems() { return this; }
168180

@@ -189,6 +201,24 @@ public JsonSchema[] getJsonSchemas() {
189201
@Override
190202
public boolean isArrayItems() { return true; }
191203
}
204+
205+
public static class ItemsDeserializer extends JsonDeserializer<Items> {
206+
207+
@Override
208+
public Items deserialize(JsonParser parser,
209+
DeserializationContext context) throws IOException, JsonProcessingException {
210+
ObjectCodec mapper = parser.getCodec();
211+
JsonNode node = parser.readValueAs(JsonNode.class);
212+
213+
if (node.isArray()) {
214+
JsonSchema[] schemas = mapper.treeToValue(node, JsonSchema[].class);
215+
216+
return new ArrayItems(schemas);
217+
}
218+
else
219+
return new SingleItems(mapper.treeToValue(node, JsonSchema.class));
220+
}
221+
}
192222

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

206236
public SingleItems asSingleItems() { return null; }
207237
public ArrayItems asArrayItems() { return null; }
208-
209-
@JsonCreator
210-
public static Items jsonCreator(Map<String,Object> props) {
211-
//for now only support deserialization of singleItems
212-
Object typeFound = props.get("type");
213-
if (typeFound == null || ! (typeFound instanceof String)) {
214-
return null;
215-
}
216-
String type = (String) typeFound;
217-
JsonSchema schema = JsonSchema.minimalForFormat(JsonFormatTypes.forValue(type));
218-
//KNOWN ISSUE: pending https://github.com/FasterXML/jackson-databind/issues/43
219-
//only deserialize items as minimal schema for type
220-
return new SingleItems(schema);
221-
}
222-
223238
}
224239

225240
/**
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.fasterxml.jackson.module.jsonSchema.types;
2+
3+
import com.fasterxml.jackson.core.JsonParser;
4+
import com.fasterxml.jackson.core.JsonProcessingException;
5+
import com.fasterxml.jackson.databind.DeserializationContext;
6+
import com.fasterxml.jackson.databind.JsonDeserializer;
7+
import com.fasterxml.jackson.databind.JsonMappingException;
8+
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonValueFormat;
9+
10+
import java.io.IOException;
11+
12+
public class JsonValueFormatDeserializer extends JsonDeserializer<JsonValueFormat> {
13+
@Override
14+
public JsonValueFormat deserialize(JsonParser jsonParser,
15+
DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
16+
String string = jsonParser.getValueAsString();
17+
18+
for (JsonValueFormat f : JsonValueFormat.values()) {
19+
if (f.toString().equals(string))
20+
return f;
21+
}
22+
23+
throw new JsonMappingException("Expected JsonValueFormat but found " + string);
24+
}
25+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.fasterxml.jackson.module.jsonSchema.types;
2+
3+
import com.fasterxml.jackson.core.JsonGenerator;
4+
import com.fasterxml.jackson.core.JsonProcessingException;
5+
import com.fasterxml.jackson.databind.JsonSerializer;
6+
import com.fasterxml.jackson.databind.SerializerProvider;
7+
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonValueFormat;
8+
9+
import java.io.IOException;
10+
11+
public class JsonValueFormatSerializer extends JsonSerializer<JsonValueFormat> {
12+
13+
@Override
14+
public void serialize(JsonValueFormat jsonValueFormat,
15+
JsonGenerator jsonGenerator,
16+
SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
17+
jsonGenerator.writeString(jsonValueFormat.toString());
18+
}
19+
}

src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ReferenceSchema.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.fasterxml.jackson.module.jsonSchema.types;
22

3-
import com.fasterxml.jackson.annotation.JsonIgnore;
43
import com.fasterxml.jackson.annotation.JsonProperty;
54
import com.fasterxml.jackson.annotation.JsonTypeInfo;
65
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
@@ -9,7 +8,7 @@
98
* This type represents an JSON reference to a {@link com.fasterxml.jackson.module.jsonSchema.JsonSchema}.
109
* @author adb
1110
*/
12-
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
11+
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, include = JsonTypeInfo.As.PROPERTY, property = "type")
1312
public class ReferenceSchema extends SimpleTypeSchema
1413
{
1514
@JsonProperty
@@ -20,7 +19,6 @@ public ReferenceSchema(String ref) {
2019
}
2120

2221
@Override
23-
@JsonIgnore
2422
public JsonFormatTypes getType() {
2523
return JsonFormatTypes.OBJECT;
2624
}

src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ValueTypeSchema.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package com.fasterxml.jackson.module.jsonSchema.types;
22

3-
import java.util.*;
4-
53
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
5+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
66
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonValueFormat;
77
import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
88

9+
import java.util.LinkedHashSet;
10+
import java.util.Set;
11+
912
/**
1013
* This class represents a {@link JsonSchema}
1114
* A primitive type.
@@ -37,6 +40,8 @@ of enum values uses the same algorithm as defined in "uniqueItems"
3740
* expressed as an URI, and this URI MAY reference a schema of that
3841
*/
3942
@JsonProperty
43+
@JsonDeserialize(using = JsonValueFormatDeserializer.class)
44+
@JsonSerialize(using = JsonValueFormatSerializer.class)
4045
protected JsonValueFormat format;
4146

4247
/**

src/test/java/com/fasterxml/jackson/module/jsonSchema/CustomSchemaReadTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public JavaType typeFromId(DatabindContext ctxt, String id) {
4242
// [module-jsonSchema#67]
4343
public void testSchema() throws Exception
4444
{
45-
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}\"}}}}}";
45+
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}\"}}}}}";
4646

4747
ObjectMapper mapper = new ObjectMapper();
4848

src/test/java/com/fasterxml/jackson/module/jsonSchema/TestCyclic.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
44
import com.fasterxml.jackson.databind.ObjectMapper;
5+
56
import java.util.List;
67
import java.util.Map;
78

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

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

7980
assertEquals(aposToQuotes(EXP), json);

src/test/java/com/fasterxml/jackson/module/jsonSchema/TestReadJsonSchema.java

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22

33
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
44
import com.fasterxml.jackson.databind.JsonNode;
5-
6-
import java.util.*;
7-
85
import com.fasterxml.jackson.databind.JsonSerializable;
96
import com.fasterxml.jackson.databind.MapperFeature;
107
import com.fasterxml.jackson.databind.ObjectMapper;
118
import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper;
129
import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema;
1310
import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema;
1411

12+
import java.io.IOException;
13+
import java.util.*;
14+
1515
/**
1616
* Trivial test to ensure {@link JsonSchema} can be also deserialized
1717
*/
@@ -29,6 +29,11 @@ static class SchemableBasic
2929
public JsonSerializable someSerializable;
3030
}
3131

32+
@JsonPropertyOrder(alphabetic=true)
33+
static class CompoundProperty {
34+
public String a, b;
35+
}
36+
3237
static class SchemableArrays
3338
{
3439
public char[] nameBuffer;
@@ -41,12 +46,22 @@ static class SchemableArrays
4146
public float[] floats;
4247
public double[] doubles;
4348
public Object[] objects;
49+
public CompoundProperty[] basics;
4450
}
4551

4652
static class SchemabeLists
4753
{
4854
public ArrayList<String> extra2;
4955
public List<String> extra;
56+
public List<CompoundProperty> basics;
57+
// TODO this generates a $ref which can't be read
58+
// public ArrayList<CompoundProperty> basics2;
59+
}
60+
61+
static class GeneratesRef {
62+
public CompoundProperty[] a;
63+
// this property serializes as $ref to previous definition of CompoundProperty
64+
public CompoundProperty[] b;
5065
}
5166

5267
static class SchemabeIterableOverObjects {
@@ -84,6 +99,10 @@ public void testReadListTypes() throws Exception {
8499
_testSimple(SchemabeLists.class);
85100
}
86101

102+
public void testReadArray$ref() throws Exception {
103+
_testSimple(GeneratesRef.class);
104+
}
105+
87106
public void testReadIterables() throws Exception {
88107
_testSimple(SchemabeIterableOverObjects.class);
89108
}
@@ -108,31 +127,43 @@ public void testAdditionalItems() throws Exception {
108127
_testSimple("SchemableArrays - additionalItems", jsonSchema);
109128
}
110129

130+
static class SchemableEnumSet {
131+
public EnumSet<SchemaEnum> testEnums;
132+
}
133+
static class SchemableEnumMap {
134+
public EnumMap<SchemaEnum, List<String>> whatever;
135+
}
136+
137+
public void testStructuredEnumTypes() throws Exception {
138+
_testSimple(SchemableEnumSet.class);
139+
_testSimple(SchemableEnumMap.class);
140+
}
141+
111142
public void _testSimple(Class<?> type) throws Exception
112143
{
144+
// Create a schema
113145
SchemaFactoryWrapper visitor = new SchemaFactoryWrapper();
114146
MAPPER.acceptJsonFormatVisitor(MAPPER.constructType(type), visitor);
115147
JsonSchema jsonSchema = visitor.finalSchema();
116148
assertNotNull(jsonSchema);
117-
149+
118150
_testSimple(type.getSimpleName(), jsonSchema);
119151
}
120152

121-
public void _testSimple(String name, JsonSchema jsonSchema) throws Exception
122-
{
123-
String schemaStr = MAPPER.writeValueAsString(jsonSchema);
124-
assertNotNull(schemaStr);
125-
JsonSchema result = MAPPER.readValue(schemaStr, JsonSchema.class);
126-
String resultStr = MAPPER.writeValueAsString(result);
127-
JsonNode node = MAPPER.readTree(schemaStr);
128-
JsonNode finalNode = MAPPER.readTree(resultStr);
153+
private void _testSimple(String name, JsonSchema jsonSchema) throws java.io.IOException {
154+
// Need to do this twice so that $ref schemas
155+
JsonSchema reread = writeAndRead(jsonSchema);
156+
JsonSchema rereadAgain = writeAndRead(reread);
129157

130-
String json1 = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(node);
131-
String json2 = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(finalNode);
132-
133-
// assertEquals(node, finalNode);
134-
assertEquals("Schemas for " + name + " differ",
135-
json1, json2);
158+
assertEquals("Schemas for " + name + " differ", reread, rereadAgain);
159+
}
160+
161+
private JsonSchema writeAndRead(JsonSchema jsonSchema) throws IOException {
162+
String asString = MAPPER.writeValueAsString(jsonSchema);
163+
164+
assertNotNull(asString);
165+
166+
return MAPPER.readValue(asString, JsonSchema.class);
136167
}
137168

138169
/**

src/test/java/com/fasterxml/jackson/module/jsonSchema/TestTypeGeneration.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package com.fasterxml.jackson.module.jsonSchema;
22

3-
import java.util.Date;
4-
53
import com.fasterxml.jackson.databind.ObjectMapper;
6-
import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
7-
import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator;
4+
5+
import java.util.Date;
86

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

0 commit comments

Comments
 (0)