Skip to content

Instantly share code, notes, and snippets.

@meixger
Last active May 21, 2025 09:18
Show Gist options
  • Select an option

  • Save meixger/c3a0f66f2db6ee26b728c015fb29d897 to your computer and use it in GitHub Desktop.

Select an option

Save meixger/c3a0f66f2db6ee26b728c015fb29d897 to your computer and use it in GitHub Desktop.

xs:list - Serializing a array as innerText of an xml element

The XmlTextAttribute informs the XmlSerializer that the value should be serialized XML text.

This works for string but fails for string[]

Expected:

<root>1 2 3</root> An array of int should be serialized as whitespace separated collection of values

Actual:

<root>123</root missing whitespace separators

Alternative using attributes

<root ids="1 2 3" /> Serialization and deserialization of string[] works with XML attributes.

.NET Framework documentation

"The .NET Framework provides accurate bindings for XML attribute declarations with list types, but not for element declarations." source

.NET [Core] documentation

No mention of this behavior. source

Links

using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Xml;
using System.Xml.Serialization;
public class Program
{
public static void Main()
{
// Serialize a array as element text using XmlTextAttribute
// e.g.: <root>1 2 3</root>
// FAIL:
// serialization: missing whitespace separators: <Root1>123</Root1>
// deserialization: doesn't split on whitespace and
XmlElement();
// OK: shadow property does splitting and joining
ShadowProperty();
// OK: alternative using attribute: <Root2 Ids="1 2 3" />
XmlAttribute();
}
public static void XmlElement()
{
Console.WriteLine("Root1 string[] property with XmlTextAttribute");
var ser = new XmlSerializer(typeof(Root1));
var sb = new StringBuilder();
var writer = XmlWriter.Create(sb, new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true });
ser.Serialize(writer, new Root1 { Ids = ["1", "2", "3"] }, new XmlSerializerNamespaces([new XmlQualifiedName("", "")]));
Console.WriteLine($"[FAIL] serialize is missing whitespace separators: {sb}");
// expected: <Root1>1 2 3</Root1>
// actual: <Root1>123</Root1>
Root1 root1 = (Root1)ser.Deserialize(new StringReader("<Root1>1 2 3</Root1>"))!;
Console.WriteLine($"[FAIL] deserialize returns only 1 squashed element instead of 3 separated: {JsonSerializer.Serialize(root1.Ids)}\n");
// expected: 3 elements: ["1", "2", "3"]
// actual: 1 element: "1 2 3"
}
public static void ShadowProperty()
{
Console.WriteLine("Root2 workaround with shadow property for splitting and joining:");
var ser = new XmlSerializer(typeof(Root2));
var sb = new StringBuilder();
var writer = XmlWriter.Create(sb, new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true });
ser.Serialize(writer, new Root2 { Ids = [1, 2, 3] }, new XmlSerializerNamespaces([new XmlQualifiedName("", "")]));
Console.WriteLine($"[OK] Root2 serialize as whitespace separated list: {sb}");
// expected: <Root2>1 2 3</Root2>
// actual: correct
// Deserialization
var xml = "<Root2>1 2 3</Root2>";
var stream = new StringReader(xml);
var root2 = ser.Deserialize(stream) as Root2;
Console.WriteLine($"[OK] Root2 deserialize has 3 elements: {JsonSerializer.Serialize(root2.Ids)}\n");
// expected: 3 elements
// actual: correct
}
public static void XmlAttribute()
{
Console.WriteLine("Root3 alternative serialization using xml attribute:");
var ser = new XmlSerializer(typeof(Root3));
var sb = new StringBuilder();
var writer = XmlWriter.Create(sb, new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true });
ser.Serialize(writer, new Root3 { Ids = ["1", "2", "3"] }, new XmlSerializerNamespaces([new XmlQualifiedName("", "")]));
Console.WriteLine($"[OK] serialize: {sb}");
// expected : attribute Ids="1 2 3"
// actual: correct
var root3 = ser.Deserialize(new StringReader("""<Root3 Ids="1 2 3" />""")) as Root3;
Console.WriteLine($"[OK] deserialize: {JsonSerializer.Serialize(root3.Ids)}\n");
// expected: 3 elements
// actual: correct
}
}
public class Root1
{
// The XmlTextAttribute [with type set to string] informs the XmlSerializer that strings should be serialize as XML [inner]text.
// Unfortunately, array on elements are not supported:
// .NET Framework https://learn.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/t84dzyst(v=vs.100) "The .NET Framework provides accurate bindings for XML attribute declarations with list types, **but not for element declarations**."
// .NET https://learn.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmltextattribute
[XmlText]
public string[] Ids;
}
public partial class Root2
{
[XmlText]
public string Value;
}
public partial class Root2
{
[XmlIgnore]
public int[] Ids
{
get => Value.Split(" ").Where(v => !string.IsNullOrWhiteSpace(v)).Select(int.Parse).ToArray();
set => Value = string.Join(" ", value);
}
}
public class Root3
{
[XmlAttribute]
public string[] Ids;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment