Giter Club home page Giter Club logo

mbknor-jackson-jsonschema's Introduction

Jackson jsonSchema Generator

Build Status Maven Central

This projects aims to do a better job than the original jackson-module-jsonSchema in generating jsonSchema from your POJOs using Jackson @Annotations.

Highlights

  • JSON Schema DRAFT-04, DRAFT-06, DRAFT-07 and DRAFT-2019-09
  • Supports polymorphism (@JsonTypeInfo, MixIn, and registerSubtypes()) using JsonSchema's oneOf-feature.
  • Supports schema customization using:
    • @JsonSchemaDescription/@JsonPropertyDescription
    • @JsonSchemaFormat
    • @JsonSchemaTitle
    • @JsonProperty(.., defaultValue) / @JsonSchemaDefault
    • @JsonSchemaOptions
    • @JsonSchemaInject
    • @JsonSchemaExamples
  • Supports many Javax-validation @Annotations including support for validation-groups
  • Works well with Generated GUI's using https://github.com/json-editor/json-editor
    • (Must be configured to use this mode)
    • Special handling of Option-/Optional-properties using oneOf.
  • Supports custom Class-to-format-Mapping
  • Supports injecting custom json-schema-fragments using the @JsonSchemaInject-annotation.

Benefits

  • Simple implementation - Just one file (for now..)
  • Implemented in Scala (Built for 2.10, 2.11, 2.12 and 2.13)
  • Easy to fix and add functionality

Flexible

If this generator does not generate exactly the schema you want, you can inject it by using the @JsonSchemaInject-annotation.

If you need to use patternProperties (which is not currently 'natively' supported by mbknor-jackson-jsonSchema), you can make it work by injecting the following json-schema-fragment:

{
  "patternProperties" : {
    "^[a-zA-Z0-9]+" : {
      "type" : "string"
    }
  }
}

.. like this in Scala:

@JsonSerialize(using = MySpecialSerializer.class)
JsonSchemaInject(
  json =
    """
      {
        "patternProperties" : {
          "^[a-zA-Z0-9]+" : {
            "type" : "string"
          }
        }
      }
    """
)
case class MyPojo(...)

.. or like this in Java

@JsonSerialize(using = MySpecialSerializer.class)
@JsonSchemaInject( json = "{\n" +
        "  \"patternProperties\" : {\n" +
        "    \"^[a-zA-Z0-9]+\" : {\n" +
        "      \"type\" : \"string\"\n" +
        "    }\n" +
        "  }\n" +
        "}" )
public class MyPojo {
    ...
    ...
    ...
}

Alternatively, the following one liner will achieve the same result:

@JsonSerialize(using = MySpecialSerializer.class)
@JsonSchemaInject(strings = {@JsonSchemaString(path = "patternProperties/^[a-zA-Z0-9]+/type", value = "string")})
public class MyPojo {
    ...
    ...
    ...
}

The annotation will nest the value at the level defined by the path. You can use the raw json along with the individual path/value pairs in the same @JsonSchemaInject annotation. Although keep in mind that the pairs are applied last. For boolean and number values use the JsonSchemaInject#bools and JsonSchemaInject#ints collections correspondingly.

public class MyPojo {
    @JsonSchemaInject(
      bools = {@JsonSchemaBool(path = "exclusiveMinimum", value = true)},
      ints = {@JsonSchemaInt(path = "multipleOf", value = 7)}
    )
    @Min(5)
    public int myIntValue;
    ...
    ...
    ...
}

If a part of the schema is not known at compile time, you can use a json supplier:

case class MyPojo {
  @JsonSchemaInject(jsonSupplier = classOf[UserNamesLoader])
  uns:Set[String]
  ...
  ...
  ...
}

class UserNamesLoader extends Supplier[JsonNode] {
  val _objectMapper = new ObjectMapper()

  override def get(): JsonNode = {
    val schema = _objectMapper.createObjectNode()
    val values = schema.putObject("items").putArray("enum")
    loadUsers().foreach(u => values.add(u.name))

    schema
  }
  ...
  ...
  ...
}

This will associate an enum of possible values for the set that you generate at runtime.

If you need even more control over the schema-generating runtime, you can use @JsonSchemaInject.jsonSupplierViaLookup like this:

case class MyPojo {
  @JsonSchemaInject(jsonSupplierViaLookup = "theKeyToUseWhenLookingUpASupplier")
  uns:Set[String]
  ...
  ...
  ...
}

Then you have to add the mapping between the key 'theKeyToUseWhenLookingUpASupplier' and the Supplier-instance in the config-object used when creating the JsonSchemaGenerator.

The default behaviour of @JsonSchemaInject is to merge the injected json into the generated JsonSchema. If you want to have full control over it, you can specify @JsonSchemaInject.merge = false to replace the generated jsonSchema with the injected json.

@JsonSchemaInject can also be used on properties.

Another way of altering the generated schema is to use the config-param classTypeReMapping.

This can be used to remap the Class found by Jackson into another Class before generating the schema for it.

It might be the case that you have a complex Class-structure using Polymorphism, but for some reason you know upfront that the user needs to enter/specify a specific subType. To enforce this into the generated schema, you can map the SuperType into the specific-type.

Project status

Applications using this project has been in production for many years.

I would really appreciate it if other developers wanted to start using and contributing improvements and features.

Dependency

This project publishes artifacts to central maven repo.

The project is also compiled using Java 8. This means that you also need to use Java 8.

Artifacts for both Scala 2.10, 2.11 and 2.12 is now available (Thanks to @bbyk for adding crossBuild functionality).

Using Maven

Add this to you pom.xml:

<dependency>
    <groupId>com.kjetland</groupId>
    <artifactId>mbknor-jackson-jsonschema_2.12</artifactId>
    <version>[---LATEST VERSION---]</version>
</dependency>    

Using sbt

Add this to you sbt build-config:

"com.kjetland" %% "mbknor-jackson-jsonschema" % "[---LATEST VERSION---]"

Code - Using Scala

This is how to generate jsonSchema in code using Scala:

    val objectMapper = new ObjectMapper
    val jsonSchemaGenerator = new JsonSchemaGenerator(objectMapper)
    val jsonSchema:JsonNode = jsonSchemaGenerator.generateJsonSchema(classOf[YourPOJO])

    val jsonSchemaAsString:String = objectMapper.writeValueAsString(jsonSchema)

This is how to generate jsonSchema used for generating HTML5 GUI using json-editor:

    val objectMapper = new ObjectMapper
    val jsonSchemaGenerator = new JsonSchemaGenerator(objectMapper, config = JsonSchemaConfig.html5EnabledSchema)
    val jsonSchema:JsonNode = jsonSchemaGenerator.generateJsonSchema(classOf[YourPOJO])

    val jsonSchemaAsString:String = objectMapper.writeValueAsString(jsonSchema)

This is how to generate jsonSchema using custom type-to-format-mapping using Scala:

    val objectMapper = new ObjectMapper
    val config:JsonSchemaConfig = JsonSchemaConfig.vanillaJsonSchemaDraft4.copy(
      customType2FormatMapping = Map( "java.time.OffsetDateTime" -> "date-time-ABC-Special" )
    )
    val jsonSchemaGenerator = new JsonSchemaGenerator(objectMapper, config = config)
    val jsonSchema:JsonNode = jsonSchemaGenerator.generateJsonSchema(classOf[YourPOJO])

    val jsonSchemaAsString:String = objectMapper.writeValueAsString(jsonSchema)

Note about Scala and Option[Int]:

Due to Java's Type Erasure it impossible to resolve the type T behind Option[T] when T is Int, Boolean, Double. As a workaround, you have to use the @JsonDeserialize-annotation in such cases. See https://github.com/FasterXML/jackson-module-scala/wiki/FAQ#deserializing-optionint-and-other-primitive-challenges for more info.

Example:

    case class PojoUsingOptionScala(
                                     _string:Option[String], // @JsonDeserialize not needed here
                                     @JsonDeserialize(contentAs = classOf[Int])     _integer:Option[Int],
                                     @JsonDeserialize(contentAs = classOf[Boolean]) _boolean:Option[Boolean],
                                     @JsonDeserialize(contentAs = classOf[Double])  _double:Option[Double],
                                     child1:Option[SomeOtherPojo] // @JsonDeserialize not needed here
                                   )

