Ignixa.FhirMappingLanguage 0.0.127

dotnet add package Ignixa.FhirMappingLanguage --version 0.0.127
                    
NuGet\Install-Package Ignixa.FhirMappingLanguage -Version 0.0.127
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Ignixa.FhirMappingLanguage" Version="0.0.127" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Ignixa.FhirMappingLanguage" Version="0.0.127" />
                    
Directory.Packages.props
<PackageReference Include="Ignixa.FhirMappingLanguage" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Ignixa.FhirMappingLanguage --version 0.0.127
                    
#r "nuget: Ignixa.FhirMappingLanguage, 0.0.127"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Ignixa.FhirMappingLanguage@0.0.127
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Ignixa.FhirMappingLanguage&version=0.0.127
                    
Install as a Cake Addin
#tool nuget:?package=Ignixa.FhirMappingLanguage&version=0.0.127
                    
Install as a Cake Tool

Ignixa.FhirMappingLanguage

A .NET implementation of the FHIR Mapping Language (FML). This library provides parsing, compilation, and execution of FHIR mapping expressions to transform FHIR resources.

Features

Supported Language Constructs

  • Map declarations: Top-level map structure with URL and identifier
  • Uses declarations: Declare source and target structure definitions
  • Imports: Import other mapping definitions
  • Groups: Logical grouping of transformation rules with parameters
  • Rules: Transformation rules with sources, targets, and dependencies
  • Sources: Input data patterns with conditions, checks, and logging
  • Targets: Output data patterns with transforms and list modes
  • Transforms: Built-in and custom transformation functions
  • Embedded FHIRPath: Conditions and expressions using FHIRPath syntax

