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

Support field aliases #338

Open
GREsau opened this issue Sep 8, 2024 · 0 comments
Open

Support field aliases #338

GREsau opened this issue Sep 8, 2024 · 0 comments

Comments

@GREsau
Copy link
Owner

GREsau commented Sep 8, 2024

In alpha.15, it's now possible to generate separate serialize/deserialize schemas - see https://graham.cool/schemars/generating/#serialize-vs-deserialize-contract

But #[derive(JsonSchema)] doesn't yet respect alias attributes, which can be set on enum variants (tracked separate in #25) or struct/variant fields (this issue).

e.g.

#[derive(JsonSchema, Deserialize, Serialize)]
pub struct MyStruct {
    #[serde(alias = "my_i32", alias = "my_integer")]
    pub my_int: i32,
    #[serde(alias = "my_optional_i32", alias = "my_optional_integer", default)]
    pub my_optional_int: i32,
}

The "serialize" schema for MyStruct should continue ignoring the aliases, because they are only relevant during deserialization. But what should the "deserialize" schema look like?

The serde behaviour is to allow any one of my_int, my_i32 or my_integer properties, failing deserialization when two or more or them are specified, and also failing when none of them are specified. my_optional_int etc. also disallow specifying two or more of them, but allow specifying none of them.

This means that a field and its aliases should be expressed as a set of mutually exclusive properties. One possible way of defining this as a JSON schema is:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "my_int": {
      "type": "integer"
    },
    "my_i32": {
      "type": "integer"
    },
    "my_integer": {
      "type": "integer"
    },
    "my_optional_int": {
      "type": "integer"
    },
    "my_optional_i32": {
      "type": "integer"
    },
    "my_optional_integer": {
      "type": "integer"
    }
  },
  "allOf": [
    {
      "oneOf": [
        { "required": ["my_int"] },
        { "required": ["my_i32"] },
        { "required": ["my_integer"] }
      ]
    },
    {
      "oneOf": [
        { "required": ["my_optional_int"] },
        { "required": ["my_optional_i32"] },
        { "required": ["my_optional_integer"] },
        {
          "properties": {
            "my_optional_int": false,
            "my_optional_i32": false,
            "my_optional_integer": false
          }
        }
      ]
    }
  ]
}

This is annoyingly verbose even for this very simple case, but I'm not aware of a better way to define this. The first oneOf requires that the input contains exactly one of my_int/my_i32/my_integer, and the second oneOf requires that the input contains at most one of my_optional_int/my_optional_i32/my_optional_integer.

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

1 participant