PS: Scala Option combined with Polymorphism does not work in jackson-scala-module and therefore not this project either.

Code - Using Java

    ObjectMapper objectMapper = new ObjectMapper();
    JsonSchemaGenerator jsonSchemaGenerator = new JsonSchemaGenerator(objectMapper);

    // If using JsonSchema to generate HTML5 GUI:
    // JsonSchemaGenerator html5 = new JsonSchemaGenerator(objectMapper, JsonSchemaConfig.html5EnabledSchema() );

    // If you want to configure it manually:
    // JsonSchemaConfig config = JsonSchemaConfig.create(...);
    // JsonSchemaGenerator generator = new JsonSchemaGenerator(objectMapper, config);


    JsonNode jsonSchema = jsonSchemaGenerator.generateJsonSchema(YourPOJO.class);

    String jsonSchemaAsString = objectMapper.writeValueAsString(jsonSchema);

Nullable types

Out of the box, the generator does not support nullable types. There is a preconfigured JsonSchemaGenerator configuration shortcut that can be used to enable them:

JsonSchemaConfig config = JsonSchemaConfig.nullableJsonSchemaDraft4();
JsonSchemaGenerator generator = new JsonSchemaGenerator(objectMapper, config);

Under the hood nullableJsonSchemaDraft4 toggles the useOneOfForOption and useOneOfForNullables properties on JsonSchemaConfig.

When support is enabled, the following types may be made nullable:

  • Use Optional<T> (or Scala's Option)
  • Use a non-optional, non-primitive type (IE: String, Boolean, Integer etc)

If you've otherwise enabled support for nullable types, but need to suppress this at a per-property level, you can do this like so:

// A standard validation @NotNull annotation.
@NotNull
public String foo;

// Using the Jackson @JsonProperty annotation, specifying the attribute as required.
@JsonProperty(required = true)
public String bar;

Using JSON Views

Using JSON Views is most helpful for an API for various clients that will receive different output fields out of the same class, by calling different service endpoints. While support for these is not built in jsonSchema, it is handy to know how to use them with it since it is not an obvious process unless you are very familiar with the Jackson API.

Hence, let's suppose that you want to filter YourPojo using properties marked with the view Views.MyView.

Here is how to do it in Scala:

    val objectMapper = new ObjectMapper

    objectMapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION)
    objectMapper.setConfig(objectMapper.getSerializationConfig().withView(Views.MyView.class))

    val jsonSchemaGenerator = new JsonSchemaGenerator(objectMapper)
    val jsonSchema:JsonNode = jsonSchemaGenerator.generateJsonSchema(classOf[YourPOJO])

    val jsonSchemaAsString:String = objectMapper.writeValueAsString(jsonSchema)

And here is the equivalent for Java:

    ObjectMapper objectMapper = new ObjectMapper();
		
    // Disabling default View so only the properties that matter are output
    objectMapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
	    
    // And the trick: grab the serializationConfig and define the desired view
    objectMapper.setConfig(objectMapper.getSerializationConfig().withView(Views.MyView.class));
	    
    // Then, proceed as usual. Only fields and classes annotated with MyView will appear in the schema
    JsonSchemaGenerator generator = new JsonSchemaGenerator(objectMapper);
    JsonNode jsonSchema = generator.generateJsonSchema(SearchResult.class);
    String jsonSchemaAsString = objectMapper.writeValueAsString(jsonSchema);

Subclass-resolving using reflection

In some cases it is needed to find extra info about classes not found in jackson data. https://github.com/classgraph/classgraph is used to solve this problem.

By default we scan the entire classpath. This can be slow, so it is better to customize what to scan.

This is how you can configure what mbknor-jackson-jsonSchema should scan

in Scala:

    // Scan only some packages (this is faster)
    
    val resolver = SubclassesResolverImpl()
                    .withPackagesToScan(List("this.is.myPackage"))
                    .withClassesToScan(List("this.is.myPackage.MyClass")) // and/or this one
                    //.withClassGraph() - or use this one to get full control..       
    
    config = config.withSubclassesResolver( resolver )

.. or in Java:

    // Scan only some packages (this is faster)
    
    final SubclassesResolver resolver = new SubclassesResolverImpl()
                                            .withPackagesToScan(Arrays.asList(
                                               "this.is.myPackage"
                                            ))
                                            .withClassesToScan(Arrays.asList( // and/or this one
                                               "this.is.myPackage.MyClass"
                                            ))
                                            //.withClassGraph() - or use this one to get full control..       
    
    config = config.withSubclassesResolver( resolver )

Choosing which DRAFT to generate

This jsonSchema-generator was originally written to generate schema according to DRAFT-04. Later more drafts/versions of jsonSchema has arrived.

I've been asked by other developers to not only support DRAFT-04, so I've invested some time reading all the migration guides from DRAFT-04 to DRAFT-06 to DRAFT-07 to DRAFT-2019-09 (Migrating from older drafts).

And from what I can see, the only part of the schema generated by this project that is different, is the schema-url.

Therefor I've concluded that this project can generate valid schema for all the versions.

But since the schema-url is different, you must specify which version to use.

In the future, if someone finds bugs and/or add new features, we'll add special-casing for the different versions when generating the schema.

This is how you specify which version/draft to use:

