Home

Awesome

StackXML

Stack based zero*-allocation XML serializer and deserializer powered by C# 9 source generators.

Why

Premature optimisation :)

Setup

<ItemGroup>
    <ProjectReference Include="..\StackXML\StackXML.csproj" />
    <ProjectReference Include="..\StackXML.Generator\StackXML.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

Features

Quirks

Performance

Very simple benchmark, loading a single element and getting the string value of its attribute attribute


BenchmarkDotNet=v0.12.1, OS=Windows 10.0.17134.1845 (1803/April2018Update/Redstone4)
Intel Core i5-6600K CPU 3.50GHz (Skylake), 1 CPU, 4 logical and 4 physical cores
.NET Core SDK=5.0.100
  [Host]     : .NET Core 5.0.0 (CoreCLR 5.0.20.51904, CoreFX 5.0.20.51904), X64 RyuJIT
  DefaultJob : .NET Core 5.0.0 (CoreC CoreCLRLR 5.0.20.51904, CoreFX 5.0.20.51904), X64 RyuJIT
MethodMeanErrorStdDevRatioRatioSDGen 0Gen 1Gen 2Allocated
ReadBuffer95.81 ns0.983 ns0.872 ns1.000.000.0178--56 B
XmlReader1,866.22 ns37.250 ns79.383 ns19.570.873.3216--10424 B
XDocument2,286.97 ns45.784 ns124.560 ns24.481.163.4313--10776 B
XmlDocument2,869.48 ns44.058 ns39.057 ns29.960.603.9196--12328 B
XmlSerializer10,386.07 ns152.481 ns142.631 ns108.441.494.7150--14882 B

Example data classes

Simple Attribute

<test attribute='value'/>
[XmlCls("test"))]
public partial class Test
{
    [XmlField("attribute")]
    public string m_attribute;
}

Text body

<test2>
    <name><![CDATA[Hello world]]></name>
</test2>

CData can be configured by setting cdataMode for serializing and deserializing

<test2>
    <name>Hello world</name>
</test2>
[XmlCls("test2"))]
public partial class Test2
{
    [XmlBody("name")]
    public string m_name;
}

Lists

<container>
    <listItem name="hey" age='25'/>
    <listItem name="how" age='2'/>
    <listItem name="are" age='4'/>
    <listItem name="you" age='53'/>
</container>
[XmlCls("listItem"))]
public partial class ListItem
{
    [XmlField("name")]
    public string m_name;
    
    [XmlField("age")]
    public int m_age; // could also be byte, uint etc
}

[XmlCls("container")]
public partial class ListContainer
{
    [XmlBody()]
    public List<ListItem> m_items; // no explicit name, is taken from XmlCls
}

Delimited attributes

<musicTrack id='5' artists='5,6,1,24,535'>
    <n><![CDATA[Awesome music]]></n>
    <tags>cool</tags>
    <tags>awesome</tags>
    <tags>fresh</tags>
</musicTrack>
[XmlCls("musicTrack"))]
public partial class MusicTrack
{
    [XmlField("id")]
    public int m_id;
    
    [XmlBody("n")]
    public string m_name;
    
    [XmlField("artists"), XmlSplitStr(',')]
    public List<int> m_artists;
    
    [XmlBody("tags")]
    public List<string> m_tags;
}