saasquatch/json-schema-inferrer

Suggestions for optimizing whether the JSON Schema field is required

Closed this issue · 3 comments

present situation:
In the default policy, if a field is set to null, the required field in the generated schema will not be included, but at the same time, the type of the field will also become null
suggestion
I think it is possible to judge in this way. For types such as strings, numbers, and arrays, if you want to set them as non mandatory, you can provide a null value instead of null. In other words, if the field is of string type and non mandatory, the provided example will be "", the number type will be 0, and the array type will be []. This way, both the type can be determined and whether the field must be set based on this

I'm not sure exactly what you mean. Can you please provide a real example?

Are you talking about something like this?

  public static void main(String[] args) throws Exception {
    final ObjectMapper mapper = new ObjectMapper();
    final JsonSchemaInferrer inferrer = JsonSchemaInferrer.newBuilder()
        .setDefaultPolicy(DefaultPolicies.useFirstSamples())
        .setRequiredPolicy(RequiredPolicies.commonFields())
        .build();
    final JsonNode sample = mapper.readTree("{\"foo\": null}");
    System.out.println(inferrer.inferForSample(sample).toPrettyString());
  }

This prints

{
  "$schema" : "http://json-schema.org/draft-04/schema#",
  "type" : "object",
  "properties" : {
    "foo" : {
      "type" : "null",
      "default" : null
    }
  },
  "required" : [ "foo" ]
}

I'm not sure exactly what you mean. Can you please provide a real example?

Are you talking about something like this?

  public static void main(String[] args) throws Exception {
    final ObjectMapper mapper = new ObjectMapper();
    final JsonSchemaInferrer inferrer = JsonSchemaInferrer.newBuilder()
        .setDefaultPolicy(DefaultPolicies.useFirstSamples())
        .setRequiredPolicy(RequiredPolicies.commonFields())
        .build();
    final JsonNode sample = mapper.readTree("{\"foo\": null}");
    System.out.println(inferrer.inferForSample(sample).toPrettyString());
  }

This prints

{
  "$schema" : "http://json-schema.org/draft-04/schema#",
  "type" : "object",
  "properties" : {
    "foo" : {
      "type" : "null",
      "default" : null
    }
  },
  "required" : [ "foo" ]
}

yes, I think the print like this is better:

final JsonNode sample = mapper.readTree("{"foo": ""}");

{
  "$schema" : "http://json-schema.org/draft-04/schema#",
  "type" : "object",
  "properties" : {
    "foo" : {
      "type" : "String",
      "default" : ""
    }
  },
  "required" : []
}

There are a few issues here.

  1. How would this library ever know that foo is supposed to be a string? Why not a number or boolean?
  2. The sample JSON object does NOT validate against the schema you provided. In JSON schema, null and string are distinct types. In your schema, if you say the type of foo is string (not capitalized String btw), then foo is not allowed to be null, because null is a completely different type. You can try with an online JSON schema validator like this one.
Screenshot 2024-12-18 at 11 51 45 AM

This library does handle multiple types for a single field. Here's an example of having multiple samples where there are multiple types per field:

  public static void main(String[] args) throws Exception {
    final ObjectMapper mapper = new ObjectMapper();
    final JsonSchemaInferrer inferrer = JsonSchemaInferrer.newBuilder()
        .setDefaultPolicy(DefaultPolicies.useFirstSamples())
        .setRequiredPolicy(RequiredPolicies.commonFields())
        .build();
    final JsonNode sample1 = mapper.readTree("{\"foo\": null}");
    final JsonNode sample2 = mapper.readTree("{\"foo\": \"a\"}");
    System.out.println(inferrer.inferForSamples(Arrays.asList(sample1, sample2)).toPrettyString());
  }

This prints

{
  "$schema" : "http://json-schema.org/draft-04/schema#",
  "type" : "object",
  "properties" : {
    "foo" : {
      "anyOf" : [ {
        "type" : "null",
        "default" : null
      }, {
        "type" : "string",
        "default" : "a"
      } ]
    }
  },
  "required" : [ "foo" ]
}