Specify draft-version in Scala:

    val config:JsonSchemaConfig = JsonSchemaConfig.vanillaJsonSchemaDraft4.withJsonSchemaDraft(JsonSchemaDraft.DRAFT_07
    val jsonSchemaGenerator = new JsonSchemaGenerator(objectMapper, config = config)

Specify draft-version in Java:

    JsonSchemaConfig config = JsonSchemaConfig.vanillaJsonSchemaDraft4().withJsonSchemaDraft(JsonSchemaDraft.DRAFT_07;
    JsonSchemaGenerator generator = new JsonSchemaGenerator(objectMapper, config);

Backstory

At work we've been using the original jackson-module-jsonSchema to generate schemas used when rendering dynamic GUI using https://github.com/json-editor/json-editor.

Recently we needed to support POJO's using polymorphism like this:

    @JsonTypeInfo(
            use = JsonTypeInfo.Id.NAME,
            include = JsonTypeInfo.As.PROPERTY,
            property = "type")
    @JsonSubTypes({
            @JsonSubTypes.Type(value = Child1.class, name = "child1"),
            @JsonSubTypes.Type(value = Child2.class, name = "child2") })
    public abstract class Parent {

        public String parentString;

    }

This is not supported by the original jackson-module-jsonSchema. I have spent many hours trying to figure out how to modify/improve it without any luck, and since it is implemented in such a complicated way, I decided to instead write my own jsonSchema generator from scratch.

mbknor-jackson-jsonschema's People

Contributors

abersnaze avatar andrii-nix avatar bbyk avatar big-andy-coates avatar ebolwidt-atlassian avatar hronom avatar jekkel avatar kog avatar luqasn avatar mbknor avatar robbrazier avatar schleichardt avatar sserhii-95 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mbknor-jackson-jsonschema's Issues

Error with acceptJsonFormatVisitor ... on Object?

08:54:16.464 [main] WARN com.kjetland.jackson.jsonSchema.JsonSchemaGenerator - Not able to generate jsonSchema-info for type: [simple type, class java.lang.Object] - probably using custom serializer which does not override acceptJsonFormatVisitor

I think I know what type is really at issue, but the main question is, what should my implementation of this method do?

StackOverflowError due to subclassing

I am trying to create a schema for a base class and class extending this base class with an additional attribute. (De)serialization with Jackson works fine, but when I try to generate a schema I get a StackOverflowError.

Am I missing something / doing something illegal in my implementation or is this a bug?

BaseClass.java

package schemaproblem;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({ @JsonSubTypes.Type(value = BaseClass.class, name = "BaseClass"),
        @JsonSubTypes.Type(value = ExtendedClass.class, name = "ExtendedClass") })
@JsonInclude(Include.NON_EMPTY)
public class BaseClass<T extends BaseClass<T>> {

    private int myInteger;

    public int getMyInteger() {
        return myInteger;
    }

    public T setMyInteger(int myInteger) {
        this.myInteger = myInteger;
        return (T) this;
    }

    @Override
    public String toString() {
        return "BaseClass [myInteger=" + myInteger + "]";
    }

}

ExtendedClass.java

package schemaproblem;

public class ExtendedClass extends BaseClass<ExtendedClass> {

    private String anotherDetail;

    public String getAnotherDetail() {
        return anotherDetail;
    }

    public ExtendedClass setAnotherDetail(String anotherDetail) {
        this.anotherDetail = anotherDetail;
        return this;
    }

    @Override
    public String toString() {
        return super.toString() + " -> ExtendedClass [anotherDetail=" + anotherDetail + "]";
    }
}

SchemaExample.java

package schemaproblem;

import java.io.File;
import java.io.IOException;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.kjetland.jackson.jsonSchema.JsonSchemaGenerator;

import scala.Option;

public class SchemaExample {

    public static final String exampleFile = "/tmp/example.json";
    private ObjectMapper mapper;

    public SchemaExample() {
        mapper = new ObjectMapper();
        mapper.findAndRegisterModules();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
    }

    public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
        SchemaExample main = new SchemaExample();
        main.readAndWriteBaseClassJson();
        main.readAndWriteExtendedJson();
        main.writeSchemav4();
    }

    public void readAndWriteBaseClassJson() throws JsonParseException, JsonMappingException, IOException {
        BaseClass<?> example = new BaseClass<>().setMyInteger(42);
        mapper.writeValue(new File(exampleFile), example);
        BaseClass<?> readExample = mapper.readValue(new File(exampleFile), BaseClass.class);
        System.out.println("read from json file: " + readExample);
    }

    public void readAndWriteExtendedJson() throws JsonGenerationException, JsonMappingException, IOException {
        ExtendedClass example = new ExtendedClass().setMyInteger(42).setAnotherDetail("hello world");
        mapper.writeValue(new File(exampleFile), example);
        ExtendedClass readExample = mapper.readValue(new File(exampleFile), ExtendedClass.class);
        System.out.println("read from json file: " + readExample);
    }

    public void writeSchemav4() throws JsonGenerationException, IOException {
        JsonSchemaGenerator jsonSchemaGenerator = new JsonSchemaGenerator(mapper);
        Option<String> emtpyOption = Option.empty();
        JsonNode jsonSchema = jsonSchemaGenerator.generateJsonSchema(BaseClass.class, emtpyOption, emtpyOption);
        System.out.println("schema:\n" + mapper.writeValueAsString(jsonSchema));
    }

}

The output

read from json file: BaseClass [myInteger=42]
read from json file: BaseClass [myInteger=42] -> ExtendedClass [anotherDetail=hello world]
Exception in thread "main" java.lang.StackOverflowError
    at scala.collection.mutable.ArrayBuilder.<init>(ArrayBuilder.scala:22)
    at scala.collection.mutable.ArrayBuilder$ofRef.<init>(ArrayBuilder.scala:56)
    at scala.collection.mutable.ArrayBuilder$.make(ArrayBuilder.scala:47)
    at scala.Array$$anon$2.apply(Array.scala:63)
    at scala.Array$$anon$2.apply(Array.scala:62)
    at scala.collection.TraversableLike$class.builder$1(TraversableLike.scala:229)
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:233)
    at scala.collection.mutable.ArrayOps$ofRef.map(ArrayOps.scala:186)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$5.apply(JsonSchemaGenerator.scala:447)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$5.apply(JsonSchemaGenerator.scala:447)
    at scala.Option.map(Option.scala:146)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper.expectObjectFormat(JsonSchemaGenerator.scala:446)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.acceptJsonFormatVisitor(BeanSerializerBase.java:809)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.acceptJsonFormatVisitor(DefaultSerializerProvider.java:580)
    at com.fasterxml.jackson.databind.ObjectMapper.acceptJsonFormatVisitor(ObjectMapper.java:3641)
    at com.fasterxml.jackson.databind.ObjectMapper.acceptJsonFormatVisitor(ObjectMapper.java:3620)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$expectObjectFormat$1$$anonfun$7.apply(JsonSchemaGenerator.scala:466)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$expectObjectFormat$1$$anonfun$7.apply(JsonSchemaGenerator.scala:463)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$DefinitionsHandler.getOrCreateDefinition(JsonSchemaGenerator.scala:160)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$expectObjectFormat$1.apply(JsonSchemaGenerator.scala:462)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$expectObjectFormat$1.apply(JsonSchemaGenerator.scala:459)
    at scala.collection.immutable.List.foreach(List.scala:381)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper.expectObjectFormat(JsonSchemaGenerator.scala:458)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.acceptJsonFormatVisitor(BeanSerializerBase.java:809)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.acceptJsonFormatVisitor(DefaultSerializerProvider.java:580)
    at com.fasterxml.jackson.databind.ObjectMapper.acceptJsonFormatVisitor(ObjectMapper.java:3641)
    at com.fasterxml.jackson.databind.ObjectMapper.acceptJsonFormatVisitor(ObjectMapper.java:3620)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$expectObjectFormat$1$$anonfun$7.apply(JsonSchemaGenerator.scala:466)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$expectObjectFormat$1$$anonfun$7.apply(JsonSchemaGenerator.scala:463)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$DefinitionsHandler.getOrCreateDefinition(JsonSchemaGenerator.scala:160)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$expectObjectFormat$1.apply(JsonSchemaGenerator.scala:462)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$expectObjectFormat$1.apply(JsonSchemaGenerator.scala:459)
    at scala.collection.immutable.List.foreach(List.scala:381)

[...]

Preserve Enum values order

The order of enum values is not preserved, due to the use of a set in the implementation.

Note that com.fasterxml.jackson.module.jsonSchema keeps the order intact, which is useful for enums representing days, months, etc.

Add support for @NotBlank annotation

Looking at the Bean Validation spec, @NotBlank states:

/**
 * The annotated element must not be {@code null} and must contain at least one
 * non-whitespace character. Accepts {@code CharSequence}.
 *
 * @author Hardy Ferentschik
 * @since 2.0
 *
 * @see Character#isWhitespace(char)
 */

It looks like a schema for a String property marked as @NotBlank would:

For properties that aren't Strings, nothing should happen - although perhaps we should log. IE: "property X is marked as @NotBlank but is not a String, ignoring..."

Question: Is is possible to have sub-objects without $ref?

Hi,

just a short question because I wasn't able to find out if the generator is configurable to support including objects without using definitions and $ref.
Because I am trying to integrate it with angular-schema-form which currently does not support $ref and definitions.

Handling Maps

I have the following simple classes. The top-level class has a Map of another class.

     public class Shape {
        private Map<String, Color> _colors;
  
        public Map<String, Color> getColors() {
           return _colors;
        }
     }
   
     public class Color {
       public String colorName;
    }

Information about the internal class completely disappears with mbknor-jackson-jsonSchema:

    {
      "$schema" : "http://json-schema.org/draft-04/schema#",
      "title" : "Shape",
      "type" : "object",
      "additionalProperties" : false,
      "properties" : {
        "colors" : {
          "type" : "object",
          "additionalProperties" : true
        }
      }
    }

The jackson-module-jsonScheme produces this instead:

    {
      "type" : "object",
      "id" : "urn:jsonschema:org:batfish:client:Client:Shape",
      "properties" : {
        "colors" : {
          "type" : "object",
          "additionalProperties" : {
            "type" : "object",
            "id" : "urn:jsonschema:org:batfish:client:Client:Color",
            "properties" : {
              "colorName" : {
                "type" : "string"
              }
            }
          }
        }
      }
    }

Is there an easy fix to this issue?

PS: Lists are being correctly handled.

Add support for JsonTypeInfo.Id.CLASS

The code currently supports only JsonTypeInfo.Id.NAME

    if ( jsonTypeInfo.use != JsonTypeInfo.Id.NAME) throw new Exception("We only support polymorphism using jsonTypeInfo.use == JsonTypeInfo.Id.NAME")

