XferLang
Introduction
XferLang is a typed, delimiter‑driven data format for data transfer, configuration, and structured content. If you know JSON, you'll feel at home using XferLang, but then you'll begin to notice what's different: explicit types, interpolated strings, no escape sequences, bound data references, and extensible processing instructions.
Getting Started
Why XferLang
- XferLang is readable without ceremony. Whitespace separates elements, and you may expand or collapse formatting without changing meaning.
- Types are explicit. There are no heuristics or guesswork.
- There is no escape tax. When content would collide with a delimiter, you may lengthen the delimiter instead of inserting backslashes.
- Parsing is programmable. Processing instructions declare metadata, bind names to elements for dynamic insertion, control parsing output, and compose elements from external sources. You may even develop your own custom processing instructions or extend the built-in ones.
Quick XferLang Example
<! document { version "1" } !>
{
title "Demo"
active ~true
retries 3
ratio *0.8125
launched @2025-08-01T09:30:00Z@
tags [ "alpha" "preview" ]
location ( *42.3601 *-71.0589 )
banner 'User=<|USER|> ok=<~true~>'
}
Visual Studio Code Extension
Install the XferLang extension from the Visual Studio Marketplace to enable syntax highlighting and basic diagnostics.
Marketplace: https://marketplace.visualstudio.com/items?itemName=paulmooreparks.xferlang
Open a file with a .xfer file extension to activate the XferLang extension.
Project Status and Roadmap
The .NET implementation of XferLang is becoming more robust, with a focus on professional-grade features like custom converters and contract resolvers. However, the project as a whole is still experimental.
The future roadmap includes:
- Completing the .NET implementation to achieve a production-quality 1.0 release
- Reimplementing the core library in Rust
- Exposing a C ABI from the Rust implementation
- Creating language wrappers (e.g., for C#, Python, JavaScript) that communicate with the C ABI
The goal of moving to a Rust core is to provide a single, high-performance, and memory-safe core for all future XferLang implementations. I'm looking for contributors and collaborators to get that work started.
XferLang Basics
Elements and Collections
{
name "Alice"
age 30
isMember ~true
scores [ *85 *90 *78.5 ]
profile {
email "alice@example.com"
joinedDate @2023-01-15T12:00:00@
}
point (*42.3601 *-71.0589)
optional ?
}
Objects ({}
) hold key/value pairs. Arrays ([]
) are ordered and homogeneous (each item must be the same element type). Tuples (()
) are ordered and heterogeneous (may contain multiple element types).
Specifiers and the "No Escapes" Rule
Instead of using escape sequences, you may lengthen the opening and closing specifier runs in order to enclose problematic content. The parser treats the contiguous run as the delimiter.
- Strings use
"…"
, and a longer run allows embedded quotes:""He said, \"Hello\".""
. - Interpolated text uses
'…'
, and a longer run allows embedded apostrophes:''Outer 'inner' still fine''
. - Comments use
</ … />
, and a longer run allows nested markers:<// contains </ safely //>
. - Use an explicit form by wrapping with
<…>
when you need to isolate internals.
{
nestedQuotes ""He said, "Hello" then left.""
dynamicWithPipe ||status|ok||
<// Outer comments containing </ inner comments /> are fine //>
explicitDate <@2025-08-01T12:00:00@>
}
Pick the shortest run that avoids ambiguity.
Interpolated Text
Interpolated‑text elements evaluate embedded elements to render a final text element. Like string elements, these will deserialize to a string type.
<! dynamicSource { username env "USER" } !>
</ Value will render as 'User=paul LoggedIn=True Since 1 Aug 2025 09:30:00' />
banner 'User=<|username|> LoggedIn=<~true~> Since <@2025-08-01T09:30:00@>'
Processing Instructions
Processing instructions (PIs) are single key/value directives that the parser consumes before continuing to parse. They use the form <! name <value> !>
. The built‑in PIs include:
document
– document metadatadynamicSource
– map names to source handlers (file/env/const)let
– bind a name to a valuescript
– batch multiple operators and apply them before the next elementif
– conditionally suppress the next elementchardef
– define character aliasesid
- assign a unique identifier to the following elementtag
– assign a categorization tag to the following element
<! document { version "1.2" env "prod" } !>
<! dynamicSource { apiKey file "secrets/api-key.txt" user env "USER" tag const "2025.08.11" } !>
<! if defined |apiKey| !> { auth { key |apiKey| user |user| tag |tag| } }
Binding References with let
You may bind names to element values with the let
processing instruction, and you may reference them in your document as reference elements.
<! let greeting "world" !>
message 'Hello <_greeting_>' </ <_greeting_> is replaced with "world" at parse time />
tuple ( _greeting _greeting ) </ Renders as tuple ("world" "world") />
If a reference cannot be resolved at parse time, the reference element is rendered as-is and the parser reports a warning.
Grouped Let Bindings with script
The script
PI groups multiple let
operators together.
<! script (
let x "Hello"
let y 'X=<_x_>'
) !>
(
_x </ Renders as "Hello" />
_y </ Renders as "X=Hello" />
)
In the future, additional keywords will be available for scripting as well.
Dynamic Elements and Sources
Dynamic elements (|name|
) resolve from sources configured by dynamicSource
or, absent a mapping, from environment variables. The built‑in source types are env
to read environment variables, file
to read files, and const
for constant values.
<! dynamicSource {
apiKey file "secrets/api-key.txt"
user env "USER"
build const "2025.08.11"
} !>
{ key '|apiKey|' user '|user|' tag '|build|' }
Conditional Element Inclusion with if
The if
PI evaluates a condition. If the condition is false, the following sibling element is not added to the document. The if
processing instruction itself is always stripped from output regardless of evaluation outcome.
<! let showDebug ~false !>
<! if _showDebug !>
{ debug { level "verbose" } }
Defined vs undefined (existence test) may also be used:
<! if defined _showDebug !> { note 'Evaluates as true even if the value is ~false.' }
Behavior & serialization rules:
- If the condition evaluates to false, the target element (the immediately following sibling element) is completely suppressed; that is, it is never added to the parsed document model.
- If the condition evaluates to true, the target element is retained.
- Regardless of outcome (true, false, evaluation error, or unknown operator name) the
if
processing instruction itself is always stripped from serialization output. It acts only as a directive at parse time. - An unknown operator name inside the condition currently acts as a no‑op (treated as truthy so the element is preserved) but the PI is still stripped. Future versions may surface a warning; do not rely on serialization visibility for diagnostics.
- Direct reference conditions (
<! if _flag !>
) test the bound value's truthiness; usedefined
to distinguish between an undefined binding and a defined but falsy value.
Example showing outcomes (serialized form shown on right):
<! let enabled ~true !> <! if _enabled !> { feature { status "on" } } </ serializes as: { feature { status "on" } } />
<! let enabled ~false !> <! if _enabled !> { feature { status "on" } } </ serializes as: (nothing emitted) />
<! if someUnknownOp["a" "b"] !> { kept ~true } </ unknown op -> element kept; PI stripped />
Character Definitions
Define symbolic character aliases for readability (keyword → Unicode code point):
<! chardef { bullet \$2022 arrow \$2192 } !>
{ list ("Item" \bullet "Next" \arrow ) }
Introduction to the .NET Library
Install the NuGet package ParksComputing.Xfer.Lang to parse and serialize XferLang in .NET projects.
Parse and Serialize
using ParksComputing.Xfer.Lang.Services;
var parser = new Parser();
var doc = parser.Parse("{ name \"Alice\" age 30 }");
// Work with the document
var roundTrip = doc.ToXfer(); // Serialize back to XferLang
Warnings include row/column anchors to help locate issues:
foreach (var w in doc.Warnings) {
Console.WriteLine($"{w.Type} @ {w.Row}:{w.Column} — {w.Message} [{w.Context}]");
}
Object Mapping (XferConvert)
XferConvert
turns CLR objects into Xfer elements and back. This is useful for configuration scenarios and typed round‑trips.
using ParksComputing.Xfer.Lang;
using ParksComputing.Xfer.Lang.Configuration;
var settings = new XferSerializerSettings();
var person = new Person { Name = "Ada", Age = 36 };
var element = XferConvert.FromObject(person, settings); // ObjectElement
var xfer = element.ToXfer();
var back = XferConvert.ToObject<Person>((ObjectElement)element, settings);
Attributes influence names and number formatting:
using ParksComputing.Xfer.Lang.Attributes;
public class Person {
[XferProperty("fullName")] public string Name { get; set; } = string.Empty;
[XferNumericFormat(XferNumericFormat.Hex)] public int Favorite { get; set; }
}
Configuration (XferSerializerSettings)
XferSerializerSettings
controls naming, numeric formatting, decimal precision, and extension points. Highlights:
- ContractResolver (default:
DefaultContractResolver
) - Converters (
IXferConverter
) — optional custom type converters - Decimal and double precision (
XferDecimalPrecisionAttribute
) - Integer/long formatting (
XferNumericFormatAttribute
)
using ParksComputing.Xfer.Lang.Configuration;
using ParksComputing.Xfer.Lang.ContractResolvers;
using ParksComputing.Xfer.Lang.Converters;
var settings = new XferSerializerSettings {
ContractResolver = new DefaultContractResolver()
};
// Optional: add custom converters when you need specialized handling
settings.Converters.Add(new MySpecialConverter());
Note: Advanced serializer extension points (custom converters and custom contract resolvers) are supported and evolving. Keep tests nearby when extending.
Core Concepts
Elements and Syntax Forms
Every value is an element. Most elements support three styles:
- Implicit: minimal form (e.g.,
42
) when unambiguous. - Compact: type delimiter(s) (e.g.,
#42
,"text"
). - Explicit: wrapped in angle brackets (e.g.,
<#42#>
,<"A quote: "">
) allowing delimiter repetition.
Collections
- Object:
{ key value ... }
(unordered key/value pairs; keys are keywords) - Array:
[ value value ... ]
(homogeneous) - Tuple:
( value value ... )
(heterogeneous)
Keywords vs Identifiers
- Keywords (object keys) are implicit barewords composed only of letters, digits, and underscore:
[A-Za-z_][A-Za-z0-9_]*
. - If a key needs any other character (dash, space, etc.) wrap it with leading & trailing
=
specifier runs:=first-name=
(lengthen the run if it appears within the key itself). - Identifiers (
:name:
) are value elements (never keys). They always use leading and trailing:
.
Numbers
Integers (#
or implicit), longs (&
), decimals (*
high precision), doubles (^
). Alternate bases for integers/longs: hex $
, binary %
.
Text and Characters
Strings use quotation marks, for example "..."
. To include the delimiter itself, repeat it or switch to the explicit form <"...">
. Characters may be written using decimal (\\65
), hexadecimal (\\$41
), binary (\\%01000001
), or predefined character keywords such as \\tab
.
Date and Time
Date and time values use the @...@
form with ISO‑8601 formats. Where implemented, date‑only and time‑only forms may also be parsed.
Null
The ?
element represents a null value.
Binding and Reference
<! let name <value> !>
binds name
to <value>
. Inside subsequent elements the value may be referenced with _name
(or inside interpolation as <_name_>
).
Batching bindings: The script
processing instruction currently supports only the let
operator. (Additional operators will be added prior to general release.) You can group several sequential let
bindings inside a single tuple so they all execute before the next element parses:
<! script ( let first "Alice" let greeting 'Hi <_first_>' let answer 42 ) !>
{ message _greeting number _answer }
All listed let
bindings are evaluated in order; later bindings can reference earlier ones (as with _first
inside the interpolated greeting) but self‑reference is prevented. Because the script PI here contains only let
bindings it does not serialize into the output (it is suppressed after execution).
Interpolated Text
Interpolated text is delimited by apostrophes, for example 'Hello <_name_>'
. Embedded elements inside must use explicit forms. The expressions are structural replacements, so no character escaping is required.
Dynamic Elements
|identifier|
resolves through the configured dynamic source resolver (e.g., environment). Content is a single identifier; nested elements are not allowed inside the delimiters in the current implementation.
Processing Instructions (PIs)
Processing instructions have a compact form ! name <value> !
and an explicit form <! name <value> !>
. Each PI consists of exactly one key/value pair, where the value may be any element. Some PIs introduce bindings or affect subsequent parsing and may be suppressed from serialization after execution.
XferLang Language Specification
Document Structure
The document order is as follows:
- Zero or more processing instructions.
- Exactly one root collection element (Object, Array, or Tuple).
Comments (</ ... />
) may appear anywhere and are ignored by the parser.
Element Syntax Variations
XferLang elements have a flexible syntax with up to three variations. This allows you to choose the most readable and concise form for your data, only using more verbose syntax when necessary to resolve ambiguity.
Implicit Syntax
For integers and keywords, no special characters are needed when the context is unambiguous.
123 </ An integer />
name "Alice" </ A key/value pair with implicit keyword 'name' and a string value />
Compact Syntax
Most elements use either a single specifier character or an enclosing pair of specifiers to denote the type. This is the most common syntax. Keywords containing whitespace or other special characters require the =
specifier, while identifiers require the :
specifier.
(
~true </ A Boolean value />
*123.45 </ A decimal value />
"Hello, World!" </ A string />
[ #1 #2 #3 ] </ An array of integers />
=special keyword= #42 </ A keyword with an embedded space />
)
Enclosing specifier characters may be repeated as many times as necessary to enable an element to contain that same specifier character.
( ""This string contains a " character with impunity."" )
Explicit Syntax
When an element's content might be ambiguous (e.g., a string containing a quote), you may wrap the compact form in angle brackets (<
and >
). This is the most verbose but also the most powerful form, as it affords the most flexibility for containing special characters.
<(
<"Alice said, "Boo!"">
<// A comment containing </another comment/> //>
<=object description=> <"This tuple is inside <()> delimiters.">
)>
XferLang Element Reference
XferLang supports a rich set of data types designed for clarity, explicitness, and flexibility. Each type is chosen to represent a distinct kind of value, making data both human-readable and machine-precise.
Primitive Types
String Element
A string element contains text data. The content is stored verbatim. To include a "
character that would conflict with the closing delimiter, repeat the specifier (e.g., ""...""
) or use explicit syntax (<"..."">
).
Specifier:
"
(Quotation Mark)Syntax:
- Compact:
"Hello, World!"
- Explicit:
<"Alice said, "Boo!"">
- Compact:
Examples:
</ Compact syntax /> message "Hello, World!" </ Explicit syntax with quotes /> quote <"Alice said, "Boo!""> </ Compact syntax with delimiter repetition /> description ""A quote is a " character.""
Character Element
A character element represents a single UTF-8 character, specified by its codepoint in decimal, hex ($
), or binary (%
), or by a predefined keyword (e.g., tab
, lf
, gt
).
- Specifier:
\
(Backslash) - Syntax:
- Compact:
\65
,\$41
,\%01000001
,\gt
- Explicit:
<\65\>
,<\$41\>
,<\gt\>
- Compact:
- Examples:
</ Compact syntax - decimal codepoint /> letterA \65 </ Compact syntax - hex codepoint /> letterB \$42 </ Compact syntax - binary codepoint /> letterC \%01000011 </ Compact syntax - keyword /> tabChar \tab newlineChar \lf </ Explicit syntax /> specialChar <\$2665\> </ Heart symbol />
Integer Element
An integer element represents a 32-bit signed integer. The value may be written in decimal, hex ($
), or binary (%
). The specifier is optional if the value is unambiguous (implicit syntax).
- Specifier:
#
(Number Sign) - Syntax:
- Implicit:
42
,-123
- Compact:
#42
,#$2A
,#%-123
,#%00101010
- Explicit:
<#42#>
,<#$2A#>
,<#%00101010#>
- Implicit:
- Range: -2,147,483,648 to 2,147,483,647
- Examples:
</ Implicit syntax (most common) /> age 30 count 1000 negative -42 </ Compact syntax /> port #8080 timeout #30 colorRed #$FF permissions #%11110000 </ Explicit syntax /> maxValue <#2147483647#> hexValue <#$DEADBEEF#> binaryFlags <#%11110000#>
Long Element
A long element represents a 64-bit signed integer. The value may be written in decimal, hex ($
), or binary (%
).
- Specifier:
&
(Ampersand) - Syntax:
- Compact:
&5000000000
,&$12A05F200
,&%1001010100000010111110010000000000
- Explicit:
<&5000000000&>
,<&$12A05F200&>
,<&%1001010100000010111110010000000000&>
- Compact:
- Range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- Examples:
</ Compact syntax /> population &7800000000 fileSize &5368709120 timestamp &1672531200000 </ Compact syntax - hexadecimal /> userId &$1A2B3C4D5E6F memoryOffset &$7FF6C2E40000 </ Compact syntax - binary /> featureFlags &%1111000011110000111100001111 </ Explicit syntax /> maxLong <&9223372036854775807&> hexAddress <&$7FFFFFFFFFFFFFFF&> binaryMask <&%1111111111111111111111111111111111111111111111111111111111111111&>
Double Element
A double element represents a 64-bit floating-point number.
- Specifier:
^
(Caret) - Syntax:
- Compact:
^3.14159
,^-2.5
- Explicit:
<^3.14159^>
,<^-2.5^>
- Compact:
- Examples:
</ Compact syntax /> pi ^3.14159 temperature ^-2.5 radius ^12.75 </ Explicit syntax /> preciseValue <^3.141592653589793^> measurement <^123.456789^> ratio <^0.618033988749^>
Decimal Element
A decimal element represents a high-precision decimal value.
- Specifier:
*
(Asterisk) - Syntax:
- Compact:
*123.45
,*-456.789
,*0.000001
- Explicit:
<*123.45*>
,<*-456.789*>
,<*0.000001*>
- Compact:
- Examples:
</ Compact syntax /> price *123.45 balance *-456.789 precision *0.000001 </ Explicit syntax /> currency <*1234567.89*> percentage <*99.999*> calculation <*0.123456789012345*>
Boolean Element
A Boolean element represents a true
or false
value.
- Specifier:
~
(Tilde) Description: - Syntax:
- Compact:
~true
,~false
- Explicit:
<~true~>
,<~false~>
- Compact:
- Examples:
</ Compact syntax /> isActive ~true isDeleted ~false hasPermission ~true </ Explicit syntax /> confirmed <~true~> disabled <~false~> verified <~true~>
Date/Time Element
A date/time element represents a date and time value in ISO 8601 format.
- Specifier:
@
(At Sign) - Syntax:
- Compact:
@2025-07-23T10:00:00@
,@2023-12-25@
,@2023-01-01T00:00:00Z@
- Explicit:
<@2025-07-23T10:00:00@>
,<@2023-12-25@>
,<@2023-01-01T00:00:00Z@>
- Compact:
- Examples:
</ Compact syntax /> created @2023-12-01T10:30:00@ birthDate @1990-05-15@ lastLogin @2023-12-25T09:30:00Z@ </ Explicit syntax /> timestamp <@2025-07-23T10:00:00@> scheduledDate <@2024-01-01T00:00:00Z@> eventTime <@2023-12-31T23:59:59.999@>
Null Element
A null element represents a null value.
- Specifier:
?
(Question Mark) - Syntax:
- Compact:
?
- Explicit:
<??>
- Compact:
- Examples:
</ Compact syntax /> optionalValue ? middleName ? description ? </ Explicit syntax /> nullField <??> missingInfo <??>
Hexadecimal and Binary Formatting
Integer and Long elements support alternative numeric representations for improved readability in specific contexts:
Hexadecimal Format
- Syntax:
$
prefix followed by hexadecimal digits (e.g.,#$2A
,&$12A05F200
) - Use Cases: Memory addresses, color values, bitmasks, low-level programming
- Parsing: Case-insensitive (
#$2A
equals#$2a
) - Attributes: Use
[XferNumericFormat(XferNumericFormat.Hexadecimal, MinDigits = 4)]
for zero-padding
Binary Format
- Syntax:
%
prefix followed by binary digits (e.g.,#%101010
,&%1001010100000010111110010000000000
) - Use Cases: Bit manipulation, feature flags
- Attributes: Use
[XferNumericFormat(XferNumericFormat.Binary, MinBits = 8)]
for zero-padding
Examples:
{
</ Decimal integer 42 in different formats />
decimal 42
hex #$2A
binary #%101010
padded_hex #$002A
padded_binary #%00101010
}
Hexadecimal and binary formatting are only supported for character, integer, and long element types. Decimal and double types preserve fractional precision and do not support these formats.
Collection Elements
Object Element
An object is a collection of key/value pairs. Keys are keyword elements, and values may be any XferLang element.
- Specifiers:
{
and}
(Curly Brackets) - Syntax:
- Compact:
{ name "Alice" age 30 }
- Explicit:
<{ name "Alice" age 30 }>
- Compact:
- Examples:
</ Compact syntax /> user { name "Alice" age 30 active ~true } config { host "localhost" port 8080 ssl ~true } </ Explicit syntax /> metadata <{ version "1.0" author "John Doe" }> settings <{ theme "dark" notifications ~true }>
Array Element
An array is a collection of elements of the same type (e.g., all integers, all strings, all objects).
- Specifiers:
[
and]
(Square Brackets) - Syntax:
- Compact:
[ 1 2 3 ]
,[ "a" "b" "c" ]
- Explicit:
<[ 1 2 3 ]>
,<[ "a" "b" "c" ]>
- Compact:
- Examples:
</ Compact syntax /> numbers [ 1 2 3 4 5 ] names [ "Alice" "Bob" "Charlie" ] booleans [ ~true ~false ~true ] </ Explicit syntax /> ports <[ #80 #443 #8080 ]> colors <[ "red" "green" "blue" ]> flags <[ ~true ~true ~false ]>
Tuple Element
A tuple is an ordered collection of elements that may be of any type, similar to arrays in JSON. Tuples are ideal for containing heterogeneous data like coordinates, records, or mixed-type sequences.
- Specifiers:
(
and)
(Parentheses) - Syntax:
- Compact:
( "Alice" 30 ~true )
- Explicit:
<( "Alice" 30 ~true )>
- Compact:
- Examples:
</ Compact syntax /> userRecord ( "John Doe" 30 ~true ) coordinates ( *42.3601 *-71.0589 ) mixedData ( "Sample" @2023-12-25@ *99.5 ) </ Explicit syntax /> complexTuple <( "Alice" 30 ~true [ "admin" "user" ] )> dataPoint <( "Experiment A" @2023-12-01T10:00:00@ *98.7 )>
Special-Purpose Types
Keyword Element and Key/Value Pairs
The key/value pair is the fundamental building block of XferLang objects. The key must be a keyword element, and the value may be any XferLang element. Key-value pairs form the basis of structured data in objects.
- Specifier:
=
(Equal Sign) - Syntax:
- Implicit:
name
,user_id
,isActive
(letters, numbers, underscores only - valid only as keys) - Compact:
=first-name=
,=email-address=
,=API-Key=
(keywords as keys with=
specifier) - Explicit:
<=first name=>
,<=email address=>
,<=API Key=>
(keywords as keys with explicit syntax)
- Implicit:
- Examples:
{
</ Implicit syntax - only valid as keys />
name "Paul"
age 30
user_id 12345
isActive ~true
</ Compact syntax for keywords as keys />
=first-name= "Alice"
=last-name= "Johnson"
=email-address= "user@example.com"
</ Explicit syntax for keywords as keys />
<=first name=> "Alice"
<=email address=> "user@example.com"
<=API Key=> "secret123"
<=content type=> "application/json"
}
Key/value pairs may recurse; that is, the value in the key value pair may itself be a key/value pair.
{
key1 key2 "key1's value is a key/value pair"
}
Processing Instruction Element
Contains processing instructions for the document, such as the document
PI which stores document metadata.
- Specifier:
!
(Exclamation Mark) - Syntax:
- Compact:
! document { version "1.0" } !
- Explicit:
<! document { version "1.0" } !>
- Compact:
- Examples:
</ Compact syntax />
! document { version "1.0" author "John Doe" } !
! id "user-config" !
! chardef { bullet \$2022 arrow \$2192 } !
</ Explicit syntax />
<! document { version "1.0" description "Sample document" } !>
<! dynamicSource { username env "USER" } !>
Comment Element
Comments provide documentation and annotations within XferLang documents. They are ignored during parsing and may be used for explanations, notes, or temporarily disabling content.
/
(Slash)</ comment />
,<// nested </comment/> via delimiter repetition //>
Dynamic Element
Represents a value resolved via the dynamic source pipeline (dynamicSource PI mapping → custom handler → environment fallback).
|
(Pipe)|USERNAME|
,|DB_PASSWORD|
<|USERNAME|>
Interpolated String Element
Interpolated elements render the contents of embedded elements into a string. Otherwise, they are deserialized to a string type just like the string element. Embedded elements must use explicit syntax in order to be evaluated and rendered.
'
(Apostrophe)'The value is <#42#>'
,'Hello, <|NAME|>!'
<'The value is <#42#>'>
,<'Hello, <|NAME|>!'>
It is a good practice to use explicit syntax (
<' ... '>
) when you are unsure whether interpolated values may contain single quotes.Reference Element
When the keyword inside a reference element has been bound to an element using the
let
processing instruction or alet
operator inside ascript
processing instruction, the parser replaces the entire reference element with a clone of the bound element._
(Underscore)_bindingName
<_bindingName_>
Common Document Patterns
Configuration Documents Most configuration files use Object as the root collection:
Data Collections For homogeneous data, use Array as the root:
Mixed Content Documents For documents with heterogeneous top-level content, use Tuple:
Complete Examples
Here are comprehensive examples showing XferLang in real-world scenarios:
Configuration File Example
User Profile Example
The .NET XferLang Library
The primary implementation of XferLang is the
ParksComputing.Xfer.Lang
library for .NET. It provides a comprehensive object model, a robust parser, and a powerful serialization/deserialization utility class,XferConvert
.Getting Started with .NET
The XferLang .NET library provides a robust implementation for parsing, generating, and working with XferLang documents programmatically.
Installation
Install the NuGet package in your .NET project:
Or via Package Manager Console in Visual Studio:
Basic Serialization and Deserialization
Working with Collections
Advanced Usage with
XferSerializerSettings
For more control, you may pass an instance of
XferSerializerSettings
to theSerialize
andDeserialize
methods.Null Value Handling
Properties with
null
values are included. You may setNullValueHandling
toIgnore
to omit them.Customizing Property Names with
IContractResolver
You may change how property names are serialized by creating a custom contract resolver. For example, to make all property names lowercase:
Custom Type Converters with
IXferConverter
For complete control over how a specific type is handled, you may create a custom converter. This is useful for types that don't map well to standard object serialization or for creating a more compact representation.
Example: A custom converter for a
Person
classNumeric Formatting with Attributes
The library supports custom numeric formatting for integer and long properties using the
XferNumericFormatAttribute
. This allows you to control how numeric values are serialized in hexadecimal or binary formats.Available Formats:
XferNumericFormat.Decimal
- Standard decimal representation (default)XferNumericFormat.Hexadecimal
- Hexadecimal with#$
prefixXferNumericFormat.Binary
- Binary with#%
prefixPadding Options:
MinDigits
- For hexadecimal, pads with leading zeros to minimum digit countMinBits
- For binary, pads with leading zeros to minimum bit countExample:
Safety Notes:
int
andlong
propertiesdecimal
anddouble
types ignore formatting attributes to preserve fractional precisionElementStylePreference
for syntax styleSerializer Settings
For more control over serialization and deserialization, you may use the
XferSerializerSettings
class. This allows you to configure element styles, null handling, contract resolvers, and custom converters.Element Style Preferences
Control how elements are serialized using the
StylePreference
property:<"value">
"value"
(default)Configuration Example
Available Settings
StylePreference
- Controls element serialization stylePreferImplicitSyntax
- Use implicit syntax for integers when possibleNullValueHandling
- How to handle null values (Include/Ignore)ContractResolver
- Custom property name resolutionConverters
- Collection of custom type convertersProperty Attributes
The library provides several attributes to control how properties are serialized and deserialized.
XferPropertyAttribute
The
XferPropertyAttribute
allows you to customize the property name used in the Xfer document, similar toJsonPropertyName
in System.Text.Json.XferNumericFormatAttribute
The
XferNumericFormatAttribute
enables custom formatting for integer and long properties, allowing hexadecimal and binary representations with optional padding.XferDecimalPrecisionAttribute
The
XferDecimalPrecisionAttribute
controls the precision and formatting of decimal and double values during serialization, allowing you to specify the maximum number of decimal places and whether to remove trailing zeros.Key Features:
decimal
anddouble
properties*
for decimal,^
for double)Use Cases:
XferCaptureTagAttribute and XferCaptureIdAttribute
Capture XferLang metadata (tags and IDs) into sibling CLR properties during deserialization. Place the attribute on the target property and reference the source by CLR property name or by the exact document key. Properties decorated with these attributes are skipped during serialization.
Rules and edge cases:
Safety Notes
XferNumericFormatAttribute
) are only applied toint
andlong
propertiesXferDecimalPrecisionAttribute
) are only applied todecimal
anddouble
propertiesdecimal
anddouble
types ignore numeric formatting attributes to preserve fractional precisionElementStylePreference
for syntax styleProcessing Instructions and Dynamic Content
XferLang supports Processing Instructions (PIs) that provide metadata and configuration for documents. PIs are special elements that control parsing behavior, define document metadata, and enable powerful dynamic content features. The processing-instruction system is fully extensible, allowing you to create custom instructions for specialized use cases.
Built-in Processing Instructions
XferLang includes several built-in PIs that address common document needs:
Document Metadata -
document
The
document
processing instruction stores metadata about the XferLang document itself and must appear first if present:Key features:
XferDocument.ProcessingInstructions
Dynamic-Element Data Sources -
dynamicSource
The
dynamicSource
processing instruction configures how dynamic elements<|key|>
are resolved, providing flexible runtime value substitution:Custom Character Definitions -
chardef
The
chardef
processing instruction allows you to define custom character aliases for use in character elements:Element ID -
id
The
id
processing instruction assigns identifiers to elements for referencing and linking:Element Tagging -
tag
The
tag
processing instruction attaches free‑form classification metadata to the immediately following element. Multipletag
processing instructions may be stacked; tags are preserved in element metadata but have no built‑in semantic effect.Conditional Element Parsing and Output -
if defined
The
if defined
processing instruction evaluates whether its value element yields a meaningful value (non‑null / non‑empty). When the value is defined, the associated content is parsed and output; otherwise, it is skipped.Evaluation occurs during processing-instruction processing; the processing instruction itself is suppressed from serialization.
Dynamic Elements and Source Resolution
Dynamic elements provide runtime value substitution with an extensible source system.
Built-in Source Types
XferLang includes three built-in source handlers:
Constant Sources (
const
)greeting const "Hello, World!"
Environment Variables (
env
)username env "USER"
(reads from $USER environment variable)File Sources (
file
)config file "settings.json"
Custom Source Handler Registration
You may extend the dynamic-source handler with custom source types for databases, web services, or other data sources:
Resolution Priority:
dynamicSource
PIExtending Processing Instructions
The PI system is designed for extensibility, allowing you to create custom instructions for specialized parsing and document processing needs.
Creating Custom PIs
To create a custom PI, inherit from
ProcessingInstruction
and implement the required behavior:PI Registration and Lifecycle
Register custom PIs with the parser during initialization:
Processing-Instruction Lifecycle:
ProcessingInstructionHandler()
is called for document-level setupElementHandler()
is called for each relevant elementAdvanced Features:
This extensible PI system makes XferLang highly adaptable for domain-specific needs, from configuration management to data validation and transformation pipelines.
Writing Custom Processing Instructions
You may add new Processing Instruction (PI) keywords without modifying the core parser by registering a factory that creates a
ProcessingInstruction
subclass. This lets you introduce custom metadata, validation passes, conditional logic, or side‑effects at parse time.When to Create a PI
Create a PI when you need to:
ProcessingInstructionHandler
).ElementHandler
).SuppressSerialization = true
).Do NOT create a PI just to change how
|dynamic|
values resolve—use a customIDynamicSourceResolver
for that (see "Dynamic Elements and Source Resolution").Parser Registration Model
The parser exposes two overloads of
RegisterPIProcessor
:Built‑ins are installed via the factory form inside
RegisterBuiltInPIProcessors()
.Minimal Custom PI Example (
trace
)Usage:
Conditioning the Next Element
To influence the element that immediately follows the PI, override
ElementHandler
. Pattern (seeIfProcessingInstruction
in source):Validation PI Skeleton
Extending Dynamic Resolution (No New PI Required)
If you just need custom resolution for
|key|
, implement a resolver:Checklist for a New PI
Keyword
.ProcessingInstruction
.SuppressSerialization
in constructor.ProcessingInstructionHandler
for one-time setup.ElementHandler
if you must inspect or suppress the following element.parser.RegisterPIProcessor(Keyword, Factory)
.Lifecycle Summary
<! keyword value !>
.ProcessingInstructionHandler()
immediately.ElementHandler()
runs (if overridden).SuppressSerialization
is true.This model keeps the core grammar stable while enabling domain‑specific behaviors.
Building XferLang
Information for developers who want to build XferLang from source or contribute to the project.
Prerequisites
Quick Start
Project Structure
The XferLang solution contains several projects and folders:
Development Commands
Community and Resources
Join the XferLang community and access helpful resources for learning and development.
Learning Resources
Getting Help
Contributing
This is an open-source project, and contributions are always welcome! If you are interested in helping, please feel free to open an issue or a pull request on GitHub. You may also reach out to me directly via email at paul@parkscomputing.com.
Grammar
The formal Backus–Naur form (BNF) grammar for XferLang can be found in the XferLang GitHub repository: xfer.bnf.