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

Inline object definitions referenced from allOf are replicated once per reference #50

Open
bgilbert opened this issue Aug 4, 2021 · 0 comments

Comments

@bgilbert
Copy link
Contributor

bgilbert commented Aug 4, 2021

If an object definition is referenced from an allOf array, and that definition includes inline object definitions, those inline definitions are expanded once per allOf reference. Instead, they should be expanded exactly once, and then referenced by name in each parent struct.

For example, consider this schema:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "top",
  "type": "object",
  "allOf": [
    {
      "$ref": "#/definitions/common"
    },
    {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        }
      }
    }
  ],
  "definitions": {
    "common": {
      "type": "object",
      "properties": {
        "info": {
          "type": "object",
          "properties": {
            "id": {
              "type": "integer"
            }
          }
        }
      }
    }
  }
}

Assuming the root structure is named Top, I'd expect this generated code:

#[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
pub struct CommonInfo {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<i64>,
}
#[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
#[serde(rename = "common")]
pub struct Common {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub info: Option<CommonInfo>,
}
#[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
pub struct Top {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub info: Option<CommonInfo>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
}

Here, the Common definition is emitted uselessly (Top doesn't use it) but the object type defined inline by Common is emitted and referenced as CommonInfo.

Schemafy actually produces this code:

#[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
pub struct CommonInfo {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<i64>,
}
#[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
#[serde(rename = "common")]
pub struct Common {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub info: Option<CommonInfo>,
}
#[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
pub struct TopInfo {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<i64>,
}
#[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
pub struct Top {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub info: Option<TopInfo>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
}

Common and CommonInfo are both emitted but unused. When Top references the ...Info struct, it produces a specialized version only used by Top. If Common were referenced in multiple allOf arrays, the ...Info struct would be replicated once for each reference, each time named after the referencer.

I dug into the code a bit, but this seems hard to fix without a substantial refactor. AFAICT, every Schema would need to carry the name of its parent definition, if any, and Schema has nowhere to put this data (without misusing an existing field).

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