I have classes that use JsonTypeInfo.Id.CLASS and running the code on them after commenting the line above seems to do the right thing.

Why does the requirement exist and can it be loosened to include JsonTypeInfo.Id.CLASS?

Add enumClass for Enum attribute types

I need to have the associated enum class for enum values, not just the values of the enum.
Let's suppose i have this:

package demo;
public enum Type { 
    A,
    B
}

package demo;
public class DemoBean {
    private Type type;
}

The JSON Schema resulting on this would be:

{
  "$schema" : "http://json-schema.org/draft-04/schema#",
  "title" : "Demo Bean",
  "type" : "object",
  "additionalProperties" : false,
  "properties" : {
    "type" : {
      "type" : "string",
      "enum" : [ "A", "B" ]
    }
  }
}

I would like to have this result instead:

{
  "$schema" : "http://json-schema.org/draft-04/schema#",
  "title" : "Demo Bean",
  "type" : "object",
  "additionalProperties" : false,
  "properties" : {
    "type" : {
      "type" : "string",
      "enum" : [ "A", "B" ],
      "enumClass": "demo.Type"
    }
  }
}

Is there any configuration i have missing to do this or workaround i can do?

Thank you.

Not able to run with scala version 2.11.8 & 2.10.6

I tried for both & in both showing the following exception:

Exception in thread "main" java.lang.NoSuchMethodError: scala.Product.$init$(Lscala/Product;)V
	at com.kjetland.jackson.jsonSchema.JsonSchemaConfig.<init>(JsonSchemaGenerator.scala:146)
	at com.kjetland.jackson.jsonSchema.JsonSchemaConfig$.<init>(JsonSchemaGenerator.scala:41)
	at com.kjetland.jackson.jsonSchema.JsonSchemaConfig$.<clinit>(JsonSchemaGenerator.scala)
	at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator.<init>(JsonSchemaGenerator.scala:179)
        ...........

So basically how can this be resolved?

Thanks in advance ....

Joda DateTime Parsing.

Hello,

I just started using your plugin and was wondering how I can get a Joda DateTime object to come out as "type" : "string" and "format": "date-time" I created a test config in java.

Map<String, String> types = new HashMap<>();
types.put(DateTime.class.getName(), "date-time");
types.put(URL.class.getName(), "uri");

JsonSchemaConfig config = JsonSchemaConfig.create(
      false, // Auto  generate title for properties.
      Optional.empty(), // Default Array Format.
      false, // Use oneOf for option.
      false, // Use oneOf for nullables.
      false, // Use property ordering.
      false, // Hide polymorphism type property
      false, // Disable warnings.
      false, // Use min length for not null
      false, // Use type id for definition name.
      types, // Custom type format mapping.
      false, // Use multiple editor select via property.
      Collections.EMPTY_SET // Unique item classes.
);

Thanks
Drew

Please support JsonSchema "default" values

JsonSchema mentions the usage of the default keyword in this chapter:
http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.2

This means that a Java class like this:

public class TestJsonPojo {
	public int test = 45;
}

Should translate to the following Schema:

{
	"$schema": "http://json-schema.org/draft-04/schema#",
	"title": "Test Json Pojo",
	"type": "object",
	"additionalProperties": false,
	"properties": {
		"test": {
			"propertyOrder": 1,
			"type": "integer",
			"default": 45,                      <=======
			"title": "Test"
		}
	},
	"required": ["test"]
}

The Json Schema Editor from the readme supports default values and these will make it very easy to supply some pre-filled data into forms.

This will also mean that getters have to be evaluated because some objects set different values when created through their default constructor. This especially useful when using inheritance where e.g. a subclass is setting a value in its superclass, such as an id.

Upgrade javax.validation to 2.0+

JSR-380 (Bean Validation 2.0) went live in August 2017, and contains a number of useful validation annotations. A human-readable summary of what's new is available at beanvalidation.org.

I'm using some of these annotations (particularly @NotBlank and @NotEmpty) for my domain model validation via Hibernate Validator, which means that I also need to combine annotations with @NotNull in order to generate correct schema. This causes some less than desirable behavior in that exactly which annotation gets tripped seems to be non-deterministic.

I've got a PR for this change and some prototypes for generating schema for the aforementioned annotations. I figure I'd post an issue before going too far - no point in building code/test cases if folks don't want to upgrade.

Dependency Changes

  • the build.sbt dependency on javax.validation:validation-api changes from 1.1.0.Final to 2.0.1.Final
  • Neither version of the artifact has any transitive dependencies (1.1.0.Final, 2.0.1.Final)

Improve null handling for non-primitive/non-optional types

Expand the support that issue #2 added for optional/option types, and allow any property that satisfies the following requirements to be nullable:

  • Is not a primitive class
  • Is not marked as @NotNull
  • Does not have a @JsonProperty with the required parameter set

The output should match that of optional classes: a oneOf array consisting of a null type, as well as the "real" expected type.

Example:

public class Example {
    public String foo;

    @NotNull
    public String bar;

    @JsonProperty(required = true)
    public String baz;
}

should produce:

{
  "$schema" : "http://json-schema.org/draft-04/schema#",
  "title" : "Example",
  "type" : "object",
  "additionalProperties" : false,
  "properties" : {
    "foo" : {
      "oneOf" : [ {
        "type" : "null",
        "title" : "Not included"
      }, {
        "type" : "string"
      } ]
    },
    "bar" : {
      "type" : "string"
    },
    "baz" : {
      "type" : "string"
    }
  },
  "required" : [ "bar", "baz" ]
}

This should be a huge help to folks using the the schema generator in Java-land, especially where optional types may not be... an option.

PS - this comes with a pull request! See #31

Java Generics parameterized classes are defined as one.

Definately an edge case ... but it is possible I belive.

I have a class like this:

class Config {
T type;
Integer iCommon;
String sCommon;
};

class MyConfig {
Config aConfig;
Config bConfig;
..
}

( plus all the normal annoations )

THe result is a schema that defines aConfig, bConfig ... as identical,

"aConfig" : { "$ref" : '#/definitions/Config' }

"bConfig" : { "$ref" : '#/definitions/Config' }

I was expectecting that .. what I didnt expect was that "Config" pulled in the correct enum values
from EnumA .

