Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

unsatisfiable schema when flattening an enum and denying unknown fields #164

Closed
dimbleby opened this issue Jul 23, 2022 · 3 comments
Closed

Comments

@dimbleby
Copy link
Contributor

dimbleby commented Jul 23, 2022

The following generates an unsatisfiable schema

#[derive(JsonSchema)]
#[schemars(deny_unknown_fields)]
pub struct MyStruct {
    pub property: String,
    #[schemars(flatten)]
    pub enumeration: MyEnum,
}

#[derive(JsonSchema)]
#[schemars(rename_all = "lowercase")]
pub enum MyEnum {
    Variant(String),
}

The resulting schema is:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "MyStruct",
  "type": "object",
  "oneOf": [
    {
      "type": "object",
      "required": [
        "variant"
      ],
      "properties": {
        "variant": {
          "type": "string"
        }
      },
      "additionalProperties": false
    }
  ],
  "required": [
    "property"
  ],
  "properties": {
    "property": {
      "type": "string"
    }
  },
  "additionalProperties": false
}

in which the properties / additionalProperties fields demand that the only valid field be property, but the oneOf (also denying additional properties) demands that the only valid field be variant.

@sourcefrog
Copy link

I was thinking about what a solution to this would look like. It seems like:

  1. The oneOf should not deny additionalProperties: the purpose of it is to say that only one flattened enum variant is present, and so it shouldn't care what other properties are there.
  2. The top-level properties should list all the names of the properties that can arise from the flattened enums. (But, of course, they should not be required.) It doesn't need to duplicate the type or other attributes of those properties, just say that they can exist.

I think this would also fix #165, if there is a oneOf per enum, and the union of all their field names are in the top-level properties. Then there's no need to generate the product of all possibilities.

It gets a bit more complicated if the flattened enum is optional: perhaps there needs to be another oneOf branch asserting that none of the properties from any of the branches exist? I think you could write that as oneOf <various enum branches> or not (anyOf [ <all the properties one by one> ]) but is it worth it? Perhaps in a first version it should just be oneOf <enum branches> or true.

@GREsau
Copy link
Owner

GREsau commented Aug 21, 2024

For schemas using draft 2019-09 and above, the fix is relatively straightforward:

  1. remove the "additionalProperties": false from the inner enum schemas
  2. change "additionalProperties": false to "unevaluatedProperties": false in the top-level schema, which behaves similarly but can "see into" oneOf etc.

Then the resulting schema for this issue's example would be:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "MyStruct",
  "type": "object",
  "properties": {
    "property": {
      "type": "string"
    }
  },
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "variant": {
          "type": "string"
        }
      },
      "required": [
        "variant"
      ]
    }
  ],
  "required": [
    "property"
  ],
  "unevaluatedProperties": false
}

For earlier JSON Schema versions like draft 7 which don't support unevaluatedProperties, I think @sourcefrog's suggestion is good. This would give us something like:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "MyStruct",
  "type": "object",
  "properties": {
    "property": {
      "type": "string"
    },
    "variant": true
  },
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "variant": {
          "type": "string"
        }
      },
      "required": [
        "variant"
      ]
    }
  ],
  "required": [
    "property"
  ],
  "additionalProperties": false
}

And for OpenAPI 3.0, it could be the same but with "variant": {} instead of "variant": true

@GREsau
Copy link
Owner

GREsau commented Aug 22, 2024

Fixed in 1.0.0-alpha.10 - the example from this issue now produces the same schema I suggested in my previous comment

@GREsau GREsau closed this as completed Aug 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants