Loading README.md +5 −1 Original line number Diff line number Diff line Loading @@ -11,7 +11,11 @@ Current version: *1.0.9* * JSON Schema Draft v4 * Supports polymorphism (**@JsonTypeInfo**, **MixIn**, and **registerSubtypes()**) using JsonSchema's **oneOf**-feature. * Supports schema customization using **@JsonSchemaDescription**, **@JsonSchemaFormat** and **@JsonSchemaTitle** * Supports schema customization using: - **@JsonSchemaDescription** - **@JsonSchemaFormat** - **@JsonSchemaTitle** - **@JsonSchemaDefault** * Supports many Javax-validation @Annotations * Works well with Generated GUI's using [https://github.com/jdorn/json-editor](https://github.com/jdorn/json-editor) - (Must be configured to use this mode) Loading src/main/java/com/kjetland/jackson/jsonSchema/annotations/JsonSchemaDefault.java 0 → 100755 +13 −0 Original line number Diff line number Diff line package com.kjetland.jackson.jsonSchema.annotations; 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 JsonSchemaDefault { String value(); } src/main/scala/com/kjetland/jackson/jsonSchema/JsonSchemaGenerator.scala +29 −2 Original line number Diff line number Diff line Loading @@ -4,7 +4,7 @@ import java.lang.reflect.{Field, Method, ParameterizedType} import java.time.{LocalDate, LocalDateTime, LocalTime, OffsetDateTime} import java.util import java.util.Optional import javax.validation.constraints.{Pattern, Max, Min, NotNull, Size} import javax.validation.constraints.{Max, Min, NotNull, Pattern, Size} import com.fasterxml.jackson.annotation.{JsonSubTypes, JsonTypeInfo} import com.fasterxml.jackson.core.JsonParser.NumberType Loading @@ -14,7 +14,7 @@ import com.fasterxml.jackson.databind._ import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.fasterxml.jackson.databind.introspect.{AnnotatedClass, JacksonAnnotationIntrospector} import com.fasterxml.jackson.databind.node.{ArrayNode, JsonNodeFactory, ObjectNode} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaDescription, JsonSchemaFormat, JsonSchemaTitle} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaDefault, JsonSchemaDescription, JsonSchemaFormat, JsonSchemaTitle} import org.slf4j.LoggerFactory object JsonSchemaGenerator { Loading Loading @@ -239,6 +239,12 @@ class JsonSchemaGenerator node.put("pattern", pattern.regexp()) } // Look for @JsonSchemaDefault Option(p.getAnnotation(classOf[JsonSchemaDefault])).map { defaultValue => node.put("default", defaultValue.value()) } // Look for @Size Option(p.getAnnotation(classOf[Size])) .map { Loading Loading @@ -322,6 +328,12 @@ class JsonSchemaGenerator max => node.put("maximum", max.value()) } // Look for @JsonSchemaDefault Option(p.getAnnotation(classOf[JsonSchemaDefault])).map { defaultValue => node.put("default", defaultValue.value().toLong ) } } new JsonNumberFormatVisitor with EnumSupport { Loading Loading @@ -361,6 +373,12 @@ class JsonSchemaGenerator max => node.put("maximum", max.value()) } // Look for @JsonSchemaDefault Option(p.getAnnotation(classOf[JsonSchemaDefault])).map { defaultValue => node.put("default", defaultValue.value().toInt) } } Loading @@ -384,6 +402,15 @@ class JsonSchemaGenerator node.put("type", "boolean") currentProperty.map { p => // Look for @JsonSchemaDefault Option(p.getAnnotation(classOf[JsonSchemaDefault])).map { defaultValue => node.put("default", defaultValue.value().toBoolean) } } new JsonBooleanFormatVisitor with EnumSupport { val _node = node override def format(format: JsonValueFormat): Unit = { Loading src/test/scala/com/kjetland/jackson/jsonSchema/JsonSchemaGeneratorTest.scala +32 −3 Original line number Diff line number Diff line Loading @@ -3,7 +3,7 @@ package com.kjetland.jackson.jsonSchema import java.time.{LocalDate, LocalDateTime, OffsetDateTime} import java.util import java.util.{Optional, TimeZone} import javax.validation.constraints.{Pattern, Max, Min, NotNull, Size} import javax.validation.constraints.{Max, Min, NotNull, Pattern, Size} import com.fasterxml.jackson.annotation.{JsonProperty, JsonSubTypes, JsonTypeInfo, JsonValue} import com.fasterxml.jackson.databind.annotation.JsonDeserialize Loading @@ -15,6 +15,7 @@ import com.fasterxml.jackson.datatype.joda.JodaModule import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import com.fasterxml.jackson.module.scala.DefaultScalaModule import com.github.fge.jsonschema.main.JsonSchemaFactory import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaDefault import com.kjetland.jackson.jsonSchema.testData._ import com.kjetland.jackson.jsonSchema.testData.mixin.{MixinChild1, MixinModule, MixinParent} import org.scalatest.{FunSuite, Matchers} Loading Loading @@ -213,6 +214,15 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers { test("Generate schema for regular class which has a property of class annotated with @JsonTypeInfo") { def assertDefaultValues(schema:JsonNode): Unit ={ assert( schema.at("/properties/stringWithDefault/type").asText() == "string" ) assert( schema.at("/properties/stringWithDefault/default").asText() == "x" ) assert( schema.at("/properties/intWithDefault/type").asText() == "integer" ) assert( schema.at("/properties/intWithDefault/default").asInt() == 12 ) assert( schema.at("/properties/booleanWithDefault/type").asText() == "boolean" ) assert( schema.at("/properties/booleanWithDefault/default").asBoolean() == true ) } // Java { val jsonNode = assertToFromJson(jsonSchemaGenerator, testData.pojoWithParent) Loading @@ -220,6 +230,8 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers { assert(false == schema.at("/additionalProperties").asBoolean()) assert(schema.at("/properties/pojoValue/type").asText() == "boolean") assertDefaultValues(schema) assertChild1(schema, "/properties/child/oneOf") assertChild2(schema, "/properties/child/oneOf") Loading @@ -233,6 +245,7 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers { assert(false == schema.at("/additionalProperties").asBoolean()) assert(schema.at("/properties/pojoValue/type").asText() == "boolean") assertDefaultValues(schema) assertChild1(schema, "/properties/child/oneOf", html5Checks = true) assertChild2(schema, "/properties/child/oneOf", html5Checks = true) Loading @@ -247,6 +260,7 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers { assert(false == schema.at("/additionalProperties").asBoolean()) assert(schema.at("/properties/pojoValue/type").asText() == "boolean") assertDefaultValues(schema) assertChild1(schema, "/properties/child/oneOf", "com.kjetland.jackson.jsonSchema.testData.Child1", false) assertChild2(schema, "/properties/child/oneOf", "com.kjetland.jackson.jsonSchema.testData.Child2", false) Loading @@ -260,6 +274,7 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers { assert(false == schema.at("/additionalProperties").asBoolean()) assert(schema.at("/properties/pojoValue/type").asText() == "boolean") assertDefaultValues(schema) assertChild1(schema, "/properties/child/oneOf", "Child1Scala") assertChild2(schema, "/properties/child/oneOf", "Child2Scala") Loading Loading @@ -706,10 +721,13 @@ trait TestData { val p = new PojoWithParent p.pojoValue = true p.child = child1 p.stringWithDefault = "y" p.intWithDefault = 13 p.booleanWithDefault = true p } val pojoWithParentScala = PojoWithParentScala(true, child1Scala) val pojoWithParentScala = PojoWithParentScala(true, child1Scala, "y", 13, true) val classNotExtendingAnything = { val o = new ClassNotExtendingAnything Loading Loading @@ -814,7 +832,18 @@ case class Child1Scala case class Child2Scala(parentString:String, child2int:Int) extends ParentScala case class PojoWithParentScala(pojoValue:Boolean, child:ParentScala) case class PojoWithParentScala ( pojoValue:Boolean, child:ParentScala, @JsonSchemaDefault("x") stringWithDefault:String, @JsonSchemaDefault("12") intWithDefault:Int, @JsonSchemaDefault("true") booleanWithDefault:Boolean ) case class ManyPrimitivesScala(_string:String, _integer:Int, _boolean:Boolean, _double:Double) Loading src/test/scala/com/kjetland/jackson/jsonSchema/testData/PojoWithParent.java +18 −1 Original line number Diff line number Diff line package com.kjetland.jackson.jsonSchema.testData; import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaDefault; public class PojoWithParent { public Boolean pojoValue; public Parent child; @JsonSchemaDefault("x") public String stringWithDefault; @JsonSchemaDefault("12") public int intWithDefault; @JsonSchemaDefault("true") public boolean booleanWithDefault; @Override public boolean equals(Object o) { if (this == o) return true; Loading @@ -12,8 +23,11 @@ public class PojoWithParent { PojoWithParent that = (PojoWithParent) o; if (intWithDefault != that.intWithDefault) return false; if (booleanWithDefault != that.booleanWithDefault) return false; if (pojoValue != null ? !pojoValue.equals(that.pojoValue) : that.pojoValue != null) return false; return child != null ? child.equals(that.child) : that.child == null; if (child != null ? !child.equals(that.child) : that.child != null) return false; return stringWithDefault != null ? stringWithDefault.equals(that.stringWithDefault) : that.stringWithDefault == null; } Loading @@ -21,6 +35,9 @@ public class PojoWithParent { public int hashCode() { int result = pojoValue != null ? pojoValue.hashCode() : 0; result = 31 * result + (child != null ? child.hashCode() : 0); result = 31 * result + (stringWithDefault != null ? stringWithDefault.hashCode() : 0); result = 31 * result + intWithDefault; result = 31 * result + (booleanWithDefault ? 1 : 0); return result; } } Loading
README.md +5 −1 Original line number Diff line number Diff line Loading @@ -11,7 +11,11 @@ Current version: *1.0.9* * JSON Schema Draft v4 * Supports polymorphism (**@JsonTypeInfo**, **MixIn**, and **registerSubtypes()**) using JsonSchema's **oneOf**-feature. * Supports schema customization using **@JsonSchemaDescription**, **@JsonSchemaFormat** and **@JsonSchemaTitle** * Supports schema customization using: - **@JsonSchemaDescription** - **@JsonSchemaFormat** - **@JsonSchemaTitle** - **@JsonSchemaDefault** * Supports many Javax-validation @Annotations * Works well with Generated GUI's using [https://github.com/jdorn/json-editor](https://github.com/jdorn/json-editor) - (Must be configured to use this mode) Loading
src/main/java/com/kjetland/jackson/jsonSchema/annotations/JsonSchemaDefault.java 0 → 100755 +13 −0 Original line number Diff line number Diff line package com.kjetland.jackson.jsonSchema.annotations; 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 JsonSchemaDefault { String value(); }
src/main/scala/com/kjetland/jackson/jsonSchema/JsonSchemaGenerator.scala +29 −2 Original line number Diff line number Diff line Loading @@ -4,7 +4,7 @@ import java.lang.reflect.{Field, Method, ParameterizedType} import java.time.{LocalDate, LocalDateTime, LocalTime, OffsetDateTime} import java.util import java.util.Optional import javax.validation.constraints.{Pattern, Max, Min, NotNull, Size} import javax.validation.constraints.{Max, Min, NotNull, Pattern, Size} import com.fasterxml.jackson.annotation.{JsonSubTypes, JsonTypeInfo} import com.fasterxml.jackson.core.JsonParser.NumberType Loading @@ -14,7 +14,7 @@ import com.fasterxml.jackson.databind._ import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.fasterxml.jackson.databind.introspect.{AnnotatedClass, JacksonAnnotationIntrospector} import com.fasterxml.jackson.databind.node.{ArrayNode, JsonNodeFactory, ObjectNode} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaDescription, JsonSchemaFormat, JsonSchemaTitle} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaDefault, JsonSchemaDescription, JsonSchemaFormat, JsonSchemaTitle} import org.slf4j.LoggerFactory object JsonSchemaGenerator { Loading Loading @@ -239,6 +239,12 @@ class JsonSchemaGenerator node.put("pattern", pattern.regexp()) } // Look for @JsonSchemaDefault Option(p.getAnnotation(classOf[JsonSchemaDefault])).map { defaultValue => node.put("default", defaultValue.value()) } // Look for @Size Option(p.getAnnotation(classOf[Size])) .map { Loading Loading @@ -322,6 +328,12 @@ class JsonSchemaGenerator max => node.put("maximum", max.value()) } // Look for @JsonSchemaDefault Option(p.getAnnotation(classOf[JsonSchemaDefault])).map { defaultValue => node.put("default", defaultValue.value().toLong ) } } new JsonNumberFormatVisitor with EnumSupport { Loading Loading @@ -361,6 +373,12 @@ class JsonSchemaGenerator max => node.put("maximum", max.value()) } // Look for @JsonSchemaDefault Option(p.getAnnotation(classOf[JsonSchemaDefault])).map { defaultValue => node.put("default", defaultValue.value().toInt) } } Loading @@ -384,6 +402,15 @@ class JsonSchemaGenerator node.put("type", "boolean") currentProperty.map { p => // Look for @JsonSchemaDefault Option(p.getAnnotation(classOf[JsonSchemaDefault])).map { defaultValue => node.put("default", defaultValue.value().toBoolean) } } new JsonBooleanFormatVisitor with EnumSupport { val _node = node override def format(format: JsonValueFormat): Unit = { Loading
src/test/scala/com/kjetland/jackson/jsonSchema/JsonSchemaGeneratorTest.scala +32 −3 Original line number Diff line number Diff line Loading @@ -3,7 +3,7 @@ package com.kjetland.jackson.jsonSchema import java.time.{LocalDate, LocalDateTime, OffsetDateTime} import java.util import java.util.{Optional, TimeZone} import javax.validation.constraints.{Pattern, Max, Min, NotNull, Size} import javax.validation.constraints.{Max, Min, NotNull, Pattern, Size} import com.fasterxml.jackson.annotation.{JsonProperty, JsonSubTypes, JsonTypeInfo, JsonValue} import com.fasterxml.jackson.databind.annotation.JsonDeserialize Loading @@ -15,6 +15,7 @@ import com.fasterxml.jackson.datatype.joda.JodaModule import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import com.fasterxml.jackson.module.scala.DefaultScalaModule import com.github.fge.jsonschema.main.JsonSchemaFactory import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaDefault import com.kjetland.jackson.jsonSchema.testData._ import com.kjetland.jackson.jsonSchema.testData.mixin.{MixinChild1, MixinModule, MixinParent} import org.scalatest.{FunSuite, Matchers} Loading Loading @@ -213,6 +214,15 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers { test("Generate schema for regular class which has a property of class annotated with @JsonTypeInfo") { def assertDefaultValues(schema:JsonNode): Unit ={ assert( schema.at("/properties/stringWithDefault/type").asText() == "string" ) assert( schema.at("/properties/stringWithDefault/default").asText() == "x" ) assert( schema.at("/properties/intWithDefault/type").asText() == "integer" ) assert( schema.at("/properties/intWithDefault/default").asInt() == 12 ) assert( schema.at("/properties/booleanWithDefault/type").asText() == "boolean" ) assert( schema.at("/properties/booleanWithDefault/default").asBoolean() == true ) } // Java { val jsonNode = assertToFromJson(jsonSchemaGenerator, testData.pojoWithParent) Loading @@ -220,6 +230,8 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers { assert(false == schema.at("/additionalProperties").asBoolean()) assert(schema.at("/properties/pojoValue/type").asText() == "boolean") assertDefaultValues(schema) assertChild1(schema, "/properties/child/oneOf") assertChild2(schema, "/properties/child/oneOf") Loading @@ -233,6 +245,7 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers { assert(false == schema.at("/additionalProperties").asBoolean()) assert(schema.at("/properties/pojoValue/type").asText() == "boolean") assertDefaultValues(schema) assertChild1(schema, "/properties/child/oneOf", html5Checks = true) assertChild2(schema, "/properties/child/oneOf", html5Checks = true) Loading @@ -247,6 +260,7 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers { assert(false == schema.at("/additionalProperties").asBoolean()) assert(schema.at("/properties/pojoValue/type").asText() == "boolean") assertDefaultValues(schema) assertChild1(schema, "/properties/child/oneOf", "com.kjetland.jackson.jsonSchema.testData.Child1", false) assertChild2(schema, "/properties/child/oneOf", "com.kjetland.jackson.jsonSchema.testData.Child2", false) Loading @@ -260,6 +274,7 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers { assert(false == schema.at("/additionalProperties").asBoolean()) assert(schema.at("/properties/pojoValue/type").asText() == "boolean") assertDefaultValues(schema) assertChild1(schema, "/properties/child/oneOf", "Child1Scala") assertChild2(schema, "/properties/child/oneOf", "Child2Scala") Loading Loading @@ -706,10 +721,13 @@ trait TestData { val p = new PojoWithParent p.pojoValue = true p.child = child1 p.stringWithDefault = "y" p.intWithDefault = 13 p.booleanWithDefault = true p } val pojoWithParentScala = PojoWithParentScala(true, child1Scala) val pojoWithParentScala = PojoWithParentScala(true, child1Scala, "y", 13, true) val classNotExtendingAnything = { val o = new ClassNotExtendingAnything Loading Loading @@ -814,7 +832,18 @@ case class Child1Scala case class Child2Scala(parentString:String, child2int:Int) extends ParentScala case class PojoWithParentScala(pojoValue:Boolean, child:ParentScala) case class PojoWithParentScala ( pojoValue:Boolean, child:ParentScala, @JsonSchemaDefault("x") stringWithDefault:String, @JsonSchemaDefault("12") intWithDefault:Int, @JsonSchemaDefault("true") booleanWithDefault:Boolean ) case class ManyPrimitivesScala(_string:String, _integer:Int, _boolean:Boolean, _double:Double) Loading
src/test/scala/com/kjetland/jackson/jsonSchema/testData/PojoWithParent.java +18 −1 Original line number Diff line number Diff line package com.kjetland.jackson.jsonSchema.testData; import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaDefault; public class PojoWithParent { public Boolean pojoValue; public Parent child; @JsonSchemaDefault("x") public String stringWithDefault; @JsonSchemaDefault("12") public int intWithDefault; @JsonSchemaDefault("true") public boolean booleanWithDefault; @Override public boolean equals(Object o) { if (this == o) return true; Loading @@ -12,8 +23,11 @@ public class PojoWithParent { PojoWithParent that = (PojoWithParent) o; if (intWithDefault != that.intWithDefault) return false; if (booleanWithDefault != that.booleanWithDefault) return false; if (pojoValue != null ? !pojoValue.equals(that.pojoValue) : that.pojoValue != null) return false; return child != null ? child.equals(that.child) : that.child == null; if (child != null ? !child.equals(that.child) : that.child != null) return false; return stringWithDefault != null ? stringWithDefault.equals(that.stringWithDefault) : that.stringWithDefault == null; } Loading @@ -21,6 +35,9 @@ public class PojoWithParent { public int hashCode() { int result = pojoValue != null ? pojoValue.hashCode() : 0; result = 31 * result + (child != null ? child.hashCode() : 0); result = 31 * result + (stringWithDefault != null ? stringWithDefault.hashCode() : 0); result = 31 * result + intWithDefault; result = 31 * result + (booleanWithDefault ? 1 : 0); return result; } }