fix(rendering): preserve enum case source order#3051
Conversation
Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
Co-Authored-By: Claude <noreply@anthropic.com>
| var json = System.IO.File.ReadAllText(path); | ||
| var output = TopLevel.FromJson(json).ToJson(); | ||
|
|
||
| if (System.IO.Path.GetFileName(path) == "enum.1.json") |
There was a problem hiding this comment.
Wow, this is a super shitty way of testing! The fixture should not be special-cased for specific test cases! Find a principled way of doing this!
There was a problem hiding this comment.
Agreed, that was the wrong place for it. I removed the filename-keyed check from the C# fixture driver in dfa4681.
The fixture harness is a JSON round-trip (deserialize, reserialize, compare), and enum case source ordering is invisible to a string round-trip, so a fixture genuinely can't express this behavior. That ordering is instead asserted directly and portably in test/unit/enum-order.test.ts, which checks the generated C# and C++ enum declarations preserve schema order through the quicktype API. The fixture driver is back to its original form with no special cases.
The C# fixture driver was special-cased to inspect the generated QuickType.cs source whenever the sample filename was enum.1.json. The fixture pipeline is a round-trip harness (deserialize then reserialize and compare JSON); wiring a filename-keyed source assertion into the driver is not something it is built for, and no other language driver does anything like it. Enum case source ordering is invisible to a string round-trip anyway, so the fixture cannot express it. That behavior is already covered directly and portably by test/unit/enum-order.test.ts, which asserts the generated C# and C++ enum declarations preserve schema order via the quicktype API. Co-Authored-By: Claude <noreply@anthropic.com>
The bug
Enum cases were always rendered in alphabetical order, regardless of the
order they appear in the source JSON Schema (or JSON samples). For example:
instead of the expected declaration order
Monday, Tuesday, Friday, Sunday.This affected every generated language (~29 renderers), since they all share
the same code path. While JSON round-tripping still worked (because
(de)serializers match by string name), anything relying on positional/ordinal
semantics — e.g. C#'s implicit underlying
intenum values — broke, alongwith unnecessary diff noise whenever a schema's enum order changed.
Root cause
ConvenienceRenderer.forEachEnumCaseinpackages/quicktype-core/src/ConvenienceRenderer.tsunconditionally ran thealready source-ordered case names through
mapSortBy, alphabetizing thembefore rendering. A second, related spot in
packages/quicktype-core/src/MakeTransformations.ts(makeEnumTransformer)also alphabetically sorted
enumType.casesbefore building the enum'sruntime string-match transformer (used e.g. by the TypeScript/Flow "Convert"
helpers), for the same reason.
The fix
Removed both forced alphabetical sorts, since
EnumType.cases(and thederived case-name map) already preserve the source's insertion order:
ConvenienceRenderer.forEachEnumCasenow iteratescaseNamesdirectlyinstead of calling
mapSortByon it.MakeTransformations.makeEnumTransformernow maps overArray.from(enumType.cases)directly instead of sorting it first.This is a default-behavior fix (no new renderer option), consistent with
other breaking-default fixes already sanctioned for the next major release.
Test coverage
test/unit/enum-order.test.ts, a focused regression test thatreproduces the exact issue Enums are ALWAYS sorted #2401 repro via the
quicktypeAPI and assertsthe generated C# enum preserves declaration order
(
Monday, Tuesday, Friday, Sunday). This test fails on unfixed code andpasses with the fix.
test/fixtures/csharp/Program.cs) toassert, when running the existing
test/inputs/schema/enum.schema+enum.1.jsonfixture sample, that the generatedQuickType.cscontainspublic enum Lvc { Lawful, Neutral, Chaotic };(the schema's declared["lawful", "neutral", "chaotic"]order, which is not alphabetical) ratherthan a sorted order. This exercises the fix through the existing
schema-csharp fixture pipeline without needing new schema/sample files.
Verification performed locally
npm run buildpasses.npm run test:unit: all 177 unit tests pass, including the newenum-order.test.ts.Kotlin, Swift, and Rust — all now emit enum cases in source declaration
order.
dotnettoolchain is not available in this environment, so the fullschema-csharpend-to-end fixture (which exercises the newProgram.csassertion) could not be executed locally; it will bevalidated by CI.
Fixes #2401.
🤖 Generated with Claude Code