"type" : { 
   "type" : "string" , 
  "enum" : [ "all","the","right","enums","from","EnumA" ]

..

Not bad !
Suggestions besides "Dont do that ?"
derived classes maybe ?
class ConfigA extends Config ; ....

Ideally the generics should be recognized as distinct JSON types -- for this purpose atleast.
( debatable in Java if they are different types or not -- yes at compile , sorta, no at runtime).

Is there a way to abuse an existing annotation ?

support JsonTypeInfo.As.EXISTING_PROPERTY

From @andrii-NIX originally posted on #27:

Hi,

Could you please also add support for JsonTypeInfo.As.EXISTING_PROPERTY?

Currently Spring DataRest doesn't support JsonTypeInfo.As.PROPERTY, as a workaround custom field could be created.
But if using it with together with PROPERTY then two "type" fields are generated when serializing with ObjectMapper, so having EXISTING_PROPERTY will solve the issue.

Thanks.

All class hierarchy properties are pushed down to the child

I have the following class hierarchy:

`public abstract class ClassHierarchy {

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = Parent1.class, name = "Parent 1"),
        @JsonSubTypes.Type(value = Parent2.class, name = "Parent 2")
})
public static class AbstractParent {

    @Getter
    @Setter
    private String abstractParentName;
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({ @JsonSubTypes.Type(value = ChildOfParent1.class, name = "Child Of Parent 1") })
public static class Parent1 extends AbstractParent {

    @Getter
    @Setter
    private String parent1Description;
}

public static class ChildOfParent1 extends Parent1 {

    @Getter
    @Setter
    private Integer childOfParentOneSize;

    @Getter
    @Setter
    private Status childOfParentOneStatus;
}

public static class Parent2 extends AbstractParent {

    @Getter
    @Setter
    private String parent2description;

    @Getter
    @Setter
    private Status parent2Status;
}

public static enum Status {

    ONLINE,
    AWAY,
    OFFLINE;
}

}`

The generated schema is:

{
  "$schema" : "http://json-schema.org/draft-04/schema#",
  "title" : "Abstract Parent",
  "oneOf" : [ {
    "$ref" : "#/definitions/Parent1"
  }, {
    "$ref" : "#/definitions/Parent2"
  } ],
  "definitions" : {
    "Parent1" : {
      "oneOf" : [ {
        "$ref" : "#/definitions/ChildOfParent1"
      } ]
    },
    "ChildOfParent1" : {
      "type" : "object",
      "additionalProperties" : false,
      "properties" : {
        "type" : {
          "type" : "string",
          "enum" : [ "Child Of Parent 1" ],
          "default" : "Child Of Parent 1"
        },
        "abstractParentName" : {
          "propertyOrder" : 1,
          "type" : "string",
          "title" : "Abstract Parent Name"
        },
        "parent1Description" : {
          "propertyOrder" : 2,
          "type" : "string",
          "title" : "Parent 1 Description"
        },
        "childOfParentOneSize" : {
          "propertyOrder" : 3,
          "type" : "integer",
          "title" : "Child Of Parent One Size"
        },
        "childOfParentOneStatus" : {
          "propertyOrder" : 4,
          "type" : "string",
          "enum" : [ "ONLINE", "AWAY", "OFFLINE" ],
          "title" : "Child Of Parent One Status"
        }
      },
      "title" : "Child Of Parent 1",
      "required" : [ "type" ],
      "options" : {
        "multiple_editor_select_via_property" : {
          "property" : "type",
          "value" : "Child Of Parent 1"
        }
      }
    },
    "Parent2" : {
      "type" : "object",
      "additionalProperties" : false,
      "properties" : {
        "type" : {
          "type" : "string",
          "enum" : [ "Parent 2" ],
          "default" : "Parent 2"
        },
        "abstractParentName" : {
          "propertyOrder" : 1,
          "type" : "string",
          "title" : "Abstract Parent Name"
        },
        "parent2description" : {
          "propertyOrder" : 2,
          "type" : "string",
          "title" : "Parent 2description"
        },
        "parent2Status" : {
          "propertyOrder" : 3,
          "type" : "string",
          "enum" : [ "ONLINE", "AWAY", "OFFLINE" ],
          "title" : "Parent 2 Status"
        }
      },
      "title" : "Parent 2",
      "required" : [ "type" ],
      "options" : {
        "multiple_editor_select_via_property" : {
          "property" : "type",
          "value" : "Parent 2"
        }
      }
    }
  }
}

I am going to create a custom ui form per each class hierarchy from provided schema

Is it possible to show the property of the class Parent1 which is: 'parent1Description'

Possible bug with using same class in multiple parents

Hi,
I am running into an issue where I am using the same class in two different objects which both subclass a parent abstract class (see error below) I have created a full working example in Java that can be found here. The code can be ran from the main method in SchemaGenerator.java. It may be something simple like a missing annotation or something but I am not sure. Please let me know if you need additional information.

Exception in thread "main" java.lang.Exception: Wrong class - working on class model.entities.Person - got class model.properties.Address
	at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$DefinitionsHandler.getOrCreateDefinition(JsonSchemaGenerator.scala:163)
	at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper.expectObjectFormat(JsonSchemaGenerator.scala:727)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.acceptJsonFormatVisitor(BeanSerializerBase.java:809)
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.acceptJsonFormatVisitor(DefaultSerializerProvider.java:580)
	at com.fasterxml.jackson.databind.ObjectMapper.acceptJsonFormatVisitor(ObjectMapper.java:3650)
	at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$7$$anon$7.myPropertyHandler(JsonSchemaGenerator.scala:648)
	at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$7$$anon$7.optionalProperty(JsonSchemaGenerator.scala:703)

Question: Is this the right module for me?

Hi,

I am posting this because I have been evaluating a number of JsonSchemaGenerators for Java. Yours seems to be nicely written, supports draft-v4 and uses javax Annotations together with Jackson, which is perfect. Only problem is that it was written using Scala. Yesterday I got mixed Scala and Java compilation working after crashing Eclipse for hours because of the Scala integration.
I started with a simple test object like this:
public class TestJson {
public int test = 45;
}
and I wondered why none of the JsonSchema Generators will output the "default" value of 45. I tryed to add this, but I guess I have to learn a little Scala first...
I think I might have to edit your module in a different project and will then import the artifact with maven to easen the workflow a little.

Support JavaType in addition to Class<?> for generateJsonSchema

Currently only Class<?> is supported as argument to the schema generator. Internally I saw it's delegated eventually to jackson as JavaType. Directly providing an overload for generation of the schema of a JavaType would allow schema generation for generic classes (ParameterizedType).

Inlining abstract classes

Hi, I have an inter-linked class structure, the essence of which can be captured as the following:

    public class SchemaTest {
       public List<Parent> parents;   
    }
    
    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "class")
    @JsonSubTypes({
       @JsonSubTypes.Type(value = Child1.class),
       @JsonSubTypes.Type(value = Child2.class)})
     public abstract class Parent {
      }
       
     public class Child1 extends Parent {
        public List<Parent> parents1;      
     }
       
     public class Child2 extends Parent {      
        public List<Parent> parents2;
     }

The module generates schema by essentially inlining the abstract class, as follows:

  {
    "$schema" : "http://json-schema.org/draft-04/schema#",
    "title" : "Schema Test",
    "type" : "object",
    "additionalProperties" : false,
    "properties" : {
      "parents" : {
        "type" : "array",
        "items" : {
          "oneOf" : [
            {
              "$ref" : "#/definitions/Child2"
            },
            {
              "$ref" : "#/definitions/Child1"
            }
          ]
        }
      }
    },
    "definitions" : {
      "Child2" : {
        "type" : "object",
        "additionalProperties" : false,
        "properties" : {
          "class" : {
            "type" : "string",
            "enum" : [
              null
            ],
            "default" : null
          },
          "parents2" : {
            "type" : "array",
            "items" : {
              "oneOf" : [
                {
                  "$ref" : "#/definitions/Child2"
                },
                {
                  "$ref" : "#/definitions/Child1"
                }
              ]
            }
          }
        },
        "title" : null,
        "required" : [
          "class"
        ]
      },
      "Child1" : {
        "type" : "object",
        "additionalProperties" : false,
        "properties" : {
          "class" : {
            "type" : "string",
            "enum" : [
              null
            ],
            "default" : null
          },
          "parents1" : {
            "type" : "array",
            "items" : {
              "oneOf" : [
                {
                  "$ref" : "#/definitions/Child2"
                },
                {
                  "$ref" : "#/definitions/Child1"
                }
              ]
            }
          }
        },
        "title" : null,
        "required" : [
          "class"
        ]
      }
    }
  }

The issue is that this representation is losing information that all of the oneOf's are of the same type (Parent). Would it be possible to generate something like the following?

   {
    "$schema" : "http://json-schema.org/draft-04/schema#",
    "title" : "Schema Test",
    "type" : "object",
    "additionalProperties" : false,
    "properties" : {
      "parents" : {
        "type" : "array",
        "items" : {
          "$ref" : "#/definitions/Parent"
        }
      }
    },
    "definitions" : {
      "Parent" : {
        "oneOf" : [
          {
            "$ref" : "#/definitions/Child2"
          },
          {
            "$ref" : "#/definitions/Child1"
          }
        ]
      },
      "Child2" : {
        "type" : "object",
        "additionalProperties" : false,
        "properties" : {
          "class" : {
            "type" : "string",
            "enum" : [
              null
            ],
            "default" : null
          },
          "parents2" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/definitions/Parent"
            }
          }
        },
        "title" : null,
        "required" : [
          "class"
        ]
      },
      "Child1" : {
        "type" : "object",
        "additionalProperties" : false,
        "properties" : {
          "class" : {
            "type" : "string",
            "enum" : [
              null
            ],
            "default" : null
          },
          "parents1" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/definitions/Parent"
            }
          }
        },
        "title" : null,
        "required" : [
          "class"
        ]
      }
    }
  }

Schema mostly disappears due to polymorphism annotations

