Skip to content

Wrapper set arities read JSON element tokens their primitive set refuses (quoted, padded, signed numbers; braced and unhyphenated Guids) #141

Closed

Description

@CaffeinatedCoder

Found by SetWrapperParityTests (#139), the sibling parity sweep between a primitive set and its wrapper arity over an identity element.

Problem

docs/serialization.md says that for the non-temporal families the JSON is "byte for byte the same: Int32Set<OrderId> and Int32Set are indistinguishable". On write that holds. On read the wrapper arity accepts element tokens its primitive sibling refuses, because the wrapper's element converters (ValueSetIntegerElementJsonConverter, ValueSetTextElementJsonConverter) hand the token's text to the family's array-literal grammar, while the primitive set reads its elements with System.Text.Json's own converter.

Measured with the default JsonSerializerOptions (SmallCode/TenantId are the conventions project's representative wrappers; an identity wrapper behaves the same):

JSON Int32Set Int32Set<SmallCode>
["7"] JsonException {7}
[" 7 "] JsonException {7}
["+7"] JsonException {7}
JSON GuidSet GuidSet<TenantId>
[" 6f9619ff-8b86-d011-b42d-00c04fc964ff "] JsonException {6f9619ff-…}
["{6f9619ff-8b86-d011-b42d-00c04fc964ff}"] JsonException {6f9619ff-…}
["6f9619ff8b86d011b42d00c04fc964ff"] JsonException {6f9619ff-…}

The same holds for Int16Set<T>, Int64Set<T> and DecimalSet<T>. Under NumberHandling.AllowReadingFromString (ASP.NET Core's web defaults) ["7"] agrees, but [" 7 "] still reads only through the wrapper.

The converter's own remarks say "Reads accept a JSON string unconditionally". That is the intent for AllowReadingFromString, but it ignores the options, and the literal grammar is wider than System.Text.Json's (whitespace, +, the other uuid_in spellings). The temporal and NodaTime arities agree with their primitive, because those primitive sets already read their JSON elements through the same grammar.

Expected

Swapping Int32Set for Int32Set<OrderId> doesn't change which payloads an endpoint accepts. The wrapper's element reader accepts a string token only when the options allow reading numbers from strings, and it reads the token with System.Text.Json's grammar for the primitive (or the primitive set's reader), not the array-literal one.

Notes

  • It isn't a wrong value: every accepted payload reads as the number it spells. It's a widening, and a payload that one sibling accepts and the other refuses.
  • Tightening it refuses input that is accepted today, so it's breaking.
  • The candidate rows are in test/oracles/candidates.json under SetWrapperParityTests. The fix's PR removes them.

Activity

  1. added this to the 10.0.0 milestone on Oct 17, 2026
  2. added
    bugSomething isn't working
    breakingChanges an answer or a signature — needs a major
    found-by/oracleFound by the generated-oracle machinery (claims × API surfaces)
    area/value-setsValue set families and wrapper arities
    area/jsonSystem.Text.Json and TypeConverter wire forms
    package/coreAffects CodoMetis.ValueRanges
    on Oct 17, 2026
  3. CaffeinatedCoder commented on Oct 18, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Fixed in #164 (stacked on #163): the integer and decimal wrapper converters read a string token only under JsonNumberHandling.AllowReadingFromString, with the grammar System.Text.Json reads inside the quotes (verified against Utf8Parser: sign and digits; sign, digits, point and exponent; never whitespace), and a new ValueSetGuidElementJsonConverter reads a wrapper Guid with Utf8JsonReader.GetGuid — the hyphenated form only. WriteAsString alone no longer round-trips a wrapper set, as it never did a plain one. The whitespace row for GuidSet went with #162 already (the uuid_in grammar refuses it too); the four numeric rows go with this PR. The value-semantics review added one more commit: the Guid converter now also refuses, at the write, an element that formats without hyphens, since neither sibling could read that payload back. Proven by reverting the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/jsonSystem.Text.Json and TypeConverter wire formsarea/value-setsValue set families and wrapper aritiesbreakingChanges an answer or a signature — needs a majorbugSomething isn't workingfound-by/oracleFound by the generated-oracle machinery (claims × API surfaces)package/coreAffects CodoMetis.ValueRangesseverity/lowCosmetic, message or documentation-level difference

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions