High-performance, memory-efficient, enterprise-grade XSD schema validation engine and precompiled schema cache for modern .NET.
EricksonLopez.Xml.Validation is an enterprise-grade XSD schema validation engine and thread-safe precompiled schema cache for modern .NET (.NET 8, .NET 9, .NET 10). Architected with Anti-XXE protection enforced by default, structured Result<bool> error reporting (Railway-Oriented Programming via EricksonLopez.Result), and first-class Microsoft Dependency Injection integration, it eliminates the severe security hazards, CPU exception-handling overhead, and repetitive compilation bottlenecks of legacy BCL XML APIs. The engine features reduced-allocation streaming with zero intermediate managed string allocations via ReadOnlySpan<byte>, zero-allocation compile-time [LoggerMessage] diagnostics, and certified compatibility under Native AOT and trimming.
- What Problem It Solves
- Key Features
- Ecosystem
- Documentation
- Installation
- Quick Start
- Core Use Cases
- Use Case 1: Perimeter Defensive Gateway in ASP.NET Core Minimal APIs
- Use Case 2: Asynchronous HTTP Body Stream Validation with Cooperative Cancellation
- Use Case 3: High-Throughput In-Memory Queue Buffer Processing
- Use Case 4: Bulk Schema Precompilation on Application Startup
- Use Case 5: Strict Regulatory Compliance with Warning Escalation
- Use Case 6: Custom Telemetry and Metrics Enrichment via Decorator Pattern
- Configuration & Integrations
- Testing & Quality
- Performance Benchmarks
- Compatibility & Technical Matrix
- Architecture & Design Principles
- Best Practices & Anti-Patterns
- Troubleshooting & Common Pitfalls
- Part of the EricksonLopez Ecosystem
- Contributing
- License
Validating XML against XML Schema Definitions (XSD) in modern enterprise systems introduces critical challenges across security, allocation pressure, latency, and reliability:
The default configurations of traditional BCL XML classes (XmlDocument, legacy XmlReaderSettings, XmlSchemaSet) historically permit external DTD processing, resolving untrusted URIs, and expanding internal entities. Unsanitized XML payloads expose systems to XML External Entity (XXE) injection (OWASP Top 10, CWE-611), Server-Side Request Forgery (SSRF), local file disclosure (file:///etc/passwd), and denial-of-service via exponential entity expansion (Billion Laughs attack).
Standard BCL validation relies heavily on throwing XmlSchemaValidationException and XmlException whenever a document is malformed or invalid. In high-throughput ingestion pipelines, handling thousands of malformed or non-compliant payloads results in massive stack unrolling overhead, thread synchronization stalls, and excessive Garbage Collector pressure on Generation 0/1.
Compiling an XSD schema requires building a complex type graph, validating content models, resolving namespaces, and generating state machines. Ad-hoc compilation per request (new XmlSchemaSet().Compile()) wastes up to 47 ms per 1,000 requests on small schemas and orders of magnitude more on massive schemas (e.g., UBL 2.1 e-Invoicing or ISO 20022 pain.001).
Standard XML validation workflows routinely require reading full payloads into string instances on the heap before validation can commence. For high-volume socket buffers, Kafka/RabbitMQ queue messages, or large payloads, this induces severe memory fragmentation and GC pauses.
-
Hardened Anti-XXE Defaults by Design: Enforces
DtdProcessing.ProhibitandXmlResolver = nullacross every internal reader and schema compilation pipeline. This protection cannot be bypassed through public APIs. -
Railway-Oriented Functional Returns: Replaces exception throws with structured
Result<bool>returns powered byEricksonLopez.Result. Business code remains functional, predictable, and allocation-friendly. -
$O(1)$ Precompiled Schema Cache: The thread-safeXmlSchemaCacheprecompiles and stores schemas in aConcurrentDictionary<string, SchemaCacheEntry>(keyed by target namespace, each entry holding a compiledXmlSchemaSetplus a pre-indexed global-elementHashSet) with lock-free read paths, delivering 5x to 100x speedup on repeated validations depending on schema complexity. -
Reduced-Allocation Span Validation: Full native support for
ReadOnlySpan<byte>enables validating UTF-8 byte payloads via pinned unmanaged memory (fixed+UnmanagedMemoryStream), eliminating intermediate managed string allocations. BCLXmlReaderinternal buffers (~2.16 KB) still allocate on Gen 0. -
Zero-Allocation Logging: Structured diagnostic logging powered by compile-time source-generated
[LoggerMessage]partial methods (Event IDs1001β1005).
- π‘οΈ Anti-XXE Enforced by Default:
DtdProcessing.ProhibitandXmlResolver = nullapplied unconditionally. External entity resolution and DTD processing are prohibited on all execution paths. - β‘ Precompiled Thread-Safe Schema Cache:
ConcurrentDictionary-backedIXmlSchemaCacheindexed bytargetNamespace, enabling thread-safe, lock-free read paths with$O(1)$ schema retrieval. Cache introspection viaContainsSchema,Count,IsRootElementDeclared, andClearβ see api-inventory.md for the full member catalog. - π Railway-Oriented Structured Errors: Returns
Result<bool>containing strongly typed error descriptors (Error.Validation,Error.NotFound) with zero control-flow exceptions. - π Precise Line & Column Diagnostics: Automatically extracts line numbers and line positions from
IXmlLineInfofor actionable schema and syntax error diagnosis. - π Multi-Modal Input Support: Direct validation over
string,Stream(retaining stream ownership),ReadOnlySpan<byte>(no intermediate managed-string allocations; BCLXmlReaderbuffers ~2.16 KB on Gen 0), and asynchronousStreamwith cooperativeCancellationToken. - π Bulk Directory Precompilation: Recursively discovers, loads, and compiles all matching
.xsdschemas from filesystem directories at startup viaRegisterSchemasFromDirectory(). β οΈ Configurable Warning Escalation: Granular control viaXmlValidationOptionsto capture non-fatal schema warnings or escalate them to full validation failures (TreatWarningsAsErrors).- π Zero-Allocation Structured Diagnostics: High-performance logging using C# compile-time
[LoggerMessage]partial methods (Event IDs1001β1005). - π First-Class Microsoft DI Integration: One-line container registration via
services.AddXmlValidation()with idempotent singleton lifecycles (TryAddSingleton) andIOptions<XmlValidationOptions>support. - π 100% Native AOT & Trim Safe: Compiled with
<IsAotCompatible>true</IsAotCompatible>and verified with dedicated ahead-of-time compilation smoke-test executables. Zero runtime reflection. - π― Multi-Targeting Modern .NET: Native support across
.NET 8.0 (LTS),.NET 9.0 (STS), and.NET 10.0.
| Package | Version | Target Frameworks | Description |
|---|---|---|---|
EricksonLopez.Xml.Validation |
net8.0, net9.0, net10.0 |
High-performance XSD schema validation engine with Anti-XXE defaults, precompiled cache, and Native AOT support. |
π Official Documentation: https://ericksonlopez.dev/xml | Technical Docs Hub: https://github.com/ericksonlopezf/dotnet-xml/tree/main/docs
The repository includes an official, interactive reference implementation located in samples/EricksonLopez.Xml.Showcase.
| Level | Topic | Description | Code Reference |
|---|---|---|---|
| Level 00 | Conceptual Foundations & Anti-XXE Threat Modeling | Core architectural philosophy, threat modeling, and comparison with raw BCL APIs | Level00Conceptual.cs |
| Level 01 | Standalone Quick Start (No DI) | Minimal standalone initialization, schema registration, and Result-based inspection | Level01QuickStart.cs |
| Level 02 | Microsoft DI Configuration & Structured Logging | Service registration, singleton lifecycles, options configuration, and structured logs | Level02FullConfiguration.cs |
| Level 03 | Real-World Enterprise Schemas | Industrial validation using UBL 2.1 e-Invoice and ISO 20022 pain.001 schemas |
Level03RealWorldUseCases.cs |
| Level 04 | Advanced Input Modalities | Bulk directory loading, Stream, ReadOnlySpan<byte>, and async cancellation |
Level04AdvancedIntegration.cs |
| Level 05 | High Concurrency & Cooperative Cancellation | Stress testing under 200 concurrent tasks and early pipeline cancellation | Level05ProcessingAndConcurrency.cs |
| Level 06 | Error Handling Taxonomy & Attack Neutralization | Classification of missing schemas, violations, syntax failures, and XXE attacks | Level06ErrorHandlingAndClassification.cs |
| Level 07 | Micro-Benchmarks & Zero-Allocation Throughput | Empirical allocation metrics and high-throughput memory profiling | Level07ScalabilityAndThroughput.cs |
| Level 08 | Extensibility via Decorator Pattern | Intercepting IXmlSchemaValidator for telemetry, auditing, and fallback caching |
Level08CustomizationAndExtensibility.cs |
| Level 09 | Architectural Boundaries & Queue Consumers | Clean architecture segregation, background workers, and boundary guards | Level09ArchitecturalBoundariesAndConsumers.cs |
| Level 10 | Enterprise Perimeter Defensive Pipeline | Frontline DMZ gateway architecture neutralizing attacks before domain ingestion | Level10EnterpriseArchitecture.cs |
- Architecture Guide β System invariants, architectural boundaries, and component responsibilities.
- Architecture Flow & State Diagrams β Formal Mermaid pipeline diagrams, sequence diagrams, and lifecycle state machines.
- Architectural Decision Records (ADRs) β 17 formal records documenting decisions on Anti-XXE, Result types, caching, and Native AOT.
- Testing Roadmap & Quality Audit β Canonical test taxonomy, 100% coverage audit, and Stryker mutation testing specification.
- API Inventory & Member Catalog β Complete inventory of public interfaces, classes, extensions, and members.
- API Reference Guide β Microsoft Learn style reference with parameters, exceptions, and remarks.
- Performance Guide & Benchmarks β Allocation analysis, concurrency scalability, and BenchmarkDotNet results.
- Cookbook & Production Recipes β 10 production-tested recipes for ASP.NET Core, streaming, and resilience.
- Migration Guide β Step-by-step migration guide from legacy BCL
XmlReaderandXmlSchemaSet. - Package Specification & Ecosystem β Central Package Management, framework matrix, and packaging metadata.
- Troubleshooting Guide β Diagnosis and remediation for error codes, syntax errors, and schema violations.
- Showcase Execution Guide β Step-by-step instructions for executing and auditing the showcase project.
- Build & Quality Guide β MSBuild settings, compiler properties, Native AOT flags, and CI quality gates.
Install the package via the .NET CLI:
dotnet add package EricksonLopez.Xml.ValidationOr via the Visual Studio Package Manager Console:
Install-Package EricksonLopez.Xml.ValidationOr reference it directly in your project file (.csproj):
<PackageReference Include="EricksonLopez.Xml.Validation" Version="1.0.0" />All Microsoft Dependency Injection, Options, and Logging extensions are embedded directly in EricksonLopez.Xml.Validation without requiring auxiliary satellite packages. Railway-Oriented Programming support is provided transitively via EricksonLopez.Result (v2.0.0+).
When validating XML workflows in consumer test suites, assert on Result<bool> outcomes using AwesomeAssertions or standard xUnit/NUnit/MSTest assertions:
dotnet add package AwesomeAssertionsusing System;
using System.IO;
using EricksonLopez.Xml.Validation;
// 1. Initialize the thread-safe schema cache
var cache = new XmlSchemaCache();
// 2. Register schema from string, stream, span, or file
string xsdContent = File.ReadAllText("orders.xsd");
cache.RegisterSchema("https://example.com/orders", xsdContent);
// 3. Initialize validator instance
var validator = new XmlSchemaValidator(cache);string xmlPayload = File.ReadAllText("order.xml");
// Validate against the registered targetNamespace
var result = validator.Validate(xmlPayload, "https://example.com/orders");
if (result.IsSuccess)
{
Console.WriteLine("Document successfully validated against XSD schema!");
}
else
{
// Typed error with code, category, and line/column coordinates
Console.WriteLine($"Validation Failed [{result.Error.Code}]: {result.Error.Description}");
}For high-throughput pipelines, validate UTF-8 byte spans directly without materializing an intermediate managed string on the heap. The span is pinned via fixed and wrapped in an UnmanagedMemoryStream, so no payload buffer copy occurs. Note: BCL XmlReader internal parsing buffers (~2.16 KB) still allocate on Gen 0.
ReadOnlySpan<byte> utf8XmlBytes = File.ReadAllBytes("order.xml");
// Validates directly over UTF-8 bytes β no managed string materialization,
// no payload array copy. BCL XmlReader internal buffers still allocate (~2.16 KB).
var result = validator.Validate(utf8XmlBytes, "https://example.com/orders");
if (result.IsFailure)
{
Console.WriteLine($"Error: {result.Error.Description}");
}using Microsoft.AspNetCore.Http;
using EricksonLopez.Result;
var result = validator.Validate(xmlPayload, "https://example.com/orders");
if (result.IsFailure)
{
return result.Error.Code switch
{
"XmlValidation.SchemaNotRegistered" => Results.NotFound(new { error = result.Error.Description }),
"XmlValidation.XmlMalformed" => Results.BadRequest(new { error = result.Error.Description }),
"XmlValidation.SchemaViolation" => Results.UnprocessableEntity(new { error = result.Error.Description }),
_ => Results.StatusCode(StatusCodes.Status500InternalServerError)
};
}Deploy IXmlSchemaValidator as an edge security boundary to sanitize and validate incoming XML payloads before handing them to typed business binders:
app.MapPost("/api/invoices", async (
HttpRequest request,
IXmlSchemaValidator validator,
CancellationToken ct) =>
{
// Enable buffering so the request body stream can be rewound after validation
request.EnableBuffering();
// Validate stream directly without buffering as string
var result = await validator.ValidateAsync(
request.Body,
"https://ericksonlopez.dev/invoice",
ct);
if (result.IsFailure)
{
return result.Error.Code switch
{
"XmlValidation.SchemaViolation" => Results.UnprocessableEntity(result.Error.Description),
"XmlValidation.XmlMalformed" => Results.BadRequest(result.Error.Description),
_ => Results.Problem(result.Error.Description, statusCode: 500)
};
}
// Reset stream position for the typed deserializer (stream left open by validator; requires EnableBuffering)
request.Body.Position = 0;
var invoice = await DeserializeInvoiceAsync(request.Body, ct);
return Results.Ok(invoice);
});Validate large XML streams arriving over the network without blocking worker threads, honoring cancellation tokens during I/O and parse loops:
public async ValueTask<Result<bool>> ProcessStreamPayloadAsync(
Stream networkStream,
string targetNamespace,
CancellationToken cancellationToken)
{
// Validates asynchronously; leaves the stream open for subsequent downstream consumers
return await _validator.ValidateAsync(
networkStream,
targetNamespace,
cancellationToken);
}Process high-volume messages from Kafka or RabbitMQ directly from byte buffers with minimal memory overhead:
public void OnMessageReceived(ReadOnlySpan<byte> messageBuffer)
{
// Zero string allocation hot path
var result = _validator.Validate(messageBuffer, "https://ericksonlopez.dev/orders");
if (result.IsFailure)
{
_deadLetterPublisher.Publish(messageBuffer, result.Error);
return;
}
_queueDispatcher.Dispatch(messageBuffer);
}Scan schema repositories and precompile hundreds of XSD documents into the cache before receiving traffic:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddXmlValidation();
var app = builder.Build();
// Preload all enterprise schemas recursively at host boot
var cache = app.Services.GetRequiredService<IXmlSchemaCache>();
string schemasPath = Path.Combine(AppContext.BaseDirectory, "Schemas");
int precompiledCount = cache.RegisterSchemasFromDirectory(schemasPath, "*.xsd");
app.Logger.LogInformation("Successfully precompiled {Count} XSD schemas.", precompiledCount);
app.Run();Enforce strict financial or healthcare schemas (e.g., UBL 2.1 or ISO 20022) where any schema warning must be treated as a rejection:
builder.Services.AddXmlValidation(options =>
{
// In compliance mode, schema warnings are captured and escalated to validation failure
options.IncludeWarnings = true;
options.TreatWarningsAsErrors = true;
});Wrap IXmlSchemaValidator with a decorator to capture OpenTelemetry metrics, execution durations, and custom security audits:
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using EricksonLopez.Result;
using EricksonLopez.Xml.Validation;
public sealed class TelemetryXmlValidatorDecorator : IXmlSchemaValidator
{
private readonly IXmlSchemaValidator _inner;
// Meter must be instantiated; it is not a static class.
private static readonly Meter _meter = new("EricksonLopez.Xml.Validation", "1.0");
private static readonly Counter<long> ValidationFailures =
_meter.CreateCounter<long>("xml_validation_failures_total");
public TelemetryXmlValidatorDecorator(IXmlSchemaValidator inner) => _inner = inner;
public Result<bool> Validate(string xmlContent, string targetNamespace)
{
var result = _inner.Validate(xmlContent, targetNamespace);
if (result.IsFailure)
{
ValidationFailures.Add(1, new KeyValuePair<string, object?>("error_code", result.Error.Code));
}
return result;
}
public Result<bool> Validate(Stream xmlStream, string targetNamespace) =>
_inner.Validate(xmlStream, targetNamespace);
public Result<bool> Validate(ReadOnlySpan<byte> utf8XmlBytes, string targetNamespace) =>
_inner.Validate(utf8XmlBytes, targetNamespace);
public Task<Result<bool>> ValidateAsync(
Stream xmlStream,
string targetNamespace,
CancellationToken cancellationToken = default) =>
_inner.ValidateAsync(xmlStream, targetNamespace, cancellationToken);
}EricksonLopez.Xml.Validation provides first-class support for Microsoft.Extensions.DependencyInjection:
using EricksonLopez.Xml.Validation;
// Register default singletons
builder.Services.AddXmlValidation();
// Or configure options explicitly covering the full options surface
builder.Services.AddXmlValidation(options =>
{
options.IncludeWarnings = true; // Captures warnings in error descriptions
options.TreatWarningsAsErrors = false; // Warnings will not fail validation unless true
options.MaxCharactersInDocument = 5_000_000; // Defense against XML DoS
options.MaxErrors = 50; // Cap error collection against memory exhaustion
options.ProcessInlineSchema = false; // Defense against inline schema poisoning
});The extension registers both IXmlSchemaCache (XmlSchemaCache) and IXmlSchemaValidator (XmlSchemaValidator) as Singletons in the container.
When ILogger<XmlSchemaValidator> is registered in the service provider, XmlSchemaValidator emits structured, source-generated diagnostic events via compile-time [LoggerMessage] partial methods:
| Event ID | Level | Event Name | Description |
|---|---|---|---|
1001 |
Debug |
SchemaNotRegistered |
Emitted when requested targetNamespace is missing from IXmlSchemaCache. |
1002 |
Debug |
ValidationFailed |
Emitted when document violates schema rules (contains line/column data). |
1003 |
Debug |
ValidationSucceeded |
Emitted when document passes all schema validation constraints. |
1004 |
Debug |
ValidationCancelled |
Emitted when validation loop is aborted via CancellationToken. |
1005 |
Debug |
MalformedXml |
Emitted on XML syntax errors or prohibited DTD/XXE injection attempts. |
EricksonLopez.Xml.Validation is verified trim-safe and Native AOT compatible:
<PropertyGroup>
<IsAotCompatible>true</IsAotCompatible>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
</PropertyGroup>- Zero Reflection on Hot Paths: Avoids dynamic code generation and unconstrained reflection.
- Source-Generated Logging: Utilizes compile-time logging delegates.
- AOT Verification: Continuously audited by the
tests/EricksonLopez.Xml.Validation.AotTestsmoke-test suite.
The EricksonLopez.Xml.Validation test harness is engineered to enforce absolute correctness, boundary safety, and regression resistance.
| Metric | Target | Initial State | Verified Final State | Status |
|---|---|---|---|---|
| Line Coverage | β₯ 100% | 100% | 100.00% (260/260 lines) | COMPLIANT |
| Branch Coverage | β₯ 100% | 100% | 100.00% (46/46 branches) | COMPLIANT |
| Method Coverage | β₯ 100% | 100% | 100.00% (24/24 methods) | COMPLIANT |
| Mutation Score | 100% | 96.58% | 100.00% (117 killed / 0 survived) | COMPLIANT |
The solution is divided into segregated testing projects targeting distinct quality concerns:
- Unit & Integration Tests (
tests/EricksonLopez.Xml.Validation.Tests): 193 tests executed across all 3 target frameworks (net8.0,net9.0,net10.0), totaling 579 test runs passing with 0 failures. Covers synchronous and asynchronous validation, multi-modal inputs, Anti-XXE enforcement, warning configurations, DoS limits, and DI lifecycles. - Architecture Tests (
tests/EricksonLopez.Xml.Validation.ArchitectureTests): 6 tests executed across all 3 target frameworks (18 test runs passing), validating design invariants usingNetArchTest.Rules(sealed public classes, absence of obsolete types, namespace boundaries, and interface contracts). - Native AOT Smoke Test (
tests/EricksonLopez.Xml.Validation.AotTest): Compiles and executes against standalone native binaries usingPublishAot=true.
Mutation testing is executed against the core validation engine to ensure test assertions are mathematically sensitive to state mutations:
{
"stryker-config": {
"thresholds": {
"high": 100,
"low": 98,
"break": 95
}
}
}Results: 100.00% Mutation Score (117 mutants killed, 0 mutants survived).
Environment: BenchmarkDotNet v0.15.8, Windows 11, AMD Ryzen 7 9800X3D 4.70GHz, .NET 10.0.11, X64 RyuJIT x86-64-v4
Measurements collected on validating an invoice payload against an enterprise XSD schema:
| Method | Mean | Error | StdDev | Ratio | Gen0 | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|
ValidateFromString |
3,029.05 ns | 447.52 ns | 24.53 ns | 1.000 | 1.5030 | 75.66 KB | 1.00 |
ValidateFromStream |
3,841.48 ns | 572.26 ns | 31.37 ns | 1.268 | 2.1591 | 108.51 KB | 1.43 |
ValidateFromSpan |
3,346.45 ns | 1,768.48 ns | 96.94 ns | 1.105 | 2.1591 | 108.59 KB | 1.44 |
CacheLookupContainsSchema |
6.31 ns | 0.44 ns | 0.02 ns | 0.002 | - | 0 B | 0.00 |
Execution time comparison over 1,000 iterations:
| Approach | Total Time | Average Time / Op | GC Allocations | Speedup |
|---|---|---|---|---|
Ad-Hoc BCL Compilation (new XmlSchemaSet().Compile()) |
~47 ms | 47 Β΅s | High (Schema graph + Gen 0/1 pressure) | Baseline |
Precompiled IXmlSchemaCache |
~9 ms | 9 Β΅s | Minimal (Validation state machine only) | 5x to 100x Faster |
Note
These figures are representative estimates (5xβ10x for simple schemas, up to 100x for complex enterprise schemas like UBL 2.1 or ISO 20022). They are not produced by XmlValidationBenchmarks.cs β that harness benchmarks input modalities against a pre-registered cache. To produce a reproducible ad-hoc vs. precompiled comparison, extend the harness with a [Benchmark] that calls new XmlSchemaSet().Add(...).Compile() before each validation. See performance-guide.md for details.
| Package | .NET 8.0 LTS | .NET 9.0 STS | .NET 10.0 | Native AOT | Trimmable | Notes |
|---|---|---|---|---|---|---|
EricksonLopez.Xml.Validation |
β Compatible | β Compatible | β Compatible | β Verified | β Verified | Zero reflection, <IsAotCompatible>true</IsAotCompatible> |
π‘οΈ Target Framework & Lifecycle Policy: First-class multi-targeting across
.NET 10(Modern LTS),.NET 9(STS), and.NET 8(Enterprise LTS) is actively maintained. Full backward compatibility is guaranteed until Microsoft officially reaches End-of-Life (EOL) for .NET 8 and .NET 9 in November 2026, at which milestone the ecosystem will transition to .NET 10 and .NET 11.
All failures produce structured Result<bool> error descriptors with standardized error codes:
| Error Code | Category | HTTP Status (RFC 9457) | Architectural Root Cause |
|---|---|---|---|
XmlValidation.SchemaNotRegistered |
ErrorType.NotFound |
404 Not Found |
The requested targetNamespace was not found in IXmlSchemaCache. |
XmlValidation.XmlMalformed |
ErrorType.Validation |
400 Bad Request |
Document is syntactically malformed, unclosed, or contains prohibited DTD/XXE structures. |
XmlValidation.SchemaViolation |
ErrorType.Validation |
422 Unprocessable Entity |
Document syntax is valid XML but violates elements, attributes, or type constraints of the XSD schema. |
The following flowchart details the decision pipeline and error branching executed on every validation invocation:
flowchart TD
Start(["Start: Validate / ValidateAsync Invocation"]) --> CheckCache{"Does targetNamespace exist\nin IXmlSchemaCache?"}
CheckCache -- "No" --> RetNotFound["Return Error.NotFound\n'XmlValidation.SchemaNotRegistered'\n(EventId 1001)"]
RetNotFound --> Done(["End"])
CheckCache -- "Yes" --> PrepSettings["Configure XmlReaderSettings:\nβ’ DtdProcessing = Prohibit (Anti-XXE)\nβ’ XmlResolver = null\nβ’ ValidationType = Schema\nβ’ Schemas = XmlSchemaSet\nβ’ MaxCharactersInDocument\nβ’ Attach ValidationEventHandler"]
PrepSettings --> CreateReader["Create XmlReader over Input\n(String / Stream / ReadOnlySpan<byte>)"]
CreateReader --> ReadLoop{"XmlReader.Read()\n/ ReadAsync()"}
ReadLoop -- "XmlException\n(Syntax error, DTD, or DoS limit)" --> RetMalformed["Return Error.Validation\n'XmlValidation.XmlMalformed'\n(EventId 1005)"]
RetMalformed --> Done
ReadLoop -- "First Element Node" --> CheckRoot{"IsRootElementDeclared\nin schemaCache?"}
CheckRoot -- "No" --> AddRootError["Append Error:\nRoot element not declared"]
CheckRoot -- "Yes" --> ContinueRead["Continue Streaming"]
AddRootError --> ReadLoop
ContinueRead --> ReadLoop
ReadLoop -- "ValidationEventHandler Error" --> AddError["Append Error with Line/Position\n(up to MaxErrors)"]
AddError --> ReadLoop
ReadLoop -- "ValidationEventHandler Warning" --> CheckWarnConfig{"options.IncludeWarnings?"}
CheckWarnConfig -- "Yes" --> AddWarn["Append Warning with '[Warning]' prefix"]
CheckWarnConfig -- "No" --> IgnoreWarn["Silently ignore warning"]
AddWarn --> ReadLoop
IgnoreWarn --> ReadLoop
ReadLoop -- "End of Document (EOF)" --> EvaluateErrors{"Errors > 0 OR\n(TreatWarningsAsErrors && Warnings > 0)?"}
EvaluateErrors -- "Yes" --> RetViolation["Return Error.Validation\n'XmlValidation.SchemaViolation'\n(EventId 1002)"]
EvaluateErrors -- "No" --> RetSuccess["Return Result<bool>.Success(true)\n(EventId 1003)"]
RetViolation --> Done
RetSuccess --> Done
stateDiagram-v2
[*] --> Received: Validate(input, ns)
Received --> CheckingCache: Query IXmlSchemaCache
CheckingCache --> SchemaNotRegistered: Schema not found in cache
SchemaNotRegistered --> [*]: Returns Error.NotFound
CheckingCache --> InitializingReader: Precompiled SchemaSet resolved
InitializingReader --> ParsingNodes: Anti-XXE XmlReaderSettings applied
state ParsingNodes {
[*] --> ReadingNode
ReadingNode --> AccumulatingErrors: XSD validation error event
AccumulatingErrors --> ReadingNode: Continue stream
ReadingNode --> EvaluatingWarning: XSD warning event
EvaluatingWarning --> AccumulatingWarnings: IncludeWarnings = true
EvaluatingWarning --> ReadingNode: IncludeWarnings = false
AccumulatingWarnings --> ReadingNode
ReadingNode --> Malformed: DTD or syntax violation
ReadingNode --> Cancelled: CancellationToken triggered
ReadingNode --> Completed: EOF reached
}
Malformed --> [*]: Returns Error.Validation (XmlMalformed)
Cancelled --> [*]: Throws OperationCanceledException
Completed --> EvaluatingResult
EvaluatingResult --> ValidationFailed: Errors > 0 or (TreatWarningsAsErrors && Warnings > 0)
EvaluatingResult --> ValidationSucceeded: No blocking errors or warnings
ValidationFailed --> [*]: Returns Error.Validation (SchemaViolation)
ValidationSucceeded --> [*]: Returns Result.Success(true)
flowchart LR
UntrustedPayload["Untrusted XML Payload\n(WebHook / HTTP Body / Queue Buffer)"] --> Firewall["Perimeter Defensive Gateway\n(IXmlSchemaValidator)"]
Firewall -- "XXE Attack / DTD Prohibited" --> RejectXXE["400 Bad Request\nImmediately Neutralized\nZero Entity Expansion"]
Firewall -- "XSD Schema Violation" --> RejectSchema["422 Unprocessable Entity\nRejected with Line/Position"]
Firewall -- "Compliant & Safe" --> SafeParser["Secure Typed Deserializer\n(Domain Command / Entity)"]
SafeParser --> DomainCore["Domain Core / CQRS Handlers\n(Guaranteed Valid and XXE-Free XML)"]
| Scenario | β Avoid | β Recommended |
|---|---|---|
| Security Defaults | Configuring DtdProcessing.Parse or assigning non-null XmlResolver |
Relying on XmlSchemaValidator defaults (DtdProcessing.Prohibit, XmlResolver = null) |
| Control Flow | Throwing and catching XmlException or XmlSchemaValidationException |
Evaluating functional Result<bool> returns without exceptions |
| Schema Compilation | Calling new XmlSchemaSet().Compile() on incoming HTTP requests |
Preloading schemas into IXmlSchemaCache as a singleton at startup |
| Memory Allocation | Converting raw byte[] buffers into string prior to validation |
Validating directly over ReadOnlySpan<byte> or streaming input |
| Stream Management | Disposing or buffering the entire Stream before validation completes |
Passing open Stream instances directly; validator preserves stream ownership |
| Diagnostic Logging | String interpolation in log statements (logger.LogInformation($"...")) |
Utilizing source-generated [LoggerMessage] structured events |
| Warning Handling | Silently ignoring XSD schema warnings in regulatory scenarios | Configuring options.IncludeWarnings = true and options.TreatWarningsAsErrors = true |
Caution
The default security configuration intentionally rejects all DTD declarations and external references. Attempting to pass documents containing <!DOCTYPE ...> will result in immediate validation failure.
- Symptom: Validation returns
Error.NotFoundwith codeXmlValidation.SchemaNotRegistered. - Root Cause: The
targetNamespacepassed tovalidator.Validate(...)does not match the namespace registered inIXmlSchemaCache. - Resolution: Verify exact URI string casing. Preload the schema during application startup using
cache.RegisterSchema("https://exact-uri", xsdContent).
- Symptom: Payload fails validation with description indicating DTD processing is prohibited.
- Root Cause: The input document contains a
<!DOCTYPE ...>declaration, which is prohibited to eliminate XXE vulnerabilities (CWE-611). - Resolution: Strip all DTD declarations from the XML before sending it to the validator, or verify that the sending client is compliant with modern pure-XSD standards.
- Symptom:
Validate(stream, ...)fails with unexpected end of file or malformed XML. - Root Cause: Upstream middleware or deserializers read the
Streamto positionLengthwithout rewinding. - Resolution: Reset the stream position (
stream.Position = 0;) before callingvalidator.Validate(...)orvalidator.ValidateAsync(...).
- Symptom: Documents with schema warnings return
Result.IsSuccess == true. - Root Cause: By default,
IncludeWarningsisfalseandTreatWarningsAsErrorsisfalse. - Resolution: Set
options.IncludeWarnings = true;andoptions.TreatWarningsAsErrors = true;when registering viaservices.AddXmlValidation(options => ...).
EricksonLopez.Xml.Validation is an integral component of the EricksonLopez enterprise foundation library ecosystem:
- β‘ EricksonLopez.Result β High-performance, struct-based Result pattern and Railway-Oriented Programming ecosystem.
- π§± EricksonLopez.SharedKernel β Foundational enterprise domain primitives, value objects, specifications, and domain events.
- π EricksonLopez.Security β Enterprise zero-trust security framework, cryptographic primitives, PKI, and authentication.
- π EricksonLopez.Idempotency β Enterprise distributed idempotency engine with database-backed coordination.
- π’ EricksonLopez.MultiTenancy β Multi-tenant isolation architecture and PostgreSQL Row-Level Security integration.
- β±οΈ EricksonLopez.Concurrency β Zero-allocation optimistic concurrency control and version-checked state transitions.
- π³ EricksonLopez.Transaction β Resilient database transaction orchestration and execution strategy manager.
- π‘ EricksonLopez.Mediator β Zero-allocation, struct-based mediator and in-process messaging pipeline.
- π EricksonLopez.Specification β Composable AOT-first specification pattern.
Contributions, issues, and feature requests are welcome! Please review our community guidelines before submitting pull requests:
- .NET 8.0 SDK, .NET 9.0 SDK, and .NET 10.0 SDK
- Visual Studio 2022 (v17.12+) or JetBrains Rider (2024.3+)
# Clone the repository
git clone https://github.com/ericksonlopezf/dotnet-xml.git
cd dotnet-xml
# Restore dependencies
dotnet restore
# Build with deterministic warnings-as-errors enforcement
dotnet build --configuration Release
# Execute comprehensive unit, integration, and architecture tests
dotnet test --configuration Release
# Execute Native AOT verification smoke test
dotnet run --project tests/EricksonLopez.Xml.Validation.AotTest/EricksonLopez.Xml.Validation.AotTest.csproj --configuration Release
# Run mutation testing with Stryker.NET
dotnet stryker --config-file stryker-config.json- Contributing Guidelines β Coding conventions, Git workflows, and commit standards.
- Code of Conduct β Contributor Covenant v2.1 standards.
- Security Policy β Responsible vulnerability disclosure guidelines.
- Support Policy β Getting help, feature requests, and bug reporting.
Distributed under the MIT License. Copyright Β© 2026 Erickson Lopez.