Overview
Cuemon.Xml delivers a lightweight XML serialization framework that gives consumers the same level of converter flexibility that Newtonsoft provides for JSON. The core entry point is XmlFormatter, a StreamFormatter<XmlFormatterOptions> that serializes objects to Stream and deserializes streams back to typed instances. Converters for common BCL types such as Uri, DateTime, TimeSpan, string, IEnumerable, Exception, and Failure are registered by default, and custom converters can be added inline through DynamicXmlConverter.Create or by subclassing XmlConverter<T>.
The package also extends System.Xml with decorator-based helpers. Extension methods on IDecorator<Stream>, IDecorator<XmlReader>, IDecorator<XmlWriter>, and IDecorator<string> cover encoding detection, reader chunking, qualified-element writing, XML escaping, and element-name sanitization. Static factories XmlDocumentFactory, XmlStreamFactory, and XPathDocumentFactory reduce boilerplate when constructing XmlDocument, XPathDocument, or raw XML streams from diverse sources.
Key APIs
XmlFormatter is the primary serialization entry point. It accepts an optional Action<XmlFormatterOptions> delegate and exposes Serialize(object source, Type objectType) => Stream and Deserialize(Stream value, Type objectType) => object (plus generic overloads from its base class). Instantiating without arguments applies all default converters; calling new XmlFormatter(o => o.Settings.Writer.Indent = true) adds indentation while keeping the defaults.
XmlFormatterOptions configures XmlFormatter. Its Settings property holds an XmlSerializerOptions instance that exposes Converters (a mutable IList<XmlConverter>), Writer (XmlWriterSettings), Reader (XmlReaderSettings), RootName (XmlQualifiedEntity), and FlattenCollectionItems. The SensitivityDetails (FaultSensitivityDetails) flag controls how much exception detail the ExceptionConverter and ExceptionDescriptorConverter include. Setting SynchronizeWithXmlConvert to true propagates options to XmlConvert.DefaultSettings.
XmlConverter and XmlConverter<T> are the abstract base classes for all pluggable converters. A concrete subclass must implement CanConvert(Type), WriteXml(XmlWriter, T, XmlQualifiedEntity), and ReadXml(Type, XmlReader). The base class exposes virtual CanRead and CanWrite properties, both defaulting to true. XmlConverter<T> is constrained to where T : class and seals the untyped overloads.
DynamicXmlConverter.Create<T> produces an XmlConverter from inline writer and reader delegates without requiring a subclass. The writer delegate receives an XmlWriter, the typed value, and an optional XmlQualifiedEntity; the reader delegate receives an XmlReader and the target Type. A canConvertPredicate delegate can further restrict which types the converter handles.
XmlConverterDecoratorExtensions provides builder-style extension methods on IDecorator<IList<XmlConverter>>. Methods such as AddEnumerableConverter(bool flattenItems), AddExceptionConverter(bool includeStackTrace, bool includeData), AddFailureConverter(), AddDateTimeConverter(), AddTimeSpanConverter(), AddUriConverter(), and AddStringConverter() each return the same decorator, enabling chained registration. AddXmlConverter<T> and InsertXmlConverter<T> accept raw delegate arguments for inline anonymous converters.
XmlQualifiedEntity carries the prefix, local name, and namespace for an XML element. Constructors accept a plain local name string, a (prefix, localName, namespace) triple, or any of the standard System.Xml.Serialization attributes (XmlRootAttribute, XmlElementAttribute, XmlAttributeAttribute, XmlAnyElementAttribute). The HasXmlElementDecoration, HasXmlAttributeDecoration, and HasXmlAnyElementDecoration properties indicate which attribute drove construction.
XmlDocumentFactory and XmlStreamFactory are static helpers that reduce boilerplate. XmlDocumentFactory.CreateDocument has overloads accepting Stream, XmlReader, Uri, or a raw XML string. XmlStreamFactory.CreateStream(Action<XmlWriter> writer, Action<XmlWriterSettings> setup) constructs a fully written and rewound Stream from a writer callback, avoiding the repetitive create-flush-rewind pattern.
Basic usage
using System.IO;
using System.Xml;
using Codebelt.Extensions.Xunit;
using Cuemon.Xml.Serialization.Formatters;
using Xunit;
namespace MyProject.Tests;
public class SensorReadingTest : Test
{
public SensorReadingTest(ITestOutputHelper output) : base(output)
{
}
[Fact]
public void XmlFormatter_ShouldRoundTripDomainObject()
{
var reading = new SensorReading { StationId = "STN-42", TemperatureC = 21, Humidity = 65 };
var formatter = new XmlFormatter(o => o.Settings.Writer.Indent = true);
var stream = formatter.Serialize(reading, typeof(SensorReading));
var doc = new XmlDocument();
doc.Load(stream);
TestOutput.WriteLine(doc.OuterXml);
Assert.Contains("<StationId>STN-42</StationId>", doc.OuterXml);
Assert.Contains("<TemperatureC>21</TemperatureC>", doc.OuterXml);
stream.Position = 0;
var restored = (SensorReading)formatter.Deserialize(stream, typeof(SensorReading));
Assert.Equal(reading.StationId, restored.StationId);
Assert.Equal(reading.TemperatureC, restored.TemperatureC);
Assert.Equal(reading.Humidity, restored.Humidity);
}
}
public class SensorReading
{
public string StationId { get; set; }
public int TemperatureC { get; set; }
public int Humidity { get; set; }
}
Use this pattern when you need XML round-trip serialization of domain objects with configurable writer settings and no dependency on System.Xml.Serialization attributes. The default converter pipeline handles primitive properties and common BCL types automatically, so only types requiring custom XML shape need an explicit XmlConverter.
Installation
dotnet add package Cuemon.Xml
Usage guidance
Cuemon.Xml is the right choice when you need pluggable XML converters for BCL types, integrated exception or Failure serialization, fine-grained XmlWriterSettings control, or stream-oriented serialization that fits into a pipeline alongside the package's XmlReaderDecoratorExtensions chunking and hierarchy helpers. If your only requirement is attribute-driven serialization of simple POCOs, the BCL System.Xml.Serialization.XmlSerializer covers that without an extra dependency; reach for Cuemon.Xml when you need the converter extension model or the FlattenCollectionItems option to avoid generic Item wrapper elements around collections.
Family packages
- 🌐Cuemon.AspNetCore
- 🏭Cuemon.AspNetCore.App
- 🌐Cuemon.AspNetCore.Authentication
- 🌐Cuemon.AspNetCore.Mvc
- 🌐Cuemon.AspNetCore.Razor.TagHelpers
- 📦Cuemon.Core
- 🏭Cuemon.Core.App
- 🗄️Cuemon.Data
- 🗄️Cuemon.Data.Integrity
- 🗄️Cuemon.Data.SqlClient
- 🩺Cuemon.Diagnostics
- 🌐Cuemon.Extensions.AspNetCore
- 🌐Cuemon.Extensions.AspNetCore.Authentication
- 🌐Cuemon.Extensions.AspNetCore.Mvc
- 🌐Cuemon.Extensions.AspNetCore.Mvc.Formatters.Text.Json
- 🌐Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml
- 🌐Cuemon.Extensions.AspNetCore.Mvc.RazorPages
- 🌐Cuemon.Extensions.AspNetCore.Text.Json
- 🌐Cuemon.Extensions.AspNetCore.Xml
- 📦Cuemon.Extensions.Collections.Generic
- 📦Cuemon.Extensions.Collections.Specialized
- 📦Cuemon.Extensions.Core
- 🗄️Cuemon.Extensions.Data
- 🗄️Cuemon.Extensions.Data.Integrity
- 📦Cuemon.Extensions.DependencyInjection
- 🩺Cuemon.Extensions.Diagnostics
- 🏗️Cuemon.Extensions.Hosting
- 📦Cuemon.Extensions.IO
- 📦Cuemon.Extensions.Net
- 📦Cuemon.Extensions.Reflection
- 📦Cuemon.Extensions.Runtime.Caching
- 📝Cuemon.Extensions.Text
- 📝Cuemon.Extensions.Text.Json
- 📦Cuemon.Extensions.Threading
- 📦Cuemon.Extensions.Xml
- 📦Cuemon.IO
- ⚙️Cuemon.Kernel
- 📦Cuemon.Net
- 📦Cuemon.Resilience
- 📦Cuemon.Runtime.Caching
- 🔐Cuemon.Security.Cryptography
- 📦Cuemon.Threading