XferLang Documentation

Your complete guide to the XferLang data-interchange format and the .NET library.

Introduction to XferLang

XferLang is a modern data-interchange format designed to support data serialization, data transmission, and offline use cases such as configuration management. Unlike JSON or XML, XferLang is designed with explicit typing, human readability, and safe data embedding at its core.

XferLang is an open-source project hosted on GitHub at github.com/paulmooreparks/Xfer. The project includes the complete language specification, a production-ready .NET library, comprehensive documentation, and development tools.

Here's a quick comparison to show XferLang in action:

JSON Example:

{
  "name": "John Doe",
  "age": 30,
  "isActive": true,
  "addresses": [
    {
      "type": "home",
      "street": "123 Main St",
      "city": "Anytown"
    }
  ],
  "metadata": null
}

XferLang Equivalent:

{
    name "John Doe"
    age #30
    isActive ~true
    addresses [
        {
            type "home"
            street "123 Main St"
            city "Anytown"
        }
    ]
    metadata ?
}

Notice how XferLang eliminates commas, uses explicit type prefixes (#30 for integers, ~true for booleans, ? for null), and maintains excellent readability.

Because whitespace is flexible, the same document can be made extremely compact:

{name"Alice"age 30 isMember~true scores[*85*90*78.5]profile{email"alice@example.com"joinedDate@2023-05-05T20:00:00@}}

Design Philosophy

XferLang is built around four core principles:

1. Clarity and Readability

The syntax is designed to be human-readable without requiring separators like commas. Whitespace naturally delimits elements, making documents easier to scan and understand.

2. Explicit Typing

All values are explicitly typed using prefixes. This eliminates the type ambiguity that can occur in formats like JSON, leading to more predictable parsing and less defensive coding.

3. Elimination of Escaping

XferLang doesn't require escaping special characters in values. Instead, values use unique delimiters that can be repeated as needed to avoid conflicts.

4. Safety in Embedding

The delimiter-repetition strategy allows safe embedding of complex data without risk of delimiter collision, making it perfect for configuration files and data transmission.

Basic Syntax

XferLang documents consist of elements separated by whitespace. Each element can be a simple value or a complex structure. XferLang supports three different syntax variations for writing elements.

Comments

Comments start with </ and end with />:

</ This is a comment />
name "John Doe"  </ This is also a comment />

Whitespace

XferLang uses whitespace (spaces, tabs, newlines) to separate elements. Multiple whitespace characters are treated as a single separator:

</ These are equivalent />
name "John" age #30
name "John"    age    #30
name "John"
age #30

Metadata & Processing Instructions (PIs)

XferLang supports document-level metadata and extensible processing instructions (PIs) for configuration, runtime directives, and schema association. These features provide powerful control over parsing, validation, and dynamic value resolution.

General Syntax

Metadata and PIs are enclosed in angle brackets with exclamation marks, e.g., <! ... !>, and typically appear at the top of a document.

<! name { key1 value1 key2 value2 ... } !>

Document Metadata

Use the xfer PI to specify XferLang version and document metadata:

<! xfer "0.7.2" author "John Doe" created @2023-12-25T10:00:00Z@ !>

Schema Association (TO BE IMPLEMENTED)

Use the schema PI to associate a document with a schema definition:

<! schema "foo" !>

Dynamic Value Resolution

The dynamicSource PI allows you to override how dynamic values (e.g., environment variables, secrets, test data) are resolved at runtime. Example:

<! dynamicSource { key "env:NAME" } !>

Extensibility

PIs are designed to be extensible. You can define custom PIs for your application and implement corresponding logic in your parser or resolver. For dynamic value resolution, subclass DefaultDynamicSourceResolver or implement IDynamicSourceResolver in .NET.

Why Use Metadata & PIs?

Syntax Variations

XferLang supports three different syntax styles for writing elements, providing flexibility for different use cases:

Implicit Syntax

For the most common types, like integers and simple keywords, no special characters are needed at all. The parser infers the type from the content.

123                 </ An integer />
name "Alice"        </ A key/value pair of a keyword 'name' and a string value />

Compact Syntax

For most other types, a single specifier character (or a pair for collections) denotes the type. This is the most common syntax.

~true               </ A boolean />
*123.45             </ A decimal />
"Hello, World!"     </ A string />
[ 1 2 3 ]           </ An array of integers />

Explicit Syntax

When an element's content might be ambiguous (e.g., a string containing a quote), you can wrap the compact form in angle brackets (< and >). This is the most verbose but also the most powerful form, as it allows for delimiter repetition to avoid any collision.

<"Alice said, "Boo!"">
<// A comment containing </another comment/> //>

Data Types

XferLang supports a rich set of data types with explicit type indicators:

Strings

Strings are enclosed in double quotes. No escaping is needed - if your string contains quotes, use multiple quote characters. You can use compact syntax for simple strings or explicit syntax for complex content:

simple "Hello, World!"
withQuotes ""She said "Hello" to me""
multipleQuotes """This string contains "" quotes"""

For complex strings that might contain multiple quote sequences, you can use the explicit string syntax:

complexString <"This can contain "quotes" and ""multiple"" quotes">

Numbers

Numbers use explicit type prefixes in compact syntax or can be written without prefixes using implicit syntax when unambiguous:

integer #42
negative #-123
hexInteger #$FF
binaryInteger #%10101010
long &123456789
hexLong &$DEADBEEF
binaryLong &%1001010100000010111110010000000000
decimal *3.14159
double ^2.71828

You can also write integers without the prefix when the context is clear (implicit syntax):

simpleNumber 42

Hexadecimal and Binary Formatting: Integer, long, and character elements support alternative numeric representations using #$ for hexadecimal and #% for binary (Integer), &$/&% for Long values, and \$/\% for Character values. These formats are ideal for memory addresses, color values, bitmasks, Unicode, and low-level programming contexts.

Booleans

Booleans use the ~ prefix in compact syntax:

isActive ~true
isDeleted ~false

Null Values

Null values use the ? (question mark) specifier in compact syntax:

optionalValue ?
explicitNull <??>

Characters

Single characters use the backslash (\) prefix and can be specified in several explicit ways: Unicode code points (\$2764 for '❤'), decimal (\65 for 'A'), hexadecimal (\$41 for 'A'), binary (\%1000001 for 'A'), and named control characters (nul, cr, lf, nl, tab, vtab, bksp, ff, bel, quote, apos, backslash, lt, gt).

letter \A
space \32
unicode \$1F600  </ 😀 emoji />
tab \tab
newline \lf
verticalTab \vtab
backspace \bksp
formFeed \ff
bell \bel
doubleQuote \quote
singleQuote \apos
backslash \backslash
lessThan \lt
greaterThan \gt
hexadecimal \$41  </ 'A' />
binary \%1000001  </ 'A' />

Dates and Times

Dates and times use the @ prefix with ISO 8601 format and closing @:

birthDate @2023-12-25@
meetingTime @2023-12-25T14:30:00@
timestamp @2023-12-25T14:30:00.123Z@

Interpolated Elements

XferLang supports interpolated elements that can contain expressions and other elements. These always require explicit syntax for complex content:

greeting 'Hello, <"World">!</ This is a comment inside an eval />!'
emoji 'I <\$2764\> Xfer <\$1F600\>'  </ I ❤︎ Xfer 😀 />

Xfer Element Types (Concept & Code)

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. The main categories are:

These concepts map directly to .NET element classes, which provide programmatic access and manipulation. Below you'll find each element type described both conceptually and with .NET code usage.

IntegerElement

Represents a 32-bit integer value.

// Concept: Used for whole numbers.
var element = new IntegerElement(42);
int value = element.Value; // 42

LongElement

Represents a 64-bit integer value.

var element = new LongElement(1234567890123L);
long value = element.Value;

BooleanElement

Represents a boolean value (true/false).

var element = new BooleanElement(true);
bool value = element.Value;

DoubleElement

Represents a double-precision floating-point value.

var element = new DoubleElement(3.14159);
double value = element.Value;

DecimalElement

Represents a high-precision decimal value.

var element = new DecimalElement(123.45m);
decimal value = element.Value;

DateTimeElement

Represents a date and time value.

var element = new DateTimeElement(DateTime.Now);
DateTime value = element.Value;

DateElement

Represents a date-only value.

var element = new DateElement(DateOnly.FromDateTime(DateTime.Now));
DateOnly value = element.Value;

TimeElement

Represents a time-only value.

var element = new TimeElement(TimeOnly.FromDateTime(DateTime.Now));
TimeOnly value = element.Value;

TimeSpanElement

Represents a time interval.

var element = new TimeSpanElement(TimeSpan.FromMinutes(90));
TimeSpan value = element.Value;

StringElement

Represents a string value.

var element = new StringElement("Hello, world!");
string value = element.Value;

CharacterElement

Represents a single character. Supported formats: literal (\A), Unicode code point (\$2764 for '❤'), decimal (\65 for 'A'), hexadecimal (\$41 for 'A'), binary (\%1000001 for 'A'), and named control (cr, lf, tab, vtab, bksp, ff, bel, quote, apos, backslash, lt, gt).
Hexadecimal and binary formatting for character elements works the same as for integer and long elements. Example: var element = new CharacterElement('A');, var element = new CharacterElement('\n');, var element = new CharacterElement((char)0x2764);

var element = new CharacterElement('A');
char value = element.Value;

Guid/StringElement

Represents a GUID value as a string.

var element = new StringElement(Guid.NewGuid().ToString());
Guid value = Guid.Parse(element.Value);

Enum/StringElement

Represents an enum value as a string.

var element = new StringElement(MyEnum.Value.ToString());
MyEnum value = Enum.Parse(element.Value);

Uri/StringElement

Represents a URI value as a string.

var element = new StringElement("https://example.com");
Uri value = new Uri(element.Value);

ArrayElement

Represents a typed array of elements.

var element = new TypedArrayElement();
element.Add(new IntegerElement(1));
element.Add(new IntegerElement(2));
// ...

TupleElement

Represents an ordered collection of elements.

var element = new TupleElement();
element.Add(new IntegerElement(1));
element.Add(new StringElement("two"));
// ...

ObjectElement

Represents a set of named key-value pairs.

var element = new ObjectElement();
element.AddOrUpdate(new KeyValuePairElement(new IdentifierElement("Name"), new StringElement("Alice")));
// ...

KeyValuePairElement

Represents a single key-value pair.

var kvp = new KeyValuePairElement(new IdentifierElement("Age"), new IntegerElement(30));

NullElement

Represents a null value.

var element = new NullElement();
object? value = element.Value; // null

InterpolatedElement

Represents an interpolated string value.

var element = new InterpolatedElement("${user.Name}");
string value = element.Value;

DynamicElement

Represents a dynamic value that is replaced during parsing or deserialization.

var element = new DynamicElement("{value}");
string value = element.Value;

Complex Structures

Objects

Objects are enclosed in curly braces and contain key-value pairs:

{
    name "John Doe"
    age #30
    address {
        street "123 Main St"
        city "Anytown"
        zipCode "12345"
    }
}

Arrays

Arrays are enclosed in square brackets and contain elements of the same type:

numbers [#1 #2 #3 #4 #5]
names ["Alice" "Bob" "Charlie"]
decimals [*85.0 *90.0 *78.5]

Tuples

Tuples are enclosed in parentheses and represent collections of elements of any type (similar to JSON arrays):

coordinates (*42.3601 *-71.0589)
nameAge ("John Doe" #30)
mixed (#1 "hello" ~true ?)

Nested Structures

Objects, arrays, and tuples can be freely nested:

users [
    {
        name "Alice"
        age #25
        roles ["admin" "user"]
    }
    {
        name "Bob"
        age #30
        roles ["user"]
    }
]

</ Tuple with mixed types />
userInfo ("Alice" #25 ["admin" "user"] ~true)

Tuples vs Arrays

Remember the key difference:

Complete Examples

Configuration File

<! xfer "0.7.2"
   name "Application Configuration"
   version "1.2.3"
!>

database {
    host "localhost"
    port #5432
    name "myapp"
    credentials {
        username "dbuser"
        password '<DBPASSWORD>'
    }
    connectionPool {
        minSize #5
        maxSize #20
        timeout *30.0
    }
}

logging {
    level "INFO"
    outputs ["console" "file"]
    file {
        path "/var/log/myapp.log"
        maxSize "10MB"
        rotate ~true
    }
}

features {
    enableCache ~true
    enableMetrics ~true
    experimentalFeatures ~false
}

User Profile Data

{
    id #12345
    profile {
        firstName "Jane"
        lastName "Smith"
        email "jane.smith@example.com"
        birthDate @1990-05-15@
        verified ~true
    }

    preferences {
        theme "dark"
        language "en-US"
        notifications {
            email ~true
            push ~false
            sms ~true
        }
    }

    addresses [
        {
            type "home"
            street "456 Oak Avenue"
            city "Springfield"
            state "IL"
            zipCode "62701"
            country "US"
            isPrimary ~true
        }
        {
            type "work"
            street "789 Business Blvd"
            city "Springfield"
            state "IL"
            zipCode "62702"
            country "US"
            isPrimary ~false
        }
    ]

    lastLogin @2023-12-25T09:30:00Z@
    loginCount #247
}

Grammar Reference

For those who want to implement parsers or need the complete technical specification, here's the formal grammar for XferLang:

<document> ::= <opt_whitespace> <metadata_element>? <opt_whitespace> <body_element>* <opt_whitespace>

<metadata_element> ::= <metadata_element_explicit> | <metadata_element_compact>
<metadata_element_explicit> ::= <element_open> <metadata_specifier> <key_value_pair>+ <metadata_specifier> <element_close>
<metadata_element_compact> ::= <metadata_specifier> <key_value_pair>* <metadata_specifier>

<body_element> ::= <opt_whitespace> (
      <key_value_pair>
    | <string_element>
    | <character_element>
    | <integer_element>
    | <long_element>
    | <double_element>
    | <decimal_element>
    | <boolean_element>
    | <datetime_element>
    | <date_element>
    | <time_element>
    | <timespan_element>
    | <null_element>
    | <object_element>
    | <array_element>
    | <tuple_element>
    | <comment_element>
    | <dynamic_element>
    | <interpolated_element>
    ) <opt_whitespace>

<string_element> ::= <string_element_explicit> | <string_element_compact>
<string_element_explicit> ::= <element_open> <metadata_specifier> <text> <metadata_specifier> <element_close>
<string_element_compact> ::= <metadata_specifier> <text> <metadata_specifier>

<key_value_pair> ::= <keyword_element> <opt_whitespace> <body_element>

<keyword_element> ::= <keyword_element_explicit> | <keyword_element_compact> | <keyword_element_implicit>
<keyword_element_explicit> ::= <element_open> <keyword_specifier> <text> <keyword_specifier> <element_close>
<keyword_element_compact> ::= <keyword_specifier> <text> <keyword_specifier>
<keyword_element_implicit> ::= <identifier>

<character_element> ::= <character_element_explicit> | <character_element_compact>
<character_element_explicit> ::= <element_open> <character_specifier> <opt_whitespace> <character_value> <opt_whitespace> <character_specifier> <element_close>
<character_element_compact> ::= <character_specifier> <character_value>

<integer_element> ::= <integer_element_explicit> | <integer_element_compact> | <integer_element_implicit>
<integer_element_explicit> ::= <element_open> <integer_specifier> <opt_whitespace> <integer_value> <opt_whitespace> <integer_specifier> <element_close>
<integer_element_compact> ::= <integer_specifier> <integer_value>
<integer_element_implicit> ::= <integer_value>

<long_element> ::= <long_element_explicit> | <long_element_compact>
<long_element_explicit> ::= <element_open> <long_specifier> <opt_whitespace> <long_value> <opt_whitespace> <long_specifier> <element_close>
<long_element_compact> ::= <long_specifier> <long_value>

<double_element> ::= <double_element_explicit> | <double_element_compact>
<double_element_explicit> ::= <element_open> <opt_whitespace> <double_specifier> <opt_whitespace> <decimal_value> <double_specifier> <element_close>
<double_element_compact> ::= <double_specifier> <decimal_value>

<decimal_element> ::= <decimal_element_explicit> | <decimal_element_compact>
<decimal_element_explicit> ::= <element_open> <decimal_specifier> <opt_whitespace> <decimal_value> <opt_whitespace> <decimal_specifier> <element_close>
<decimal_element_compact> ::= <decimal_specifier> <decimal_value>

<boolean_element> ::= <boolean_element_explicit> | <boolean_element_compact>
<boolean_element_explicit> ::= <element_open> <boolean_specifier> <opt_whitespace> <boolean> <opt_whitespace> <boolean_specifier> <element_close>
<boolean_element_compact> ::= <boolean_specifier> <boolean>

<datetime_element> ::= <datetime_element_explicit> | <datetime_element_compact>
<datetime_element_explicit> ::= <element_open> <datetime_specifier> <opt_whitespace> <datetime> <opt_whitespace> <datetime_specifier> <element_close>
<datetime_element_compact> ::= <datetime_specifier> <datetime> <datetime_specifier>

<date_element> ::= <date_element_explicit> | <date_element_compact>
<date_element_explicit> ::= <element_open> <date_specifier> <opt_whitespace> <date> <opt_whitespace> <date_specifier> <element_close>
<date_element_compact> ::= <date_specifier> <date> <date_specifier>

<time_element> ::= <time_element_explicit> | <time_element_compact>
<time_element_explicit> ::= <element_open> <time_specifier> <opt_whitespace> <time> <opt_whitespace> <time_specifier> <element_close>
<time_element_compact> ::= <time_specifier> <time> <time_specifier>

<timespan_element> ::= <timespan_element_explicit> | <timespan_element_compact>
<timespan_element_explicit> ::= <element_open> <timespan_specifier> <opt_whitespace> <timespan> <opt_whitespace> <timespan_specifier> <element_close>
<timespan_element_compact> ::= <timespan_specifier> <timespan> <timespan_specifier>

<null_element> ::= <null_element_explicit> | <null_element_compact>
<null_element_explicit> ::= <element_open> <null_specifier> <null_specifier> <element_close>
<null_element_compact> ::= <null_specifier>

<dynamic_element> ::= <dynamic_element_explicit> | <dynamic_element_compact>
<dynamic_element_explicit> ::= <element_open> <dynamic_specifier> <opt_whitespace> <identifier> <opt_whitespace> <dynamic_specifier> <element_close>
<dynamic_element_compact> ::= <dynamic_specifier> <identifier>

<comment_element> ::= <element_open> <comment_specifier> <text> <comment_specifier> <element_close>

<interpolated_element> ::= <interpolated_element_explicit> | <interpolated_element_compact>
<interpolated_element_explicit> ::= <element_open> <interpolated_specifier> <opt_whitespace> <interpolated_content> <opt_whitespace> <interpolated_specifier> <element_close>
<interpolated_element_compact> ::= <interpolated_specifier> <opt_whitespace> <interpolated_content> <opt_whitespace> <interpolated_specifier>

<interpolated_content> ::= (<text> | <string_element_explicit> | <character_element_explicit> | <integer_element_explicit>
                 | <long_element_explicit> | <double_element_explicit> | <decimal_element_explicit>
                 | <boolean_element_explicit> | <datetime_element_explicit> | <dynamic_element_explicit>
                 | <interpolated_element_explicit>)*

<object_element> ::= <object_element_explicit> | <object_element_compact>
<object_element_explicit> ::= <element_open> <object_specifier_open> <opt_whitespace> <key_value_pair>* <opt_whitespace> <object_specifier_close> <element_close>
<object_element_compact> ::= <object_specifier_open> <opt_whitespace> <key_value_pair>* <opt_whitespace> <object_specifier_close>

<array_element> ::= <array_element_explicit> | <array_element_compact>
<array_element_explicit> ::= <element_open> <array_specifier_open> <opt_whitespace> <body_element>* <opt_whitespace> <array_specifier_close> <element_close>
<array_element_compact> ::= <array_specifier_open> <opt_whitespace> <body_element>* <opt_whitespace> <array_specifier_close>

<tuple_element> ::= <tuple_element_explicit> | <tuple_element_compact>
<tuple_element_explicit> ::= <element_open> <tuple_specifier_open> <opt_whitespace> <body_element>* <opt_whitespace> <tuple_specifier_close> <element_close>
<tuple_element_compact> ::= <tuple_specifier_open> <opt_whitespace> <body_element>* <opt_whitespace> <tuple_specifier_close>

<element_open> ::= "<"
<element_close> ::= ">"

<metadata_specifier> ::= "!"+
<string_specifier> ::= "\""+
<keyword_specifier> ::= "="+
<character_specifier> ::= "\\"+
<integer_specifier> ::= "#"+
<long_specifier> ::= "&"+
<double_specifier> ::= "^"+
<decimal_specifier> ::= "*"+
<boolean_specifier> ::= "~"+
<datetime_specifier> ::= "@"+
<date_specifier> ::= "@"+
<time_specifier> ::= "@"+
<timespan_specifier> ::= "@"+
<null_specifier> ::= "?"+
<object_specifier_open> ::= "{"+
<object_specifier_close> ::= "}"+
<array_specifier_open> ::= "["+
<array_specifier_close> ::= "]"+
<tuple_specifier_open> ::= "("+
<tuple_specifier_close> ::= ")"+
<comment_specifier> ::= "/"+
<dynamic_specifier> ::= "|"+
<interpolated_specifier> ::= "'"+

<character_value> ::= <positive_integer>
    | <hexadecimal> | <binary>
    | "nul" | "cr" | "lf" | "nl" | "tab" | "vtab"
    | "bksp" | "ff" | "bel" | "quote" | "apos"
    | "backslash" | "lt" | "gt"

<integer_value> ::= <signed_integer>
    | <hexadecimal>
    | <binary>
    | <hex_integer>
    | <binary_integer>
    | <dynamic_element>

<long_value> ::= <signed_integer>
    | <hexadecimal>
    | <binary>
    | <hex_long>
    | <binary_long>
    | <dynamic_element>

<decimal_value> ::= <signed_decimal> | <dynamic_element>

<signed_integer> ::= ("+" | "-")? [0-9]+
<signed_decimal> ::= ("+" | "-")? [0-9]+ "." [0-9]*
<positive_integer> ::= [0-9]+
<hexadecimal> ::= "$" ([0-9] | [A-F] | [a-f])+
<binary> ::= "%" [0-1]+
<hex_integer> ::= "#$" ([0-9] | [A-F] | [a-f])+
<binary_integer> ::= "#%" [0-1]+
<hex_long> ::= "&$" ([0-9] | [A-F] | [a-f])+
<binary_long> ::= "&%" [0-1]+

<opt_whitespace> ::= <whitespace>*
<whitespace> ::= (" " | "\t" | "\n" | "\r")

<boolean> ::= "true" | "false"

<datetime> ::= [0-9]{4} "-" [0-9]{2} "-" [0-9]{2} ("T" [0-9]{2} ":" [0-9]{2} (":" [0-9]{2} ("." [0-9]+)?)?)? ("Z" | ("+" | "-") [0-9]{2} ":" [0-9]{2})? | <dynamic_element>

<date> ::= [0-9]{4} "-" [0-9]{2} "-" [0-9]{2} | <dynamic_element>

<time> ::= [0-9]{2} ":" [0-9]{2} (":" [0-9]{2} ("." [0-9]+)?)? | <dynamic_element>

<timespan> ::= ("-")?([0-9]+".")[0-9]{2}":"[0-9]{2}":"[0-9]{2}("."[0-9]+)? | <dynamic_element>

<identifier> ::= ([A-Z] | [a-z] | "_") ([A-Z] | [a-z] | "_" | [0-9])*

<text> ::= <character>*
<character> ::= /* any Unicode character except the sequence that matches the closing delimiter of the current element */

The complete grammar specification is available in the xfer.bnf file in the repository.

Getting Started with .NET

Now that you understand the XferLang format, let's explore how to use it in .NET applications.

Install from NuGet (Recommended)

The ParksComputing.Xfer.Lang library provides a powerful and flexible set of tools for working with XferLang in .NET. To get started, install the package from NuGet:

dotnet add package ParksComputing.Xfer.Lang

Build from Source

Alternatively, you can build XferLang from source by cloning the GitHub repository:

git clone https://github.com/paulmooreparks/Xfer.git
cd Xfer
dotnet build
dotnet test

The library provides similar functionality to popular JSON libraries like Newtonsoft.Json, but for the XferLang format. You can serialize .NET objects to XferLang and deserialize XferLang documents back to .NET objects.

For detailed build instructions, development setup, and contribution guidelines, see the Building XferLang section.

Serialization

Serializing a .NET object to an Xfer string is straightforward using the static XferConvert.Serialize method.


public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
    public bool IsActive { get; set; }
}

var user = new User { Name = "John Doe", Age = 30, IsActive = true };

// Serialize the object to an Xfer string
string xferString = XferConvert.Serialize(user, Formatting.Indented);

Console.WriteLine(xferString);
// Output will look like:
// {
//     Name "John Doe"
//     Age 30
//     IsActive ~true
// }
            

Deserialization

To deserialize an Xfer string back into a .NET object, use the generic XferConvert.Deserialize<T> method.


string xferString = @"{ Name ""Jane Doe"" Age 28 IsActive ~false }";

var user = XferConvert.Deserialize<User>(xferString);

// user.Name will be "Jane Doe"
// user.Age will be 28
// user.IsActive will be false
            

Serializer Settings

For more control over serialization and deserialization, you can 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:

Configuration Example


var settings = new XferSerializerSettings
{
    StylePreference = ElementStylePreference.CompactWhenSafe,
    PreferImplicitSyntax = true,  // Use implicit syntax for integers when safe
    NullValueHandling = NullValueHandling.Ignore,
    ContractResolver = new CustomContractResolver(),
    // ... other settings
};

string xferString = XferConvert.Serialize(user, Formatting.None, settings);
            

Available Settings

Property 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 to JsonPropertyName in System.Text.Json.


public class User
{
    [XferProperty("user_name")]
    public string UserName { get; set; }

    [XferProperty("is_active")]
    public bool IsActive { get; set; }

    // This property will use the default name "Age"
    public int Age { get; set; }
}
            

This will produce Xfer like: {user_name "Alice" is_active ~true Age 25}

XferEvaluatedAttribute

The XferEvaluatedAttribute is used internally to mark properties that have been processed during deserialization. This is typically not used by end users but may be relevant for advanced serialization scenarios.


public class MyClass
{
    [XferEvaluated]
    public string ProcessedProperty { get; set; }
}
            

Numeric Formatting 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

Padding Options

Example Usage


public class ConfigurationData
{
    [XferNumericFormat(XferNumericFormat.Decimal)]
    public int Port { get; set; } = 8080;

    [XferNumericFormat(XferNumericFormat.Hexadecimal)]
    public int ColorValue { get; set; } = 0xFF5733;

    [XferNumericFormat(XferNumericFormat.Binary, MinBits = 8)]
    public int Flags { get; set; } = 42;

    [XferNumericFormat(XferNumericFormat.Hexadecimal, MinDigits = 8)]
    public long MemoryAddress { get; set; } = 0x7FF6C2E40000;
}

var config = new ConfigurationData();
string xfer = XferConvert.Serialize(config);
// Result: {Port 8080 ColorValue #$FF5733 Flags #%00101010 MemoryAddress &$7FF6C2E40000}
            

Safety Notes

Writing Custom Processing Instruction (PI) Processors

XferLang.NET allows you to extend the parser and deserializer by implementing custom logic for Processing Instructions (PIs). This is useful for advanced configuration, runtime directives, schema association, and dynamic value resolution.

Conceptual Overview

.NET API Usage

To handle custom PIs, implement or extend the relevant resolver or processor interface. For example, to handle dynamicSource PIs, implement IDynamicSourceResolver or subclass DefaultDynamicSourceResolver:


public class MyDynamicSourceResolver : DefaultDynamicSourceResolver
{
    public override string? Resolve(string key)
    {
        // Custom logic for dynamic keys
        if (key == "special")
            return "custom-value";
        // Fallback to default
        return base.Resolve(key);
    }
}

// Usage:
var settings = new XferSerializerSettings
{
    DynamicSourceResolver = new MyDynamicSourceResolver()
};

var obj = XferConvert.Deserialize(xferString, settings);
    

Demo: Custom PI Processor


// Example: Custom PI for feature flags
public class FeatureFlagProcessor : IProcessingInstructionProcessor
{
    public void Process(ProcessingInstructionElement pi, XferDocumentContext context)
    {
        if (pi.Name == "featureFlag")
        {
            var flag = pi["enabled"]?.Value;
            // Apply feature flag logic
            context.Items["FeatureEnabled"] = flag == "true";
        }
    }
}

// Register processor
var settings = new XferSerializerSettings();
settings.ProcessingInstructionProcessors.Add(new FeatureFlagProcessor());
    

Best Practices

Custom PI processors make XferLang.NET highly adaptable for configuration, runtime, and integration scenarios.

Contract Resolvers

A contract resolver allows you to customize how property names are mapped between your .NET objects and the Xfer document. For example, you can easily convert property names to camelCase.


public class CamelCaseContractResolver : DefaultContractResolver
{
    public override string ResolvePropertyName(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName) || !char.IsUpper(propertyName[0]))
        {
            return propertyName;
        }
        return char.ToLowerInvariant(propertyName[0]) + propertyName.Substring(1);
    }
}

var settings = new XferSerializerSettings
{
    ContractResolver = new CamelCaseContractResolver()
};

var user = new User { Name = "John Doe", Age = 30, IsActive = true };
string xferString = XferConvert.Serialize(user, Formatting.Indented, settings);

// Console.WriteLine(xferString);
// {
//     name "John Doe"
//     age 30
//     isActive ~true
// }
            

Custom Converters

For complex types or when you need a custom representation, you can create your own converter by inheriting from XferConverter<T>. This gives you full control over the serialization and deserialization process for a specific type.


// A custom converter for the Person class
public class PersonConverter : XferConverter<Person>
{
    public override Element WriteXfer(Person value, XferSerializerSettings settings)
    {
        // Serialize to a compact string: "John Doe,42"
        return new StringElement($"{value.Name},{value.Age}");
    }

    public override Person ReadXfer(Element element, XferSerializerSettings settings)
    {
        if (element is StringElement stringElement)
        {
            var parts = stringElement.Value.Split(',');
            if (parts.Length == 2 && int.TryParse(parts[1], out int age))
            {
                return new Person { Name = parts[0], Age = age };
            }
        }
        throw new InvalidOperationException("Cannot convert element to Person.");
    }
}

// Add the converter to your settings
var settings = new XferSerializerSettings();
settings.Converters.Add(new PersonConverter());

var person = new Person { Name = "John Doe", Age = 42 };
string xfer = XferConvert.Serialize(person, settings); // Result: "John Doe,42"

var deserializedPerson = XferConvert.Deserialize<Person>(xfer, settings);
            

GitHub Repository

XferLang is an open-source project hosted on GitHub. The repository contains the complete source code, documentation, examples, and development tools.

Repository Overview

Key Repository Contents

Getting Involved

XferLang welcomes contributions! Here's how you can get involved:

Alternative Implementations

The repository also includes experimental implementations in other languages:

Building XferLang

XferLang is built using standard .NET development tools. Follow these instructions to build the project from source.

Prerequisites

Quick Start

# Clone the repository
git clone https://github.com/paulmooreparks/Xfer.git
cd Xfer

# Build the entire solution
dotnet build

# Run the tests
dotnet test

# Create NuGet packages (optional)
dotnet pack --configuration Release

Project Structure

The XferLang solution contains several projects:

Development Commands

# Build in Debug mode
dotnet build --configuration Debug

# Build in Release mode
dotnet build --configuration Release

# Run specific test project
dotnet test XferTest/

# Run with verbose output
dotnet test --verbosity normal

# Run tests with code coverage
dotnet test --collect:"XPlat Code Coverage"

# Package for NuGet distribution
dotnet pack ParksComputing.Xfer.Lang/ --configuration Release --output ./nupkg

Using the Command-Line Tools

The xferc project provides command-line tools for working with XferLang:

# Build the CLI tools
dotnet build xferc/

# Run the XferLang REPL
dotnet run --project xferc/

# Convert JSON to XferLang
dotnet run --project xferc/ -- convert input.json output.xfer

# Validate XferLang documents
dotnet run --project xferc/ -- validate document.xfer

IDE Setup

Visual Studio

Open Xfer.sln in Visual Studio 2022 or later. The solution includes all projects and is ready for development.

VS Code

Install the C# extension and open the repository folder. VS Code will automatically detect the .NET projects.

# Install recommended extensions
code --install-extension ms-dotnettools.csharp
code --install-extension ms-dotnettools.vscode-dotnet-runtime

Contributing Guidelines

When contributing to XferLang:

Community & Resources

Here are the key resources for learning, getting help, and staying up to date with XferLang.

Official Resources

Documentation & Examples

Getting Help

Development Status

XferLang is actively developed with regular updates and improvements:

Language Comparison

See how XferLang compares to other data formats:

Feature XferLang JSON XML YAML
Explicit Types ✅ Built-in ❌ Limited ⚠️ Via attributes ⚠️ Via tags
Human Readable ✅ Yes ✅ Yes ⚠️ Verbose ✅ Yes
Comments ✅ Native ❌ No ✅ Native ✅ Native
Safety ✅ High ⚠️ Medium ✅ High ⚠️ Medium