mbknor / mbknor-jackson-jsonSchema

Generate JSON Schema with Polymorphism using Jackson annotations
MIT License
235 stars 79 forks source link

Generating schema for deserialization #132

Open almson opened 4 years ago

almson commented 4 years ago

Thank you for creating this library!

It seems that the way this library works is that it generates a schema for serialization. Ie, it looks at @JsonValue instead of @JsonCreator. Is it possible to have it generate schemas for deserialization?

Here is an example:

import com.fasterxml.jackson.annotation.JsonAutoDetect.Value;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonCreator.Mode;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import com.kjetland.jackson.jsonSchema.JsonSchemaConfig;
import com.kjetland.jackson.jsonSchema.JsonSchemaGenerator;
import org.junit.Test;

/**
 *
 * @author alex
 */
public class JsonSchemaTest {

    static class Config
    {
        @JsonCreator (mode = Mode.PROPERTIES)
        Config(@JsonProperty(required = false) Integer foo) {    
            this.boo = foo != null ? foo : 42;
        }

        final int boo;
    }

    @Test
    public void requiredParameterBug() throws Exception {

        ObjectMapper mapper = new ObjectMapper();
        /** Necessary to activate Jackson support for POJO constructors. */
        mapper.registerModule (new ParameterNamesModule (JsonCreator.Mode.PROPERTIES));
        mapper.setDefaultVisibility 
                (Value
                    .defaultVisibility()
                    .withFieldVisibility (Visibility.NON_PRIVATE)
                    .withCreatorVisibility (Visibility.NON_PRIVATE));
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        JsonSchemaGenerator jsonSchemaGenerator = new JsonSchemaGenerator(mapper, JsonSchemaConfig.html5EnabledSchema());

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

        String schema = mapper.writeValueAsString (jsonSchema);

        System.out.println (schema);
    }
}

It must be compiled with Java 8, javac option -parameters and following dependencies:

        <dependency>
            <groupId>com.fasterxml.jackson.module</groupId> 
            <artifactId>jackson-module-parameter-names</artifactId> 
            <version>2.11.3</version> 
        </dependency> 
        <dependency>
            <groupId>com.kjetland</groupId>
            <artifactId>mbknor-jackson-jsonschema_2.11</artifactId>
            <version>1.0.39</version>
        </dependency> 

The output is:

{
  "$schema" : "http://json-schema.org/draft-04/schema#",
  "title" : "Config",
  "type" : "object",
  "additionalProperties" : false,
  "properties" : {
    "boo" : {
      "propertyOrder" : 1,
      "type" : "integer",
      "title" : "Boo"
    }
  },
  "required" : [ "boo" ]
}

Note that the property is called "boo" instead of "foo", and it is marked as required.

Nevertheless, jsonSchema works pretty well as long as steps are taken to make the serialized output the same as the input. Annotations applied to parameters also work, probably due to how Jackson works.

big-andy-coates commented 3 years ago

Hey @almson, sorry to say that the property name comes from Jackson, and not from the code in this repo. The code implements JsonObjectFormatVisitor to get hold of all the properties, as Jackson sees things, for a given type.

So I'm afraid what you're asking isn't possible. This question would need to be asked against the Jackson databind project.

almson commented 3 years ago

Hmm, I'm not sure about the foo/boo name issue, but the assumption that the schema is for reading serialized objects (instead of writing json that will be deserialized) is pervasive in the code. For example, in these lines it is assumed that primitives are always required.

https://github.com/mbknor/mbknor-jackson-jsonSchema/blob/e370f80d5dd20eb9396455ab2ddfd7083d0e25fb/src/main/scala/com/kjetland/jackson/jsonSchema/JsonSchemaGenerator.scala#L1126-L1133