Corvus.JsonSchema icon indicating copy to clipboard operation
Corvus.JsonSchema copied to clipboard

Invalid generated code with nested anyOf and $ref

Open NotNite opened this issue 7 months ago • 2 comments

I discovered this when trying to generate code for https://internect.info/lexicon-schema.json, but I tracked it down to this example:

{
    "$schema": "http://json-schema.org/draft-07/schema",
    "type": "object",
    "anyOf": [
        {
            "type": "object",
            "required": ["kind", "data"],
            "additionalProperties": false,
            "properties": {
                "kind": { "const": "foo" },
                "data": {
                    "anyOf": [
                        {
                            "type": "object",
                            "required": ["kind"],
                            "additionalProperties": false,
                            "properties": {
                                "kind": { "const": "bar" }
                            }
                        },
                        {
                            "type": "object",
                            "required": ["kind"],
                            "additionalProperties": false,
                            "properties": {
                                "kind": { "const": "baz" }
                            }
                        }
                    ]
                }
            }
        },
        {
            "$ref": "#/anyOf/0/properties/data/anyOf/0"
        }
    ]
}

This produces invalid code with these errors:

0>Lexicon_g.cs(231,69): Error CS0102 : The type 'Lexicon' already contains a definition for 'AsAnyOf0Entity'
0>Lexicon_g.cs(242,17): Error CS0102 : The type 'Lexicon' already contains a definition for 'IsAnyOf0Entity'

I presume this is because the properties are named after the type, and both types named "AnyOf0Entity":

  • /// Gets a value indicating whether the instance is a <see cref="LexiconTest.Lexicon.AnyOf0Entity" />.
  • /// Gets a value indicating whether the instance is a <see cref="LexiconTest.Lexicon.AnyOf0Entity.DataEntity.AnyOf0Entity" />.

NotNite avatar May 09 '25 17:05 NotNite

Oh, that's a good one! I thought I had caught all these de-duplication cases, but evidently not.

(Also you have a peculiar case there with two identical schema in the most deeply nested anyOf - is that just because you've trimmed it down to produce the example?)

mwadams avatar May 09 '25 19:05 mwadams

(Also you have a peculiar case there with two identical schema in the most deeply nested anyOf - is that just because you've trimmed it down to produce the example?)

Yeah, the schema I'm ingesting is auto-generated from a TypeScript validation library, so the resulting schema is a bit wonky.

NotNite avatar May 09 '25 20:05 NotNite

I've scheduled this for a fix this week.

mwadams avatar May 18 '25 11:05 mwadams

And that schedule whistled past at high speed. I am looking at this today.

mwadams avatar May 29 '25 07:05 mwadams

Removed: I can repro the issue in a source generator.

mwadams avatar May 29 '25 09:05 mwadams

The issue was possible naming collisions when using simple GetTypeName() in Is and As property name generation.

We have changed the heuristic so that it now tries the GetTypeName() form first, then falls back on the full type name (excluding namespace) then disambiguates with an incrementing numeric suffix.

mwadams avatar May 29 '25 09:05 mwadams