fix(schema): honor patternProperties value schema for object maps - #2996
Open
schani wants to merge 9 commits into
Open
fix(schema): honor patternProperties value schema for object maps#2996schani wants to merge 9 commits into
schani wants to merge 9 commits into
Conversation
) quicktype only recognized `patternProperties` as a map-value type when it had a literal `.*` key (a hack for a Go->Schema generator quirk). Any other regex pattern, including the ones real-world schemas actually use, was silently discarded, so the map fell back to an untyped `Dictionary<string, object>` instead of generating a proper value class. Now, when `additionalProperties` is absent, any non-empty `patternProperties` schema(s) are used to type the map values (multiple patterns are combined with `anyOf`), matching how `additionalProperties` already behaves. Added a JSON Schema fixture (pattern-properties-value.schema/.1.json) and a unit test asserting the generated C# class, and verified the existing go-schema-pattern-properties and class-with-additional fixtures still pass. Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
…2996) Co-Authored-By: Claude <noreply@anthropic.com>
cJSON does not validate map/patternProperties value types, so the negative case pattern-properties-value.1.fail.json was round-tripped instead of rejected. Skip the schema for cjson, alongside the sibling go-schema-pattern-properties.schema which is skipped for the same reason. The positive and negative cases still run (and the negative is genuinely rejected) for the other languages. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Resolve test/languages.ts conflict: master refactored the per-language map-value skip into the shared skipsMapValueValidation constant. Move the new pattern-properties-value.schema into that constant (alongside its sibling go-schema-pattern-properties.schema) so every map-value-lenient language skips its negative case, not just cjson. The positive and negative cases still run for the strict languages (verified: Python rejects the mistyped map value and accepts the valid sample). Co-Authored-By: Claude <noreply@anthropic.com>
Generated-output differences26 files differ — 0 modified, 26 new, 0 deleted |
Generated-output differences27 files differ — 0 modified, 27 new, 0 deleted |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bug
quicktype discarded the value schema of JSON Schema
patternPropertiesmaps. InmakeObjectType(packages/quicktype-core/src/input/JSONSchemaInput.ts), a schema'spatternPropertieswas only ever consulted when it had a literal".*"key — a narrow hack originally added for a Go->Schema generator quirk (#976). Any other regex pattern (e.g."^.*$", or the real-world"^.*\S.*$"from this issue) was silently ignored, so the property fell back to an untypedDictionary<string, object>instead of a proper generated class, even though the exact same schema written withadditionalPropertiesinstead ofpatternPropertiesalready worked correctly.Repro (before fix)
{ "type": "object", "properties": { "Materials": { "type": "object", "patternProperties": { "^.*$": { "type": "object", "properties": { "roughness": { "type": "string" }, "thickness": { "type": "number" } }, "required": ["roughness", "thickness"] } } } }, "required": ["Materials"] }produced:
(no
Materials/value class generated at all, plus a warning that quicktype "cannot infer this type").Fix
When
additionalPropertiesis absent andpatternPropertiesis a non-empty object, its value schema(s) are now used to type the map's values — a single pattern's schema is used directly, and multiple patterns are combined withanyOf— matching howadditionalPropertiesis already handled. This doesn't attempt full JSON SchemapatternPropertiesregex-matching semantics (nothing in quicktype validates property names against the pattern); it just stops discarding the type information, consistent with howadditionalPropertiesis already treated as "the type of any additional map values."After the fix, the same repro produces:
Test coverage
test/inputs/schema/pattern-properties-value.schema+.1.json, exercising a non-.*patternPropertiespattern with an object value schema. It's picked up automatically by the existing schema-input fixture discovery (test/fixtures.ts) for every registeredJSONSchemaFixturelanguage.test/unit/pattern-properties-schema.test.tsasserting the generated C# output has the typedDictionary<string, Material>and theMaterialclass withRoughness/Thicknessproperties (fixture round-trip tests can't distinguish a typed map value fromobject, since both round-trip the same JSON).go-schema-pattern-properties.schema(the narrow.*-key case) andclass-with-additional.schema(theadditionalPropertiescase).Verification performed locally
npm run buildpasses.npm run test:unit: 164/164 tests pass.QUICKTEST=true FIXTURE=schema-golang script/test: 68/68 tests pass (includes the new fixture plus the two regression fixtures above).dist/index.jsfor--lang cs, confirming correct output (shown above), and also against the exact pattern from the issue (^.*\S.*$) with the full property set — matches the issue's expected output.Fixes #1854
🤖 Generated with Claude Code