The binder starts off by creating an instance of the bound type with default values of public fields and properties (the same way as Activator.CreateInstance
does) and then recursively binds the value of every field and property to a corresponding subsection of the settings tree. See binding nodes to models page for a step-by-step illustration of this process.
Fields and properties don't have to be mutable: readonly fields and properties with private setters (or even without one) are fine.
Following members are ignored: indexers, constants, static and non-public fields and properties, computed properties without backing fields.
Nested classes and collections are supported without restrictions.
When binding a field or property value, the settings tree is scoped to the member name. This imposes a requirement to synchronize naming in configuration data provided by sources and models in the application code. In order to bind a model property named Timeout
from a section of JSON file, this section must contain a property with the same name (barring case).
Name aliases allow to decouple property names from configuration data names.
By default all fields and properties are treated as optional: they are just left with default values if no data could be found in the settings tree (default values may come from field and property initializers). It's possible to make members required though. Required members with no data in the settings tree cause the binding process to fail and produce an exception.
Model classes must either have a parameterless constructor, a constructor with a single parameter, or have an OmitConstructorsAttribute
(which allows creating an uninitialized object, therefore, no default parameters are present).
Binders are required for all public fields and properties.
Only object nodes are supported.
A type that has exactly one constructor with a single argument of a type that can be bound can also be bound with argument injection. This is especially useful for collection customization:
This technique can also be used for additional initialization or settings data preprocessing:
Arrays and lists:
T[]
List<T>
IEnumerable<T>
(backed by T[]
)
IReadOnlyList<T>
(backed by T[]
)
IReadOnlyCollection<T>
(backed by T[]
)
ICollection<T>
(backed by List<T>
)
IList<T>
(backed by List<T>
)
Dictionaries:
Dictionary<TKey, TValue>
IDictionary<TKey, TValue>
(backed by Dictionary
)
IReadOnlyDictionary<TKey, TValue>
(backed by Dictionary
)
Sets:
HashSet<T>
ISet<T>
(backed by HashSet
)
Collections require binders defined for element types (both keys and values in case of dictionaries).
Nulls are not valid as dictionary keys.
Any element binding failure results in complete collection binding failure.
Binders to objects. There are built-in binders for , , . One can also extend the library by .
Collections can have and other collections as elements.
or node. Empty nodes are converted to empty collections.
An empty collection is returned unless .
A custom element comparer (such as StringComparer.OrdinalIgnoreCase
for case-insensitive dictionary keys) can be achieved by wrapping the collection in a custom type and utilizing the .
Primitive types are parsed from string values in value nodes.
Type
Allowed format examples
string
Literally any string.
char
Literally any single character.
bool
true
, True
, TRUE
byte, sbyte
Anything built-in TryParse
method can grok.
short, ushort
Anything built-in TryParse
method can grok.
int, uint
Anything built-in TryParse
method can grok.
long, ulong
Anything built-in TryParse
method can grok.
float, double, decimal
1.23
, 1,23
, 5,12e2
Guid
Anything built-in TryParse
method can grok.
Uri
http://example.com
, example.com/some
, /part/of/path
TimeSpan
00:12:34
, 2 seconds
, 500 ms
, 1.5 days
, 10s
, 0.5 minutes
DateTime(Offset)
2018-03-14 15:09:26.535
, 20050809T181142+0330
IPAddress
127.0.0.1
, 2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d
IPEndPoint
192.168.1.10:80
453453
(just bytes), 1 kb
, 24.3 megabytes
, 500 TB
500
(just bytes/sec), 200 kilobytes/second
, 5 GB/sec
, 80 mb/s
Encoding
utf-8
, us-ascii
Enum
types
Anything built-in TryParse
method can grok.
Nullable structs
A valid value or null
.
DataSize and DataRate are custom new types. They also provide factory extensions and operators:
There's also support for types that implement Parse
or TryParse
method with standard signature:
This allows to use arbitrary types with string representation without resorting to implementation of custom binders.
Value node or a container (array/object) node with a single value node child.
Default value for the type is used unless explictly required.
Explicitly specified null
string value has the same effect.
Parsing errors arising from incorrect value formats lead to binder failure and result in exceptions, even for optional members (see classes and structs for more context).