The schema does not descend into the contents of this map; it does not include BaseAttribute and its subclasses. If the annotations are not present, then the schema includes all of that.

    @JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, include = JsonTypeInfo.As.PROPERTY, property = "type")
    @JsonTypeIdResolver(DmTypeIdResolver.class)
    public abstract Map<String, BaseAttribute> getAttributes();

Properties in object constructor are required.

JSON object properties which are not required are being set as required when setting those values in that objects constructor.

public class Example {
    public Example() {
        field1 = 4;
    }
    @JsonPropertyDescription("asdsad")
    private Object field1;
    
    @JsonPropertyDescription("asdsad")
    private Object field2;
}

Then in my schema I see:

"required" : [ "field1"],

This is trouble for me since I never specified.

@JsonProperty(required=true)

and even when I say

@JsonProperty(required=false)

it shows up as required anyway.

Dependency to too 'old' fast-classpath-scanner version 2.0.20 fails schema generation

We're using mbknor-jackson-jsonschema_2.12 version 1.0.28

when asm 6.0 is in the classpath fast-classpath-scanner version 2.0.20 fails the schema generation with

Caused by: java.lang.RuntimeException: Unknown constant pool tag [I@3d565ef8 in classfile module-info.class (element size unknown, cannot continue reading class. Please report this on the FastClasspathScanner GitHub page.
	at io.github.lukehutch.fastclasspathscanner.scanner.ClassfileBinaryParser.readClassInfoFromClassfileHeader(ClassfileBinaryParser.java:562)
	at io.github.lukehutch.fastclasspathscanner.scanner.ClasspathElementZip.openInputStreamAndParseClassfile(ClasspathElementZip.java:306)
	at io.github.lukehutch.fastclasspathscanner.scanner.ClasspathElement.parseClassfiles(ClasspathElement.java:321)
	at io.github.lukehutch.fastclasspathscanner.scanner.Scanner$3.processWorkUnit(Scanner.java:373)
	at io.github.lukehutch.fastclasspathscanner.scanner.Scanner$3.processWorkUnit(Scanner.java:367)
	at io.github.lukehutch.fastclasspathscanner.utils.WorkQueue.runWorkLoop(WorkQueue.java:133)
	... 6 common frames omitted

It's a fixed bug in fast-classpath-scanner issue #141

Does not work in Spring Boot 2

Hi,

Tried running in spring boot 2 but get the following exception:

java.lang.RuntimeException: Unknown constant pool tag [I@63d345a0 in classfile module-info.class (element size unknown, cannot continue reading class. Please report this on the FastClasspathScanner GitHub page.

So I tried to change the fast classpath scanner to a newer verision however get this, which I guess is expected:

Caused by: java.lang.IllegalArgumentException: A class and its auxiliary class have different superclasses: class extends scala.runtime.AbstractFunction1<java.util.Properties, scala.collection.convert.Wrappers$JPropertiesWrapper> implements scala.Serializable ; class extends scala.collection.mutable.AbstractMap<java.lang.String, java.lang.String> implements scala.Product, scala.Serializable

JSON Views support (workaround in description)

Just started to use this project and I could not find an explicit way to use views when generating a schema.
i.e. @JSONVIEW(Views.MyView.class)
This is most helpful for an API for internal/external clients that will receive different outputs out of the same class, by calling different service endpoints.
So if I could use this feature I would be able to distribute different schemas out of a single response class for each designed client.
Now, I have found a solution to have this working by fiddling with the ObjectMapper API. This kind of make it not really necessary to modify the jsonSchema code. I am sort of new with Github so rather than an issue this might be more like an advice post. Hence I apologize beforehand if opening an issue is of any distress for you.
Anyway, here is how you do it:

// Our vanilla ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
		
// Disabling default View so only the stuff that matters is output
objectMapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
	    
// And the trick: grab the serializationConfig and define the desired view
objectMapper.setConfig(objectMapper.getSerializationConfig().withView(Views.MyView.class));
	    
// Then, proceed as usual. Only fields and classes annotated with MyView will appear in the schema
JsonSchemaGenerator generator = new JsonSchemaGenerator(objectMapper);

Since this is simple enough, you might want to consider adding the example to the documentation and/or close the issue so that at least people looking for the feature can read it here.

Cheers and thanks for sharing your very useful code.

"format": "date" is used for java.time.LocalDate

For a field with type java.time.LocalDate a corresponding schema definition is generated as

		"dateOfBirth": {
			"type": "string",
			"format": "date"
		}

I like that date and date-time are treated differently but, unfortunately, "format": "date" is not listed as a defined format of the json schema, only date-time is. As a result, per json schema spec, If the type of the instance to validate is not in this set, validation for this format attribute and instance SHOULD succeed. This makes the custom format date not very useful for validation unless a validator supports this specific custom format. Similarly, json schema -> code generators like jsonSchema2Pojo are going to ignore this custom format.

Should java.time.LocalDate use "format": "date-time" by default? If a custom format attribute like date is desired then @JsonSchemaFormat(value = "date") can be used.

Version used: com.kjetland:mbknor-jackson-jsonschema_2.11:1.0.9.

@JsonPropertyDescription is not implemented?

It appears that @JsonPropertDescription is not implemented?

I have this class:
public class Shape {
@JsonPropertyDescription("this is name")
public String name;
}

The output of jackson-modeule-jsonSchema:
{
"type" : "object",
"id" : "urn:jsonschema:org:batfish:client:Client:Shape",
"properties" : {
"name" : {
"type" : "string",
"description" : "this is name"
}
}
}

The output of mbknor-jackson-jsonSchema:
{
"$schema" : "http://json-schema.org/draft-04/schema#",
"title" : "Shape",
"type" : "object",
"additionalProperties" : false,
"properties" : {
"name" : {
"type" : "string"
}
}
}

Is this something that is on your todo (soon) list?

Enums repeated over and over, any alternative?

Is there any way to get Java enums to turn into some sort of top-level item? I get the same long enumeration over and over in the schema.

"script": {
"type": "string",
"enum": [
"Dsrt",
"Bamu",
...
}
There are many items in the list, and it turns up in my schema many times.

The order is also rather randomized, FWIW.

Cannot get schema for POJO (java)

Hello.

I'm trying to use this module with simple POJO class to futher validate with json scheme v4, but receiving exception. Similar code with jackson-module-jsonSchema works ok.

java.lang.NoSuchMethodError: java.lang.Class.getDeclaredAnnotation(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;

    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper.expectObjectFormat(JsonSchemaGenerator.scala:288)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.acceptJsonFormatVisitor(BeanSerializerBase.java:809)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.acceptJsonFormatVisitor(DefaultSerializerProvider.java:580)
    at com.fasterxml.jackson.databind.ObjectMapper.acceptJsonFormatVisitor(ObjectMapper.java:3641)
    at com.fasterxml.jackson.databind.ObjectMapper.acceptJsonFormatVisitor(ObjectMapper.java:3620)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator.generateJsonSchema(JsonSchemaGenerator.scala:433)
    at ru.infon.mas.protocol.JsonSchemaValidatorTest.simple(JsonSchemaValidatorTest.java:15)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)`

Here is my code:

package com.test;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.kjetland.jackson.jsonSchema.JsonSchemaGenerator;
import org.junit.Test;

public class JsonSchemaValidatorTest {
    @Test
    public void simple() throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new SimpleModule());
        JsonSchemaGenerator schemaGen = new JsonSchemaGenerator(mapper, false);
        final JsonNode jsonSchema = schemaGen.generateJsonSchema(Book.class);
        System.out.println(mapper.writeValueAsString(jsonSchema));
    }
}
package com.test;

public class Book {
    private String author;

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }
}

Very slow for first generation

Hi,
In an spring boot application when i generate class schema for the first time, it takes too long(approximately 12-13 seconds) to create json schema string.

I wonder what is taking time(classpath scanning, some initialization code vs.)

Thanks

Pattern Property Support

Hello,

I have a use case for supporting pattern properties in JSON schema. I am trying to achieve a Language Map similar to the one specified here https://www.w3.org/TR/json-ld/#language-maps.

Example:

{
    "ja": "忍者",
    "en": "Ninja",
    "cs": "Nindža"
  }

The idea is to support dynamic keys in the object but the type is always a string.

"LanguageMap": {
  "type": "object",
  "additionalProperties": false,
  "patternProperties": {
    "^[a-zA-Z0-9]+": {
      "type": "string"
    }
  }
}

The underlying data structure would be a Map<Locale, String>. I started creating a custom object and JsonSerializer. Is it possible to achieve this with your generator?

Thanks
Drew

Add support for @NotEmpty annotation

Looking at the Bean Validation spec, @NotEmpty states:

/**
 * The annotated element must not be {@code null} nor empty. Supported types are:
 * <ul>
 * <li>{@code CharSequence} (length of character sequence is evaluated)</li>
 * <li>{@code Collection} (collection size is evaluated)</li>
 * <li>{@code Map} (map size is evaluated)</li>
 * <li>Array (array length is evaluated)</li>
 * </ul>
 *
 * @author Emmanuel Bernard
 * @author Hardy Ferentschik
 *
 * @since 2.0
 */

It looks like this would affect JSON Strings, Objects, and Arrays:

  • The property should be marked as required
  • For Strings, a minLength of 1 would be generated for the property
  • For Arrays, a minItems of 1 would be generated for the property
  • For Objects, a minProperties of 1 would be generated for the property

For other types we can log something like "property X is marked @NotEmpty, but this does not apply to [type name], ignoring..."

The Shema Generator ignores JsonSubTypes-Infos added via JacksonMixInAnnotations

JsonSubTypes-Infos added via JacksonMixInAnnotations ( see http://wiki.fasterxml.com/JacksonMixInAnnotations ) are ignored.

Here is my sample Code, written in Java:

// sample_zoo.json:
//{
//  "animals":
//     [
//       {"type":"dog","name":"Spike","breed":"mutt",
//           "leashColor":"red"},
//       {"type":"cat","name":"Fluffy",
//           "favoriteToy":"spider ring"}
//     ]
//}

package jacksontest;

import java.util.List;

import org.slf4j.Logger;

import scala.Option;
import scala.Some;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.kjetland.jackson.jsonSchema.JsonSchemaConfig;
import com.kjetland.jackson.jsonSchema.JsonSchemaGenerator;

public class Foo {
    static Logger logger = org.slf4j.LoggerFactory.getLogger(Foo.class);

    static class Zoo {
        public List<Animal> animals;
    }

    static abstract class Animal {
        public String name;
    }

    static class Dog extends Animal {
        public String breed;
        public String leashColor;
    }

    static class Cat extends Animal {
        public String favoriteToy;
    }

    static class MyModule extends SimpleModule {
        @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
        @JsonSubTypes({ @Type(value = Dog.class, name = "dog"), @Type(value = Cat.class, name = "cat") })
        abstract class MixIn {

        }

        @Override
        public void setupModule(SetupContext context) {
            context.setMixInAnnotations(Animal.class, MixIn.class);
        }
    }

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new MyModule());

        // jackson read and write test
        Zoo zoo = mapper.readValue(Foo.class.getResourceAsStream("./sample_zoo.json"), Zoo.class);
        logger.info(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(zoo));

        // Html5 Schema generator test
        try {
            JsonSchemaGenerator html5 = new JsonSchemaGenerator(mapper, JsonSchemaConfig.html5EnabledSchema());
            Option<String> title = new Some<String>("MyZooTitle");
            Option<String> description = new Some<String>("MyZooDescription");
            JsonNode schema = html5.generateJsonSchema(Zoo.class, title, description);
            logger.info(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema));
        } catch (Exception e) {
            logger.error("Unexpected exception", e);
        }

    }
}

Here is the output with Exception:

2016-10-04T10:36:01.730 INFO  [jacksontest.Foo] - {
  "animals" : [ {
    "type" : "dog",
    "name" : "Spike",
    "breed" : "mutt",
    "leashColor" : "red"
  }, {
    "type" : "cat",
    "name" : "Fluffy",
    "favoriteToy" : "spider ring"
  } ]
}
2016-10-04T10:36:01.845 ERROR [jacksontest.Foo] - Unexpected exception
java.lang.Exception: Did not find info about the class class jacksontest.Foo$Animal in @JsonSubTypes
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$com$kjetland$jackson$jsonSchema$JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$extractPolymorphismInfo$1$$anonfun$3$$anonfun$apply$12.apply(JsonSchemaGenerator.scala:435)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$com$kjetland$jackson$jsonSchema$JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$extractPolymorphismInfo$1$$anonfun$3$$anonfun$apply$12.apply(JsonSchemaGenerator.scala:435)
    at scala.Option.getOrElse(Option.scala:121)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$com$kjetland$jackson$jsonSchema$JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$extractPolymorphismInfo$1$$anonfun$3.apply(JsonSchemaGenerator.scala:435)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$com$kjetland$jackson$jsonSchema$JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$extractPolymorphismInfo$1$$anonfun$3.apply(JsonSchemaGenerator.scala:431)
    at scala.Option.map(Option.scala:146)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$com$kjetland$jackson$jsonSchema$JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$extractPolymorphismInfo$1.apply(JsonSchemaGenerator.scala:430)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$com$kjetland$jackson$jsonSchema$JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$extractPolymorphismInfo$1.apply(JsonSchemaGenerator.scala:421)
    at scala.Option.map(Option.scala:146)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper.com$kjetland$jackson$jsonSchema$JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$extractPolymorphismInfo(JsonSchemaGenerator.scala:420)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$8.apply(JsonSchemaGenerator.scala:510)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$8.apply(JsonSchemaGenerator.scala:483)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$DefinitionsHandler.getOrCreateDefinition(JsonSchemaGenerator.scala:184)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper.expectObjectFormat(JsonSchemaGenerator.scala:704)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.acceptJsonFormatVisitor(BeanSerializerBase.java:809)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.acceptJsonFormatVisitor(DefaultSerializerProvider.java:580)
    at com.fasterxml.jackson.databind.ObjectMapper.acceptJsonFormatVisitor(ObjectMapper.java:3641)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anon$2.itemsFormat(JsonSchemaGenerator.scala:286)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$8$$anon$7.myPropertyHandler(JsonSchemaGenerator.scala:611)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator$MyJsonFormatVisitorWrapper$$anonfun$8$$anon$7.optionalProperty(JsonSchemaGenerator.scala:680)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.depositSchemaProperty(BeanPropertyWriter.java:805)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.acceptJsonFormatVisitor(BeanSerializerBase.java:833)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.acceptJsonFormatVisitor(DefaultSerializerProvider.java:580)
    at com.fasterxml.jackson.databind.ObjectMapper.acceptJsonFormatVisitor(ObjectMapper.java:3641)
    at com.fasterxml.jackson.databind.ObjectMapper.acceptJsonFormatVisitor(ObjectMapper.java:3620)
    at com.kjetland.jackson.jsonSchema.JsonSchemaGenerator.generateJsonSchema(JsonSchemaGenerator.scala:807)
    at jacksontest.Foo.main(Foo.java:76)

JsonFormatVisitor.format(...) not recognized

Hi!

I see you have started to implement your own Annotations to set a "format": specifier. - The jackson JsonFormatVisitor already has a method to set the Format, with an Enum of Standard-Formats. But this seems to be currently ignored by the jsonSchema,

Example:

new JsonSerializer< LocalDate >() {
    public void serialize( final LocalDate v, final JsonGenerator gen, final SerializerProvider sp ) {
        gen.writeString( v.toString() );
    }

    public void acceptJsonFormatVisitor( final JsonFormatVisitorWrapper visitor, final JavaType typeHint ) {
        visitor.expectStringFormat( typeHint ).format( JsonValueFormat.DATE );
    }
}

Expected output would be:
"format" : "date"

Improved "location-info" in error messages

When using the module on a complex set of classes, I get the following exception/warning messages:

    java.lang.Exception: We only support polymorphism using jsonTypeInfo.use == JsonTypeInfo.Id.NAME

    [main] WARN com.kjetland.jackson.jsonSchema.JsonSchemaGenerator - Not able to generate jsonSchema-info for type: [simple type, class java.lang.Object] - probably using custom serializer which does not override acceptJsonFormatVisitor

It'll be helpful if responsible class names were printed. In the first case, printing "ac" does the trick. (I have a local change that I can push if you give me commit rights.) In the second case, I was not sure what the fix is.

Thanks.

The java example doesn't compile

generateJsonSchema requires three arguments. I don't suppose you could update the doc to show how a Java program creates a scala.Option?

Wrong deserialization of (Java) List of Lists

I have a Java class in which one of the fields (categories) is a List<List>

expected output in JSON schema:
"categories": { "type": "array", "items": { "type": "array", "items": { "type": "string" } } },
current output:
"categories": { "type": "array", "items": { "type": "string" } },

I removed preferredElementType in expectArrayFormat (JsonSchemaGenerator.scala) and it works correctly for Java classes now, but I think it breaks processing of scala List[List[String]]

Title attribute being set to null when more than 2 definitions added

Good afternoon.

I have the following pojo :

public class AccountingEntryRequestTO {

    public enum MovementType {

        ALL(1),
        CLAIMS(2),
        POLICIES(3);

        private Integer intValue;
        private static Map<Integer, MovementType> map = new HashMap<>();

        MovementType(Integer intValue){
            this.intValue = intValue;
        }

        static {
            for (MovementType type : MovementType.values()) {
                map.put(type.intValue, type);
            }
        }

        public static MovementType valueOf(int type) {
            return map.get(type);
        }

        public Integer getIntValue(){
            return this.intValue;
        }

    }

    @JsonProperty("job-instance.label.email")
    private String email;

    @JsonProperty("job-instance.label.initialDate")
    private Date initialDate;

    @JsonProperty("job-instance.label.endDate")
    private Date finishDate;

    @JsonProperty("job-instance.label.attach")
    private Boolean attach;

    @JsonProperty("job-instance.label.periodicity")
    private EntryMovementPeriodicityType periodicity;

    @JsonProperty("job-instance.label.movementType")
    private MovementType movementType;

    @JsonProperty(value = "DynamicCombo", required = true)
    private List<DynamicCombo> dynamicCombo;

DynamicCombo as following

@Getter
@Setter
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = DynamicComboCompanies.class, name="job-instance.label.company"),
        @JsonSubTypes.Type(value = DynamicComboJournalTypes.class, name="job-instance.label.journalType"),
        @JsonSubTypes.Type(value = DynamicComboProducts.class, name="job-instance.label.product")}
)
public abstract class DynamicCombo {

    @JsonProperty("value")
    private String value;

}

And finally the generated json :

{
  "$schema" : "http://json-schema.org/draft-04/schema#",
  "title" : "Accounting Entry Request TO",
  "type" : "object",
  "additionalProperties" : false,
  "properties" : {
    "job-instance.label.email" : {
      "type" : "string"
    },
    "job-instance.label.initialDate" : {
      "type" : "integer",
      "format" : "utc-millisec"
    },
    "job-instance.label.endDate" : {
      "type" : "integer",
      "format" : "utc-millisec"
    },
    "job-instance.label.attach" : {
      "type" : "boolean"
    },
    "job-instance.label.periodicity" : {
      "type" : "string",
      "enum" : [ "DAILY", "MONTHLY" ]
    },
    "DynamicCombo" : {
      "type" : "array",
      "items" : {
        "oneOf" : [ {
          "$ref" : "#/definitions/DynamicComboCompanies"
        }, {
          "$ref" : "#/definitions/DynamicComboJournalTypes"
        }, {
          "$ref" : "#/definitions/DynamicComboProducts"
        } ]
      }
    }
  },
  "required" : [ "DynamicCombo" ],
  "definitions" : {
    "DynamicComboCompanies" : {
      "type" : "object",
      "additionalProperties" : false,
      "properties" : {
        "type" : {
          "type" : "string",
          "enum" : [ null ],
          "default" : null
        },
        "value" : {
          "type" : "string"
        }
      },
      **"title" : null,**
      "required" : [ "type" ]
    },
    "DynamicComboJournalTypes" : {
      "type" : "object",
      "additionalProperties" : false,
      "properties" : {
        "type" : {
          "type" : "string",
          "enum" : [ null ],
          "default" : null
        },
        "value" : {
          "type" : "string"
        }
      },
      **"title" : null,**
      "required" : [ "type" ]
    },
    "DynamicComboProducts" : {
      "type" : "object",
      "additionalProperties" : false,
      "properties" : {
        "type" : {
          "type" : "string",
          "enum" : [ null ],
          "default" : null
        },
        "value" : {
          "type" : "string"
        }
      },
      **"title" : null,**
      "required" : [ "type" ]
    }
  }
}

As you can see the title attribute is not being set. The enum attribute is not being set either but I dont know the consequences.

Thanks in advance for any help.

Not correctly resolving subTypeName for polymorphism if not using @JsonTypeName

I am using these settings on my base class:
@JsonTypeInfo(include=JsonTypeInfo.As.PROPERTY, use=JsonTypeInfo.Id.NAME,property="type")

I am getting this returned as the properties if I don't set a @JsonTypeName:
"properties" : { "type" : { "type" : "string", "enum" : [ null ], "default" : null, "options" : { "hidden" : true } }
When I do set a @JsonTypeName("User") like so I get what I expect:
"properties" : { "type" : { "type" : "string", "enum" : [ "User" ], "default" : "User", "options" : { "hidden" : true } }

Does this lib work correctly with Scala 2.12?

Trying to generate a schema for following case class:

case class Event(userId: String,
                             source: String,
                             destination: String)

Using the code from the examples:

    val objectMapper = new ObjectMapper
    val jsonSchemaGenerator = new JsonSchemaGenerator(objectMapper)
    val jsonSchema: JsonNode =
     jsonSchemaGenerator.generateJsonSchema(classOf[Event])
    objectMapper.writeValueAsString(jsonSchema)

Result:
{"$schema":"http://json-schema.org/draft-04/schema#","title":"Event","type":"object","additionalProperties":false,"properties":{}}

No fields from case class are generated in the resulting JSON schema.

Am I doing anything wrong?

Use oneOf for Option/Optional

When the pojo has property like this:

    diversity: Option[Diversity]

generate schema like this:

{
    "diversity": {
        "oneOf": [
            {
                "type": "null",
                "title": "Not included"
            },
            {
                "$ref": "#/definitions/Diversity"
            }
        ],
        "title": "Diversity"
    }
}

JsonTypeInfo WRAPPER_OBJECT support

Would it be possible to support JsonTypeInfo.WRAPPER_OBJECT?

I've mocked up a sample object and schema of what it could look like below.

Are there any reasons to why you didn't originally implement support for WRAPPER_OBJECT, or was it just because there seems to be quite a bit more to do compared with PROPERTY and EXTERNAL_PROPERTY?

I'm happy to have a look into this if there are no objections

{
  "id": "1",
  "person": [
    {
      "Person": {
        "firstName": "first",
        "lastName": "last"
      }
    }
  ]
}

{
"$schema" : "http://json-schema.org/draft-04/schema#",
  "type" : "object",
  "additionalProperties" : false,
  "properties" : {
    "id" : {
      "type" : "string"
    },
    "person" : {
      "type" : "array",
      "items" : {
        "$ref" : "#/definitions/PersonWrapper"
      },
      "minItems": 1,
      "maxItems": 1
    }
  },
  "definitions" : {
    "PersonWrapper": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "Person": {
          "$ref": "#/definitions/Person"
        }
      }
    },
    "Person" : {
      "type" : "object",
      "additionalProperties" : false,
      "properties" : {
        "firstName" : {
          "type" : "string"
        },
        "lastName" : {
          "type" : "string"
        }
      }
    }
  }
}

additionalProperties not supported

I have my POJO with annotations:

    @JsonAnyGetter
    public Map<String, Object> getAdditionalProperties() {
        return additionalProperties != null
                ? additionalProperties
                : (additionalProperties = new LinkedHashMap<>());
    }

    @JsonAnySetter
    public void setAdditionalProperty(String name, Object value) {
        this.getAdditionalProperties().put(name, value);
    }

Genrated JSON-Schema has: "additionalProperties":false instead of true :-(

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.