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

flatten of Option<Enum> generates schema where enum is required #317

Open
meskill opened this issue Aug 14, 2024 · 4 comments
Open

flatten of Option<Enum> generates schema where enum is required #317

meskill opened this issue Aug 14, 2024 · 4 comments

Comments

@meskill
Copy link

meskill commented Aug 14, 2024

Consider the next types:

#[derive(JsonSchema)]
enum Enum {
    Var1(String),
    Var2(u32)
}

#[derive(JsonSchema)]
struct FlattenOptionEnum {
    #[serde(flatten)]
    enum_: Option<Enum>
}

The generated schema looks like

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "FlattenOptionEnum",
  "type": "object",
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "Var1": {
          "type": "string"
        }
      },
      "additionalProperties": false,
      "required": [
        "Var1"
      ]
    },
    {
      "type": "object",
      "properties": {
        "Var2": {
          "type": "integer",
          "format": "uint32",
          "minimum": 0
        }
      },
      "additionalProperties": false,
      "required": [
        "Var2"
      ]
    }
  ]
}

Hence, forcing the value from enum to be specified by the schema i.e. basically ignoring Option

Serde handles this cases properly by handling optional case. Check the playground

@meskill
Copy link
Author

meskill commented Aug 14, 2024

possible fix in #318

@GREsau
Copy link
Owner

GREsau commented Aug 16, 2024

This is a valid bug, but I think fixing it may be quite complicated. It's also related to #48 in that it depends on the deserialize vs serialize behaviour we're trying to describe.

Consider these types:

#[derive(JsonSchema)]
pub struct MyStruct {
    pub my_int: i32,
    #[schemars(flatten)]
    pub enum1: Option<MyEnum1>,
    #[schemars(flatten)]
    pub enum2: Option<MyEnum1>,
}

#[derive(JsonSchema)]
pub enum MyEnum1 {
    Foo(i32),
    Bar(i32),
    Foobar(i32),
}

#[derive(JsonSchema)]
pub enum MyEnum2 {
    Baz(i32),
    Qux(i32),
}

What should the schema for MyStruct look like?

When serialized to JSON, the value will be an object that must contain the properties:

  • my_int
  • at most one (but possibly zero) of Foo, Bar or Foobar
  • at most one (but possibly zero) of Baz or Qux

There are several ways to represent that schema. For example, using dependentSchemas to disallow the mutually-exclusive properties:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "MyStruct",
  "type": "object",
  "properties": {
    "my_int": {
      "type": "integer",
      "format": "int32"
    },
    "Foo": {
      "type": "integer",
      "format": "int32"
    },
    "Bar": {
      "type": "integer",
      "format": "int32"
    },
    "Foobar": {
      "type": "integer",
      "format": "int32"
    },
    "Baz": {
      "type": "integer",
      "format": "int32"
    },
    "Qux": {
      "type": "integer",
      "format": "int32"
    }
  },
  "dependentSchemas": {
    "Foo": {
      "properties": {
        "Bar": false,
        "Foobar": false
      }
    },
    "Bar": {
      "properties": {
        "Foo": false,
        "Foobar": false
      }
    },
    "Foobar": {
      "properties": {
        "Foo": false,
        "Bar": false
      }
    },
    "Baz": {
      "properties": {
        "Qux": false
      }
    },
    "Qux": {
      "properties": {
        "Baz": false
      }
    }
  },
  "required": [
    "my_int"
  ]
}

But if we're instead considering what JSON values will successfully deserialize to a MyStruct via serde_json, then all of the following are valid:

  • {"my_int": 0}
  • {"my_int": 0, "Foo": 0, "Bar": 0, "Foobar": 0}
  • {"my_int": 0, "Foo": "string", "Bar": null, "Foobar": false}

So if we're strictly following the deserialization behaviour, then the schema would be:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "MyStruct",
  "type": "object",
  "properties": {
    "my_int": {
      "type": "integer",
      "format": "int32"
    }
  },
  "required": [
    "my_int"
  ]
}

...which seems much less useful

@GREsau
Copy link
Owner

GREsau commented Aug 18, 2024

This is a special case of #48, but I'll leave this issue open because it's a complicated case in itself, and I don't want to further overcomplicate the other issue

@taorepoara
Copy link

I think that a good solution would be to manage the flatten attribute with anyOf, so each element of the anyOf could manage it's own required parameters and a $ref could be used to manage the enum.

There might be some problem with the additionalProperties with this solution, but it needs to be tested.

Linked to #311 two.

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

Successfully merging a pull request may close this issue.

3 participants