Source-Generated XML and Protobuf in C

Source-generated XML and Protobuf serialization in modern C# replaces traditional runtime reflection with compile-time code generation to transform objects into serialized formats. This approach drastically improves application startup times, eliminates memory overhead associated with reflection, and ensures full compatibility with Native Ahead-Of-Time (AOT) compilation in modern .NET runtimes. This article covers how source generators handle XML and Protocol Buffers (Protobuf), why they outperform traditional serialization mechanisms, and their primary benefits in modern, high-performance systems.

The Problem with Traditional Serialization

Historically, serializers such as System.Xml.Serialization.XmlSerializer and early versions of protobuf-net relied heavily on runtime reflection and runtime code emission via Reflection.Emit. At startup, these libraries inspect an object's properties, types, and custom attributes to dynamically construct serialization logic.

While flexible, this approach introduces distinct drawbacks:

  • Startup Latency: Dynamically generating serialization logic at startup increases cold-start times.
  • High Memory Usage: Caching metadata and generated dynamic assemblies consumes non-trivial amounts of memory.
  • Incompatibility with Native AOT: Modern .NET platforms targeting Native AOT cannot use dynamic code generation or heavy reflection because unused code is stripped during linking, causing runtime failures.

How Source-Generated Serialization Works

Introduced in .NET 5 and expanded in subsequent releases, Roslyn Source Generators allow developers to inspect C# code during compilation and generate additional C# source files directly into the build pipeline.

When applied to serialization:

  1. Inspection: The source generator scans data contract classes decorated with specific attributes (such as XML attributes or Protobuf tags).
  2. Code Generation: Instead of deferring logic to runtime, the generator produces concrete, strongly typed C# methods that read directly from or write directly to format-specific streams and buffers.
  3. Compilation: The emitted serialization code compiles alongside your application logic into the final binary.

Source-Generated Protobuf

In the Protobuf ecosystem, tools like protobuf-net provide source generators, and Google's official protoc compiler generates standard C# contracts. Modern C# protobuf source generators inspect POCOs decorated with [ProtoContract] and [ProtoMember] attributes to emit direct binary encoding and decoding routines. This translates to direct writes into IBufferWriter<byte> or Span<byte>, avoiding boxing and runtime type inspection.

Source-Generated XML

While .NET introduced the built-in System.Text.Json source generator in .NET 6, XML serialization in modern high-throughput architectures has historically relied on external tooling or ahead-of-time tools like sgen.exe. Modern source generators for XML analyze types marked with [XmlRoot] and [XmlElement], generating custom XmlReader and XmlWriter implementations directly. The generated code explicitly advances tokens and writes nodes without checking metadata at runtime.

Key Benefits in Modern C# Systems

1. Native AOT Compatibility

Native AOT requires all executable code to exist before compilation finishes. Source-generated serializers produce standard, static C# code, enabling applications to run under Native AOT without missing member exceptions or requiring complex trimming configuration files.

2. Reduced Latency and Allocation

Generated serializers call strongly typed properties directly. This eliminates boxing of value types, bypasses dynamic dispatch, and utilizes low-allocation APIs such as ReadOnlySpan<T> and Memory<T>.

3. Compile-Time Diagnostics

Because logic is evaluated during compilation, invalid contract designs—such as duplicate Protobuf tags, non-serializable property types, or missing parameterless constructors—are surfaced as compiler errors or warnings instead of failing silently until a request is handled in production.

Conclusion

Source-generated XML and Protobuf serialization modernize C# data handling by moving serialization work from runtime to compile time. By shifting to source-generated serialization, modern .NET services achieve faster cold starts, lower allocations, and full readiness for Native AOT deployment.