LightXmlWriter 1.1.0
dotnet add package LightXmlWriter --version 1.1.0
NuGet\Install-Package LightXmlWriter -Version 1.1.0
<PackageReference Include="LightXmlWriter" Version="1.1.0" />
paket add LightXmlWriter --version 1.1.0
#r "nuget: LightXmlWriter, 1.1.0"
// Install LightXmlWriter as a Cake Addin #addin nuget:?package=LightXmlWriter&version=1.1.0 // Install LightXmlWriter as a Cake Tool #tool nuget:?package=LightXmlWriter&version=1.1.0
LightXmlWriter
This is a replacement of XmlWriter which can be useful in high-performance scenarios.
There are several goals to reach:
- high performance
- zero allocation
- be faster than XmlWriter
- no runtime validation
- keep it simple (no configuration)
- allow writing concrete types (Span<T>, int, double, DateTime, etc.)
- allow to disable escaping values in similar way to Newtonsoft.Json
- produce as small as possible output XML
- easy migration from XmlWriter
LightXmlWriter vs XmlWriter
Benchmarks
OTA_Standard_XML.LightXmlWriter_Write_Xml
| Method | Mean | Error | StdDev | Median | Allocated |
|--------------- |----------:|----------:|----------:|--------:|----------:|
| LightXmlWriter | 8.585 us | 0.1733 us | 0.2429 us | 8.6 us | - |
| XmlWriter | 17.923 us | 0.4765 us | 1.3671 us | 18.3 us | 1288 B |
Similarities
- Method names are kept the same, and they should work the same way
For example for both writers:
writer.WriteStartElement("soapenv", "Envelope", "http://schemas.xmlsoap.org/soap/envelope/");
produce:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
- both are not thread-safe
Both writers keep state, so they must not be processed in parallel. LightXmlWriter does not do any validation, so it could produce an unexpeced output.
both implement these methods:
- WriteStartElement
- WriteEndElement
- WriteElementString
- WriteStartAttribute
- WriteEndAttribute
- WriteAttributeString
- WriteRaw
- WriteValue
both implement IDisposable interface
Differences
- LightXmlWriter contains more overloads of methods
The overloads accept int, double, bool, ReadOnlySpan<char> (only in .NET Core), char[] etc. You can even use overload that uses Action on TextWriter.
- WriteEndElement must have name of the element
XmlWriter:
writer.WriteStartElement("Person");
writer.WriteEndElement();
LightXmlWriter:
writer.WriteStartElement("Person");
writer.WriteEndElement("Person");
- LightXmlWriter does not use XmlWriterSettings
It only uses the default settings. That means, no validation at run-time, no pretty-print of output, no XML declaration - it is just simple writer.
- LightXmlWriter has better performance than XmlWriter
LightXmlWriter is about 2x faster and generates less allocations than XmlWriter because of methods overloads that don't need conversion to string before.
- LightXmlWriter produces more compressed self-closed xml tags
The compressed version does not contain space before closing singn. It is still valid XML.
XmlWriter:
writer.WriteStartElement("Person");
writer.WriteEndElement();
//produces <Person />
LightXmlWriter:
writer.WriteStartElement("Person");
writer.WriteEndElement("Person");
//produces <Person/>
- LightXmlWriter does not validate & escape tag and attribute names
XmlReader throws an exception in case of unescaped name.
- LightXmlWriter allows write values without escaping
This is similar to Newtonsoft JsonWriter where you can set flag escape: false
. It brings better performance than with enabled escape. Use it where you are sure that value does not need escaping.
Example:
writer.WriteStartElement("Code");
writer.WriteValue("ABCD", escape: false);
writer.WriteEndElement();
//produces <Code>ABCD</Code>
writer.WriteElementString("Code", "ABCD", escapeValue: false);
//produces <Code>ABCD</Code>
Migration from XmlWriter
- To each
WriteEndElement();
add argument with name of closing element, e.g.WriteEndElement("Person");
- Optionally, to
WriteEndAttribute();
add argument with name of closing attribute, e.g.WriteEndAttribute("Age");
- Optionally, use more adequate method overload to reduce string allocations, e.g.
WriteAttributeString("Age", 25.ToString());
toWriteAttributeString("Age", 25);
- Optionally. disable value escaping where it is not needed, e.g.
WriteAttributeString("Code", "ABC123");
toWriteAttributeString("Code", "ABC123", escapeValue: false);
- If you see any difference in output, report it here
Contributing
You are welcome to help with this package. There are also a lot to do: write more benchmark tests, more tests, more overloads, finding bugs, making optimisations of existing code.
Acknowledgments
Please use it carefully. LightXmlWriter can produce an invalid XML, so write tests for each output.
Requirements
Framework compatible with .NET Standard 1.3 (.NET Core 1.0, .NET Framework 4.6, Mono 4.6) or higher.
Currently ReadOnlySpan<char> overloads are only available in .NET Core 2.1 and .NET 5.0 builds.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp1.0 was computed. netcoreapp1.1 was computed. netcoreapp2.0 was computed. netcoreapp2.1 is compatible. netcoreapp2.2 is compatible. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard1.3 is compatible. netstandard1.4 was computed. netstandard1.5 was computed. netstandard1.6 was computed. netstandard2.0 was computed. netstandard2.1 is compatible. |
.NET Framework | net46 was computed. net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen30 was computed. tizen40 was computed. tizen60 was computed. |
Universal Windows Platform | uap was computed. uap10.0 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETCoreApp 2.1
- No dependencies.
-
.NETCoreApp 2.2
- No dependencies.
-
.NETStandard 1.3
- NETStandard.Library (>= 1.6.1)
- System.Buffers (>= 4.5.1)
- System.Memory (>= 4.5.5)
-
.NETStandard 2.1
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
- Added ReadOnlySpan API to netstandard1.3 build