Skip to content

Fix reflection XmlSerializer choice identifier with XmlEnum aliases#130445

Open
StephenMolloy wants to merge 1 commit into
dotnet:mainfrom
StephenMolloy:130017-Reflection-Serializer-Choice-Identifier-Name-Flexibility
Open

Fix reflection XmlSerializer choice identifier with XmlEnum aliases#130445
StephenMolloy wants to merge 1 commit into
dotnet:mainfrom
StephenMolloy:130017-Reflection-Serializer-Choice-Identifier-Name-Flexibility

Conversation

@StephenMolloy

Copy link
Copy Markdown
Member

The reflection-based XmlSerializer (ReflectionXmlSerializationReader) resolved an [XmlChoiceIdentifier] enum value during deserialization by comparing the choice enum member identifiers directly against the matched XML element name. That only works when the enum member name equals the element name, so a choice enum using [XmlEnum] aliases (e.g. [XmlEnum("Number")] NumberChoice) failed to round-trip: the choice field was left at its default instead of the value implied by the element.

Resolve the choice value positionally instead, using the element index already computed when matching the element and reading the corresponding choice.MemberIds[elementIndex]. This mirrors exactly how the IL-generated reader resolves the choice and reuses the alias-aware MemberIds table that XmlReflectionImporter builds up front, so [XmlEnum] aliases are honored.

Add a regression test (Xml_TypeWithAliasedChoiceIdentifier) plus the supporting AliasedChoiceType enum and TypeWithAliasedChoiceIdentifier type, covering the previously untested combination of [XmlChoiceIdentifier] with [XmlEnum] aliases. The test runs against both the reflection reader and the ReflectionOnly serializer configurations.

Fixes #130017

The reflection-based XmlSerializer (ReflectionXmlSerializationReader)
resolved an [XmlChoiceIdentifier] enum value during deserialization by
comparing the choice enum member identifiers directly against the matched
XML element name. That only works when the enum member name equals the
element name, so a choice enum using [XmlEnum] aliases (e.g.
[XmlEnum("Number")] NumberChoice) failed to round-trip: the choice field
was left at its default instead of the value implied by the element.

Resolve the choice value positionally instead, using the element index
already computed when matching the element and reading the corresponding
choice.MemberIds[elementIndex]. This mirrors exactly how the IL-generated
reader resolves the choice and reuses the alias-aware MemberIds table that
XmlReflectionImporter builds up front, so [XmlEnum] aliases are honored.

Add a regression test (Xml_TypeWithAliasedChoiceIdentifier) plus the
supporting AliasedChoiceType enum and TypeWithAliasedChoiceIdentifier type,
covering the previously untested combination of [XmlChoiceIdentifier] with
[XmlEnum] aliases. The test runs against both the reflection reader and the
ReflectionOnly serializer configurations.

Fixes dotnet#130017

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@StephenMolloy StephenMolloy added this to the 11.0.0 milestone Jul 10, 2026
@StephenMolloy StephenMolloy self-assigned this Jul 10, 2026
Copilot AI review requested due to automatic review settings July 10, 2026 00:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a deserialization bug in the reflection-based XmlSerializer reader where [XmlChoiceIdentifier] values were derived by comparing enum member identifiers to XML element names (which breaks when the enum uses [XmlEnum] aliases). The reader now resolves the choice identifier by using the already-computed matched element index to select choice.MemberIds[elementIndex], aligning behavior with the IL-generated reader.

Changes:

  • Update ReflectionXmlSerializationReader to set choice identifiers based on the matched element index (rather than element name string comparison).
  • Add new serialization test types covering [XmlChoiceIdentifier] + [XmlEnum] alias usage.
  • Add a regression test that round-trips an aliased choice identifier and validates both the item and choice enum value.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs Switch choice identifier assignment to use positional MemberIds[elementIndex], ensuring alias-aware resolution matches ILGen behavior.
src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs Add AliasedChoiceType and TypeWithAliasedChoiceIdentifier test types to model aliased choice identifiers.
src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs Add regression test validating round-trip of aliased choice identifier through SerializeAndDeserialize.

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
  "version": 5,
  "last_dispatched_commit": "3d78a07b9f1df545070ca5011b899591cc3626dd",
  "last_dispatched_base_ref": "main",
  "last_dispatched_base_sha": "6c5849144dc8a23aa0760080d6f7784fed60da37",
  "last_reviewed_commit": "3d78a07b9f1df545070ca5011b899591cc3626dd",
  "last_reviewed_base_ref": "main",
  "last_reviewed_base_sha": "6c5849144dc8a23aa0760080d6f7784fed60da37",
  "last_recorded_worker_run_id": "29690568647",
  "review_attempt_commit": "",
  "review_attempt_base_ref": "",
  "review_attempt_count": 0,
  "max_review_attempts": 5,
  "review_history_format": "holistic-review-disclosure-v1",
  "review_history": [
    {
      "commit": "3d78a07b9f1df545070ca5011b899591cc3626dd",
      "review_id": 4730919419
    }
  ]
}

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holistic Review

Motivation: The reflection-based XmlSerializer (ReflectionXmlSerializationReader) resolved an [XmlChoiceIdentifier] enum value by comparing choice enum member identifiers against the matched XML element name. This only works when the enum member name equals the element name, so a choice enum using [XmlEnum] aliases (e.g. [XmlEnum("Number")] NumberChoice) failed to round-trip and left the choice field at its default. This addresses #130017.

Approach: The fix resolves the choice value positionally using the element index already computed while matching the element (choice.MemberIds[elementIndex]), instead of string-comparing enum member names. WriteElement gains an elementIndex parameter threaded from WriteMemberElementsIf, and Member.ChoiceSource changes from Action<object> (element name) to Action<int> (element index). This mirrors exactly how the IL-generated reader (XmlSerializationReaderILGen) resolves the choice and reuses the alias-aware MemberIds table that XmlReflectionImporter builds up front, so [XmlEnum] aliases are honored.

Summary: This is a well-targeted, minimal correctness fix that brings the reflection reader into parity with the IL-generated reader. Key points verified:

  • MemberIds is built in XmlReflectionImporter parallel to accessor.Elements, and the matching loop in WriteMemberElementsIf uses that same per-member element index i, so the positional lookup is consistent with the table it indexes.
  • The bounds check (uint)elementIndex >= (uint)memberIds.Length safely handles the -1 default (used by the recursive WriteElement call at line 1253, where member is null and ChoiceSource is never invoked anyway), avoiding any IndexOutOfRangeException.
  • The any-element fallback path correctly passes elementIndex: i.
  • The added regression test covers the previously untested [XmlChoiceIdentifier] + [XmlEnum] alias combination and runs against both the reflection reader and ReflectionOnly configurations.

No actionable issues found. The change is correct, consistent with existing conventions, and well-tested.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 55.2 AIC · ⌖ 10.5 AIC · ⊞ 10K

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reflection-based XmlSerializer resolves [XmlChoiceIdentifier] enum incorrectly with [XmlEnum] aliases

2 participants