Lexer Features

  • Keywords: map, uses, as, alias, imports, group, extends, default, where, check, log, then, source, target, etc.
  • Operators: =, ->, ::, ., ,, ;
  • Literals: Strings, numbers, booleans, URLs
  • Comments: Line comments (//) and block comments (/* */)
  • Trivia mode: Preserve whitespace and comments for round-tripping

Usage

Basic Example

using Ignixa.FhirMappingLanguage;
using Ignixa.FhirMappingLanguage.Evaluation;
using Ignixa.FhirMappingLanguage.Parser;

// Parse a mapping
var parser = new MappingParser();
var mapping = @"
map 'http://example.org/fhir/StructureMap/PatientTransform' = 'PatientTransform'

uses 'http://hl7.org/fhir/StructureDefinition/Patient' alias Patient as source
uses 'http://hl7.org/fhir/StructureDefinition/Bundle' alias Bundle as target

group PatientToBundle(source src : Patient, target bundle : Bundle) {
  src.name as vn -> bundle.entry as entry;
}
";

var map = parser.Parse(mapping);

// Create evaluator with security options
var options = MappingEvaluatorOptions.Default; // Recommended security settings
var evaluator = new MappingEvaluator(options);

// Create evaluation context
var context = new MappingContext();
context.SetSource("src", sourcePatient);
context.SetTarget("bundle", targetBundle);

// Execute the mapping
evaluator.Execute(map, context);

Compiled Mapping

// Compile once, execute many times
var parser = new MappingParser();
var compiled = parser.Compile(mappingText);

// Set sources and targets
compiled.Context.SetSource("src", sourcePatient);
compiled.Context.SetTarget("bundle", targetBundle);

// Execute the first (default) group
compiled.Execute();

// Or execute a specific group by name
compiled.ExecuteGroup("PatientToBundle");

Working with FHIRPath Expressions

FHIRPath support is built-in and enabled by default. The evaluator automatically uses Ignixa.FhirPath for all embedded FHIRPath expressions like where, check, and log clauses.

// FHIRPath expressions work out of the box - no configuration needed!
var mapping = @"
map 'http://example.org/map' = 'Example'
group Transform(source src : Patient, target tgt : Bundle) {
  src.name where (use = 'official') -> tgt.entry;
  src.identifier check (system.exists()) -> tgt.id;
}
";

var parser = new MappingParser();
var map = parser.Parse(mapping);
var evaluator = new MappingEvaluator(); // FHIRPath enabled by default
evaluator.Execute(map, context);

Custom FHIRPath Evaluator (optional - only if you need custom behavior):

// Disable built-in integration and provide your own
var evaluator = new MappingEvaluator(enableFhirPath: false);

context.FhirPathEvaluator = (expression, element) =>
{
    // Your custom FHIRPath evaluation logic
    return MyCustomFhirPathEvaluator(expression, element);
};

Custom Transform Functions

// Register custom transform functions
context.TransformResolver = (functionName, arguments) =>
{
    return functionName switch
    {
        "create" => CreateResource(arguments[0].ToString()!),
        "translate" => TranslateCode(arguments),
        "dateOp" => ParseDate(arguments[0].ToString()!),
        _ => throw new NotSupportedException($"Transform '{functionName}' not supported")
    };
};

Security and Error Handling

Security Configuration

MappingEvaluatorOptions provides comprehensive security controls to prevent resource exhaustion attacks:

var options = new MappingEvaluatorOptions
{
    // Resource limits
    MaxRecursionDepth = 50,              // Prevent infinite recursion
    MaxElementsCreated = 100_000,        // Prevent memory exhaustion
    MaxMapSizeBytes = 50_000_000,        // 50 MB max map size
    MaxInputResourceSizeBytes = 10_000_000, // 10 MB max input size

    // Timeouts
    TransformTimeout = TimeSpan.FromSeconds(30),
    FhirPathTimeout = TimeSpan.FromSeconds(5),

    // Import security
    AllowFileSystemImports = false,      // Disable file:// imports by default
    AllowedImportDomains = { "hl7.org", "fhir.org" },

    // ConceptMap security
    AllowedConceptMapTargetSystems = { "http://snomed.info/sct", "http://loinc.org" },
    MaxCodeLength = 100,

    // Error handling
    ErrorMode = ErrorMode.Strict,        // Fail fast on errors
    MaxErrorsCollected = 100             // Limit errors in Lenient mode
};

var evaluator = new MappingEvaluator(options);

Default Configuration: MappingEvaluatorOptions.Default provides recommended security settings suitable for production use.

Error Handling Modes

The library supports two error handling modes:

Strict Mode (Default)

Throws MappingExecutionException on the first error:

var context = new MappingContext
{
    ErrorMode = ErrorMode.Strict
};

try
{
    evaluator.Execute(map, context);
}
catch (MappingExecutionException ex)
{
    Console.WriteLine($"Mapping failed: {ex.Message}");
}
Lenient Mode

Collects all errors and continues execution where possible:

var options = new MappingEvaluatorOptions
{
    ErrorMode = ErrorMode.Lenient,
    MaxErrorsCollected = 100  // Throw if too many errors
};

var context = new MappingContext
{
    ErrorMode = ErrorMode.Lenient
};

evaluator.Execute(map, context);

// Check for errors after execution
if (context.Errors.Any())
{
    foreach (var error in context.Errors)
    {
        Console.WriteLine($"Rule '{error.RuleName}': {error.Message}");
        Console.WriteLine($"Location: {error.Location}");
        Console.WriteLine($"Path: {error.ElementPath}");

        if (error.AvailableElements != null)
        {
            Console.WriteLine($"Available elements: {string.Join(", ", error.AvailableElements)}");
        }
    }
}

Enhanced Error Messages

Error messages include rich contextual information:

  • RuleName: Name of the failing rule
  • GroupName: Name of the group containing the rule
  • RuleIndex: Index of the rule within the group
  • ElementPath: Path to the element that caused the error (e.g., src.name.family)
  • AvailableElements: List of valid child elements when accessing non-existent elements

Example error output:

Rule 'copyName' in group 'PatientTransform' at index 0
Source element 'middleName' not found (cardinality 1..1 requires at least 1)
Path: Patient.name.middleName
Available elements: family, given, prefix, suffix, use
Location: StructureMap.group[PatientTransform].rule[0]

Cardinality Constraints

Source expressions support cardinality constraints to validate element counts:

// Require exactly one element
src.identifier : Identifier 1..1 -> tgt.id;

// Require at least one element
src.name : HumanName 1..* -> tgt.entry;

// Allow zero or one element
src.telecom : ContactPoint 0..1 -> tgt.contact;

// Allow any number of elements (default)
src.address : Address 0..* -> tgt.addresses;

When cardinality constraints are violated, the evaluator:

  • Strict mode: Throws MappingExecutionException immediately
  • Lenient mode: Collects error and continues with next rule

FHIR Mapping Language Syntax

Map Structure

map "url" = "identifier"
[uses "structure-url" alias Name as source|target|queried|produced]*
[imports "map-url"]*
[group definitions]*

Group Definition

group Name(source src : Type, target tgt : Type) [extends OtherGroup] {
  [rules]*
}

Rule Syntax

[name:] source [, source]* [-> target [, target]*] [then { [rule]* }];

Source Syntax

context [as variable] [: type] [cardinality] [where (condition)] [check (condition)] [log (message)] [default expression]

Where cardinality is optional and follows the pattern min..max (e.g., 1..1, 0..*, 1..*)

Target Syntax

[context] [as variable] [= transform] [list-mode]

Transform Functions

Common transform functions include:

  • create(type) - Create a new resource of the specified type
  • translate(source, map, code) - Translate codes using a ConceptMap
  • truncate(string, length) - Truncate a string
  • dateOp(value) - Parse and convert dates

Implementation Notes

Similar to FhirPath Library

This library follows the exact same architectural patterns as Ignixa.FhirPath:

  1. Lexer/Tokenizer: Uses Superpower for high-performance tokenization
  2. Expression Tree: Strongly-typed AST classes for type-safe manipulation
  3. Parser Grammar: Declarative parser using Superpower's parser combinators
  4. Evaluator: Visitor pattern for executing the AST

Dependencies

  • Superpower: Parser combinator library for lexing and parsing
  • Ignixa.Abstractions: Core abstractions
  • Ignixa.FhirPath: FHIRPath evaluation for embedded expressions
  • Ignixa.Serialization: FHIR Serialization and Deserialization

Testing

The library includes comprehensive unit tests in Ignixa.FhirMappingLanguage.Tests:

dotnet test test/Ignixa.FhirMappingLanguage.Tests/

Specification Compliance

This implementation is based on the FHIR Mapping Language specification:

License

MIT License - see LICENSE file in repository root

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.127 45 12/29/2025
0.0.109 260 12/18/2025
0.0.101 258 12/16/2025
0.0.96 409 12/10/2025
0.0.87 411 12/8/2025
0.0.70 291 12/7/2025
0.0.68 207 12/7/2025
0.0.62 218 12/6/2025
0.0.59 162 12/5/2025
0.0.58 183 12/5/2025
0.0.57 186 12/3/2025
0.0.56 650 12/3/2025
0.0.55 413 12/1/2025
0.0.54 405 11/30/2025
0.0.53 405 11/30/2025
0.0.51 114 11/29/2025
0.0.50 97 11/29/2025