Fix JsonSchemaExporter dropping null branch for nullable floating-point composition schemas#129530
Fix JsonSchemaExporter dropping null branch for nullable floating-point composition schemas#129530Copilot wants to merge 4 commits into
Conversation
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
|
Tagging subscribers to this area: @dotnet/area-system-text-json |
eiriktsarpalis
left a comment
There was a problem hiding this comment.
@copilot This doesn't look right.
| } | ||
| } | ||
|
|
||
| anyOf.Add(new JsonSchema { Type = JsonSchemaType.Null }); |
There was a problem hiding this comment.
Wouldn't this happen even if AnyOf is the empty list? Are you sure this is desirable?
There was a problem hiding this comment.
Good catch. Reworked MakeNullable to fold null into the first anyOf branch that carries a concrete type, rather than appending a standalone { "type": "null" } branch. An empty anyOf (or one whose branches are all type-less) now leaves the schema untouched instead of gaining a spurious null branch. Fixed in 11a4bcb.
| "anyOf": [ | ||
| { "type": "number" }, | ||
| { "enum": ["NaN", "Infinity", "-Infinity"] }, | ||
| { "type": "null" } |
There was a problem hiding this comment.
This looks invalid. The expected schema should have had the first subschema be { "type" : ["number","null"] }.
There was a problem hiding this comment.
Updated — the first subschema is now { "type": ["number","null"] } and the separate null branch is gone. Fixed in 11a4bcb.
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
Agreed — the original approach was wrong. I've reworked it in {"anyOf":[{"type":["number","null"]},{"enum":["NaN","Infinity","-Infinity"]}]}This also handles the empty/type-less |
| return; | ||
| } | ||
|
|
||
| if (firstTypedBranch is null && branch.Type is not JsonSchemaType.Any) |
There was a problem hiding this comment.
@copilot I fail to see how this fix is valid in the general case. If anyOf contained objects, this code would erroneously insert null in the first object in the list which we clearly don't want to do. What we should be fixing here is handling specifically for nullable structs when number handling is specified, not inaccurate post hoc patching like is being attempted here.
There was a problem hiding this comment.
You're right. I reverted the MakeNullable change entirely and moved the fix into the nullable-value-type path (NullableElementConverter block in JsonSchemaExporter), alongside the existing enum handling. This code only runs for Nullable<T>, where the element schema can only be an anyOf when it's an IEEE floating-point type under AllowNamedFloatingPointLiterals — so the null is folded into the numeric branch ({ "type": ["number","null"] }) and never touches object/polymorphic compositions. Fixed in c81c58a.
…, revert MakeNullable Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
JsonSchemaExporteromitted null-acceptance for CLR-nullable types (double?,float?) whose schema is emitted as ananyOfcomposition — which happens for IEEE floating-point types underJsonNumberHandling.AllowNamedFloatingPointLiterals. The same property exports correctly as{"type":["number","null"]}under default number handling, so adouble?was advertised to downstream JSON-Schema/OpenAPI consumers as a non-nullable number.Description
JsonSchema.MakeNullableonly addedJsonSchemaType.Nullwhen the schema carried a concretetypekeyword. Composition schemas (anyOf) haveType == Any, soMakeNullablewas a no-op and the inherited null branch was lost.NullableElementConverterblock inJsonSchemaExporter.MapJsonSchemaCore), mirroring the existing enum handling. This code only runs forNullable<T>, where the element schema can only be ananyOfwhen it is an IEEE floating-point type formatted underAllowNamedFloatingPointLiterals; in that caseJsonSchemaType.Nullis folded into the numeric branch (producing{ "type": ["number","null"] }). The general-purposeMakeNullablemethod is left unchanged so it never affects arbitraryanyOfcompositions such as polymorphic object unions.double?,float?, and a POCO with nullable floating-point properties under globalAllowNamedFloatingPointLiterals, across the reflection and source-gen test contexts.Resulting export for a nullable property: