Repository navigation
Honour a non-public [JsonConstructor] when deciding constructor binding [patch] - #86
Merged
Merged
Conversation
…ng [patch] SelectDeserializationConstructor looked only at public constructors, but System.Text.Json 8.0 and later honour [JsonConstructor] on a private or internal one, the usual shape of an immutable model. Its get-only properties never counted as populated, so the holder was not claimed and each decorated child validated on its own: violations lost their path prefix and every one after the first was dropped. Non-public constructors are now considered when a one-time probe finds the running System.Text.Json honours them, so net7.0's in-box copy keeps its public-only behaviour. Fixes #55 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017vfJW4hDvfx3eQasG41fzL
The probe's refusal branches only ran on net7.0, which no test leg covers. CanConstruct now takes the probe type, so tests reach both refusals on every leg: a type with no usable constructor, and one whose [JsonConstructor] is ambiguous. A further test pins that every tested leg honours a private [JsonConstructor]. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017vfJW4hDvfx3eQasG41fzL
…n-constructor # Conflicts: # JsonRequiredConditionally.Test/TestModels.cs
…n-constructor # Conflicts: # CLAUDE.md # JsonRequiredConditionally.Test/TestModels.cs
|
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.



Fixes #55
What was wrong
RequirementRuleCompiler.SelectDeserializationConstructorlooked only attype.GetConstructors(), which returns public constructors only. System.Text.Json 8.0 and later also honour[JsonConstructor]on a private or internal constructor, which is the usual shape of an immutable model. For such a type:Tuninginstead ofChild.Tuning.B.Tuningnever appeared next toA.Tuning.Fix
SelectDeserializationConstructornow also searches non-public instance constructors for[JsonConstructor], but only when the running System.Text.Json honours them.NonPublicJsonConstructorSupport. It deserializes a private type whose only constructor is a private[JsonConstructor]. The library uses no conditional compilation, so a runtime check is the only option. net7.0's in-box System.Text.Json ignores such constructors, so it keeps the old public-only behaviour.FieldAwareProbeOptions, which carry an explicitDefaultJsonTypeInfoResolver, so it does not depend on reflection being enabled by default.Tests
Three new tests in
EligibilityTests, as the issue's acceptance criteria ask:[JsonConstructor]reportsChild.Tuning.[JsonConstructor]reports bothA.TuningandB.Tuningin one exception.structwith a private[JsonConstructor]reportsChild.Tuning.Results:
["Tuning"]instead of["Child.Tuning"], and one returns["Tuning"]instead of["A.Tuning", "B.Tuning"].This touches the same method that #44 changed earlier, through #71 (constructor chosen from the deserialized type). That change is already on main, and this one builds on it.
Generated by Claude Code