Add Separator property to XmlTextAttribute and XmlAttributeAttribute for configurable list serialization#126767
Draft
Add Separator property to XmlTextAttribute and XmlAttributeAttribute for configurable list serialization#126767
Separator property to XmlTextAttribute and XmlAttributeAttribute for configurable list serialization#126767Conversation
…for configurable list serialization Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/68db653f-3600-4ba3-b399-78995d7c9d4b Co-authored-by: StephenMolloy <19562826+StephenMolloy@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Add Separator property to XmlTextAttribute and XmlAttributeAttribute
Add Apr 10, 2026
Separator property to XmlTextAttribute and XmlAttributeAttribute for configurable list serialization
This was referenced Apr 11, 2026
Open
Open
1 task
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.
[XmlText] string[]has always concatenated array items with no separator (val1val2), while[XmlAttribute] string[]uses space separation (val1 val2). This inconsistency is intentional and tested — changing the default would be breaking. This PR adds an opt-inSeparatorproperty to both attributes so users can control the separator character.API Changes
XmlTextAttribute— newchar Separator { get; set; }(default'\0'= no separator, preserves existing concatenation behavior):XmlAttributeAttribute— samechar Separator { get; set; }(default'\0'= use existing space behavior):'\0'(null char) is the "not set" sentinel — chosen becausechar?is not valid as a C# attribute argument type (CS0655).Implementation
XmlTextAttribute/XmlAttributeAttribute: addchar SeparatorpropertyMappings.cs: addchar? SeparatortoTextAccessorandAttributeAccessor(internal;char?is valid here)XmlReflectionImporter: wire attribute → accessor, validate withXmlConvert.VerifyXmlChars()(rejects XML-illegal chars like\x01;'\0'sentinel skips validation)ReflectionXmlSerializationWriter,XmlSerializationWriter,XmlSerializationWriterILGen): whenTextAccessor.Separator.HasValue, emit items with separator between them; for attributes, useSeparator ?? ' 'ReflectionXmlSerializationReader,XmlSerializationReader,XmlSerializationReaderILGen): whenTextAccessor.Separator.HasValue, split text on separator and populate array; usesstring.Split(char)overload (notchar[]) for IL-gen compatibilitySystem.Xml.ReaderWriterref assembly: updated with new public surfaceBackward Compatibility
[XmlText] string[]with noSeparator→ unchanged concatenation (val1val2)[XmlAttribute] string[]with noSeparatoroverride → unchanged space separation (val1 val2)XML_TypeWithXmlTextAttributeOnArraycontinues to assertval1val2Original prompt
Context
Issue #115837 reports that
[XmlText] string[]on element content serializes array items concatenated without any separator (abcd), while[XmlAttribute] string[]serializes them space-separated (a b c d). This inconsistency has existed since .NET Framework and is the documented/tested behavior — changing the default would be a breaking change.Design Decision (from discussion with area owner)
Rather than adding a simple
IsListboolean, the solution is to add achar? Separatorproperty to bothXmlTextAttributeandXmlAttributeAttribute, allowing users to opt into list-style serialization with a configurable separator character.Key design points:
XmlTextAttribute.Separator— defaults tonull(meaning no separator, preserving current concatenation behavior of[XmlText] string[]). Setting e.g.Separator = ' 'opts into space-separated list serialization for element text content.XmlAttributeAttribute.Separator— defaults to' '(preserving existing space-separated behavior for[XmlAttribute] string[]). Users can override to a different separator if desired.Type should be
char?(nullable char), wherenullmeans "no separator / use default behavior". This eliminates multi-character separator edge cases and is simple to validate and emit.Validation: Use
XmlConvert.VerifyXmlChars()on the separator character at reflection/import time (inXmlReflectionImporter). TheXmlWriterlayer already handles escaping of characters like<,&,>etc. in both attribute values and text content, so those are safe — they'll be entity-escaped in the output. The only truly dangerous characters are those illegal in XML 1.0 entirely (like\0), whichVerifyXmlCharscatches.Implementation Guide
Files that need changes:
Public API surface:
src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlTextAttribute.cs— Addpublic char? Separator { get; set; }property (default:null)src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlAttributes.cs— TheXmlAttributeAttributeclass needs the samepublic char? Separator { get; set; }property (default:' ')Internal mapping infrastructure:
src/libraries/System.Private.Xml/src/System/Xml/Serialization/Mappings.cs—TextAccessoris currently an empty class (internal sealed class TextAccessor : Accessor { }). Add anIsListproperty (orSeparatorproperty) mirroring whatAttributeAccessoralready has with itsIsListbool. Consider whether to store the separator char itself or just a bool here.Reflection import (wiring up the attribute to the mapping):
src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlReflectionImporter.cs— InImportAccessorMapping, around line 1630-1640, where[XmlText]on array-like types creates aTextAccessor, wire up the separator from the attribute to the accessor. Validate the separator character here usingXmlConvert.VerifyXmlChars(). Also around line 1595, whereisListis computed for attributes, incorporate the newSeparatorproperty fromXmlAttributeAttribute.Serialization writers (emitting the separator during write):
src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs— InWriteMember, the attributeIsListpath already writes" "between values. Add analogous logic forTextAccessorwhen it has a separator.src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs— Similar changes for the non-reflection writer path.src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriterILGen.cs— IL-gen'd serializer path needs the same logic.Deserialization readers (splitting on the separator during read):
IsListdeserialization currently splits on whitespace and apply similar logic for text content.src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cssrc/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cssrc/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.csWhat NOT to change:
[XmlAttribute] string[]default behavior must remain space-separated (backward compatible)[XmlText] string[]default behavior must remain concatenated with no separator (backward compatible)XML_TypeWithXmlTextAttributeOnArrayassertsval1val2concatenation — this must continue to passTests to add:
[XmlText(Separator = ' ')] string[]round-trips as space-separated text content[XmlText(Separator = ',')] string[]round-trips with comma separation[XmlText] string[](no separa...This pull request was created from Copilot chat.