StringTokenFormatter 7.3.0
See the version list below for details.
dotnet add package StringTokenFormatter --version 7.3.0
NuGet\Install-Package StringTokenFormatter -Version 7.3.0
<PackageReference Include="StringTokenFormatter" Version="7.3.0" />
paket add StringTokenFormatter --version 7.3.0
#r "nuget: StringTokenFormatter, 7.3.0"
// Install StringTokenFormatter as a Cake Addin #addin nuget:?package=StringTokenFormatter&version=7.3.0 // Install StringTokenFormatter as a Cake Tool #tool nuget:?package=StringTokenFormatter&version=7.3.0
StringTokenFormatter v7.3
This library provides token replacement for interpolated strings not known at compile time such as those retrieved from data stores (file system, database, API, config files etc) and offers support for a variety of token to value mappers.
Available on nuget.org at https://www.nuget.org/packages/StringTokenFormatter.
using StringTokenFormatter;
string interpolatedString = "Hello {FirstName} {LastName}";
var client = new {
FirstName = "John",
LastName = "Smith",
};
string message = interpolatedString.FormatFromObject(client);
.NET versions
.NET 6, 7, 8 and .NET Standard 2.0 with C# 10 language features
Migrating from version 6
There are major breaking changes. See the v6 migration page for details on how to upgrade from version 6 to version 7.
Features overview
As well as string
extensions, there are equivalent Uri
extensions and a reusable Resolver class which allows for easier sharing of custom settings.
string source = "Answer is {percent,10:P}";
var resolver = new InterpolatedStringResolver(StringTokenFormatterSettings.Default);
string actual = resolver.FromTuples(source, ("percent", 1.2));
Assert.Equal("Answer is 120.00%", actual);
Tokens with formatting and alignment can be specified in the same way as string.Format
(.net docs). Alternative token syntax can be selected in the settings.
Nested tokens (like prefix.name
), cascading containers and other complex token resolution setups are supported through the CompositeTokenValueContainer
, see Building composite containers for the helper class.
Conditional blocks of text can be controlled through boolean token values and the conditional syntax, for example:
string original = "start {if>IsValid}{middle}{ifend>IsValid} end";
var tokenValues = new { Middle = "center", IsValid = true };
string result = original.FormatFromObject(tokenValues);
Assert.Equal("start center end", result);
The Value Converter settings provide Lazy
loading and function-resolved values and can be extended to perform custom conversion logic after token matching but before formatting. Any value container can return a Lazy
or Func
which will be resolved before formatting.
See also additional features and notes for performance optimization strategies and advanced usage.
Value containers
Using properties of an object (including an anonymous object) to resolve tokens:
string original = "start {middle} end";
var tokenValues = new { Middle = "center" };
string result = original.FormatFromObject(tokenValues);
Assert.Equal("start center end", result);
Using a dictionary of values or other implementation of IEnumerable<KeyValuePair<string, object>>
to resolve tokens:
string original = "start {middle} end";
var tokenValues = new Dictionary<string, object> { { "middle", "center" } };
string result = original.FormatFromPairs(tokenValues);
Assert.Equal("start center end", result);
Using an enumerable of ValueTuples
to resolve tokens:
string original = "start {middle} end";
var tokenValues = new [] { ("middle", "center") };
string result = original.FormatFromTuples(source, tokenValues);
Assert.Equal("start center end", result);
Using a single name and value to resolve tokens:
string original = "start {middle} end";
string result = original.FormatFromSingle("middle", "center");
Assert.Equal("start center end", result);
Using a function to resolve tokens:
string original = "start {middle} end";
Func<string, object> func = (token) => { return "center"; };
string result = original.FormatFromFunc("middle", func);
Assert.Equal("start center end", result);
See building composite token value containers for hierarchical or cascading containers. Also custom containers.
Note: comma (,) and colon (:) should not be used in token names to avoid confusion with alignment and format values.
Settings
All interpolating methods accept an optional StringTokenFormatterSettings
parameter which is used in preference to the StringTokenFormatterSettings.Global
settings.
The settings record is immutable so the with
keyword is used to mutate the settings, so for example to replace the global settings, something like the following can be used:
StringTokenFormatterSettings.Global = StringTokenFormatterSettings.Global with { Syntax = CommonTokenSyntax.Round };
It should be noted that whilst overriding the global is a convenient action, it can cause side effects by other code using this library. Library implementations should not update Global
. Alternately, consider creating an instance of InterpolatedStringResolver
which takes the settings object in its constructor and provides the common methods for expanding from different ITokenValueContainer
implementations.
Creating Settings instances
Using the Global
settings as the base:
var customSettings = StringTokenFormatterSettings.Global with { Syntax = CommonTokenSyntax.Round };
var expanded = "This interpolated string uses (token) as its syntax".FormatFromSingle("token", "expanded value", customSettings);
Using the default settings as the base:
var settings1 = new StringTokenFormatterSettings { Syntax = CommonTokenSyntax.Round };
// or
var settings2 = StringTokenFormatterSettings.Default with { Syntax = CommonTokenSyntax.Round };
Initially, the Global
settings are the Default
settings.
Settings properties
Syntax
Takes a TokenSyntax
instance and defines the syntax is used for detecting tokens. Default CommonTokenSyntax.Curly
.
Build-in syntax within the CommonTokenSyntax class:
Name | Marker | Escape |
---|---|---|
Curly | {Token} |
{{ |
DollarCurly | ${Token} |
${{ |
Round | (Token) |
(( |
DollarRound | $(Token) |
$(( |
DollarRoundAlternative | $(Token) |
$$( |
Note: Token markers are case sensitive.
FormatProvider
Is used to specify the IFormatProvider
applied to token values and uses string.Format to apply formatting and alignment for example: {value,10:D4}
. Default CultureInfo.CurrentUICulture
.
NameComparer
The comparer used by ITokenValueContainer
when performing token to value look-ups. Takes a standard StringComparer
. Default StringComparer.OrdinalIgnoreCase
.
Conditions
Simple boolean conditions can be used to exclude blocks of text. ConditionStartToken
with default if>
signifies the start of the block whilst ConditionEndToken
with default ifend>
signifies the end. It is expected that after the condition prefix will be the name of the token whose boolean value dictates whether to include the block or not.
Nested conditions are supported.
Note: Condition tokens are case sensitive.
TokenResolutionPolicy
Controls how token values are handled by ITokenValueContainer
implementations. Default TokenResolutionPolicy.ResolveAll
.
The policies are:
Policy | Result |
---|---|
ResolveAll | Always uses the value returned |
IgnoreNull | Uses the value if it is not null |
IgnoreNullOrEmpty | Uses the value if it is not null and not an empty string |
What happens next will depend upon what else is configured:
- if this is a
CompositeTokenValueContainer
then the matching will cascade to the next container - if
UnresolvedTokenBehavior
setting is set toThrow
then an exception will be raised
UnresolvedTokenBehavior
Defines what should happen if the token specified in the interpolated string cannot be matched within the ITokenValueContainer
. Default UnresolvedTokenBehavior.Throw
.
Behavior | Result |
---|---|
Throw | An UnresolvedTokenException exception is raised |
LeaveUnresolved | The text will contain the original token unmodified |
InvalidFormatBehavior
Defines how string.Format exceptions are handled. Default InvalidFormatBehavior.Throw
.
Behavior | Result |
---|---|
Throw | An TokenValueFormatException exception is raised |
LeaveUnformatted | The text will contain the token value unformatted |
LeaveToken | The text will contain the original token unmodified |
ValueConverters
Applies to token values after matched and before formatting. Converters are attempted in order so that once one has successfully converted the value then no further conversions take place. Default collection (from TokenValueConverters
):
Value | Result |
---|---|
Null | no conversion |
string or ValueType |
no conversion |
Lazy<T> |
Lazy.Value |
Func<T> |
function result |
Func<string, T> |
Supplied token name function result |
They can be useful to provide post-match functionality; a great example is a when using an object which contains a property that uses a Lazy
. The token matcher resolves the token marker to property and then through the ValueConverters
calls the Lazy.Value
and returns the value of the Lazy
for formatting.
All passed through types must be handled by a Value Converters otherwise an exception is thrown.
HierarchicalDelimiter
Defines the prefix for HierarchicalTokenValueContainer
instances. Default .
(period).
See also Token Value Container Builder.
Additional features and notes
Flow of Control
When resolving the token values within an interpolated string, the following sequence is followed:
- The
InterpolatedStringParser
turns astring
into anInterpolatedString
- The
InterpolatedStringExpander
take theInterpolatedString
and processes it. For a given token- The passed
ITokenValueContainer
provides the value based on the token name - A value conversion is then attempted based on the collection of
ValueConverters
in the settings - If the token contains alignment or formatting details,
string.Format
is called with theFormatProvider
from the settings
- The passed
Reusing InterpolatedString instances
The InterpolatedStringParser.Parse
method is responsible for identifying tokens within the source string and returning the InterpolatedString
of segments. Generating the InterpolatedString
takes time but can be stored and pass multiple times to the InterpolatedStringExpander.Expand
method.
An example would be a mail merge whereby the same message text is used but with client-specific details within the ITokenValueContainer
.
See also The Resolver.
The Resolver
A helper class called InterpolatedStringResolver
exists to allow the easy reuse of custom settings without overriding the global default. An IoC container could be used to store the resolver for use throughout the application. The resolver contains the standard expansion methods and is in some ways a preferred option to using the string
extension methods.
The resolver provides methods for both expansion of tokens from string
and parsed InterpolatedString
.
Building composite token value containers
The TokenValueContainerBuilder
provides methods for creating CompositeTokenValueContainer
instances.
Note that matching attempts are made in the order that the containers are passed to the CompositeTokenValueContainer
instance which will be the same as the order that they are added to the builder. This includes nested containers.
Nested containers are supported such that {prefix.token}
first matches the prefix and then uses the associated container to match the suffix. In the example below, the prefix is Account
and the suffix Id
exists as a property on the account
object.
var account = new {
Id = 2,
Name = "The second account",
};
var builder = new TokenValueContainerBuilder(StringTokenFormatterSettings.Default);
builder.AddSingle("text", "Message text");
builder.AddNestedObject("Account", account);
var combinedContainer = builder.CombinedResult();
string interpolatedString = "Ref: {Account.Id}. {text}.";
string actual = interpolatedString.FormatFromContainer(combinedContainer);
Assert.Equal("Ref: 2. Message text.", actual);
The delimiter can changed in the settings.
Deep nesting is supported but discouraged, instead opt for flatter composites by adding the nested container to the top level with a separate prefix.
Conditions
Simple boolean conditions can be used to exclude blocks of text. The ITokenValueContainerSettings
contains the special token prefixes ConditionStartToken
(default if>
) and ConditionEndToken
(default ifend>
).
It is expected that after the condition prefix will be the name of the token whose boolean value dictates whether to include the block or not.
string original = "start {if>IsValid}{middle}{ifend>IsValid} end";
var tokenValues = new { Middle = "center", IsValid = true };
string result = original.FormatFromObject(tokenValues);
Assert.Equal("start center end", result);
Nested conditions are supported. For ConditionEndToken
, the token name suffix is purely for clarity and is optional.
The condition prefixes are case sensitive whilst the token providing the boolean value abides by the TokenResolutionPolicy
(as well as ValueConverters
).
Creating a custom ITokenValueContainer
Whilst there are a number of built-in containers, it many be necessary to create a complete custom container. The container should take in the settings interface ITokenValueContainerSettings
and obey NameComparer
and TokenResolutionPolicy
properties.
See also Token Value Container Builder.
Async loading of token values
There is no plan to support async/await within the library, the reason is that the library is designed to the CPU-bound and adding in an IO-bound layer massively changes the design and considered use-cases.
The InterpolatedString
returned by the InterpolatedStringParser
contains an extension method Tokens
which provides a unique list of tokens found within the interpolated string. These token names can be used by an async method to, for example, request the token values from a data store. The token values can be loaded into an object or IEnumerable<KeyValuePair<string, T>>
and provided as a parameter to the matching TokenValueContainerFactory
method. The InterpolatedString
and ITokenValueContainer
can then be passed to the InterpolatedStringExpander.Expand
method which in turns returns the resultant string.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
-
net6.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages (2)
Showing the top 2 NuGet packages that depend on StringTokenFormatter:
Package | Downloads |
---|---|
SimairaDigital.DevOps.Pipeline.NukeTool
NUKE build system always require pipeline build system to intiate this. |
|
FowlFever.Conjugal
Structs and annotations for producing nice word conjugations and other linguistic metadata, such as abbreviations, units of measure, and terms of venery. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
9.0.1 | 34,890 | 3/19/2024 |
9.0.0 | 1,506 | 3/10/2024 |
8.0.0 | 159,492 | 1/15/2024 |
7.3.0 | 7,145 | 12/12/2023 |
7.2.0 | 2,975 | 12/5/2023 |
7.1.0 | 3,541 | 11/13/2023 |
7.0.0 | 1,184 | 10/29/2023 |
6.1.0 | 44,485 | 6/13/2023 |
4.1.0 | 675,403 | 12/2/2020 |
4.0.0 | 254,896 | 7/19/2019 |
3.1.0 | 814,690 | 4/1/2019 |
3.0.0 | 1,929 | 3/5/2019 |
2.0.8 | 3,805 | 1/22/2019 |
2.0.7 | 11,917 | 11/2/2018 |
2.0.6 | 10,586 | 12/22/2017 |
2.0.5 | 1,576 | 12/22/2017 |
2.0.4 | 1,537 | 12/22/2017 |
2.0.3 | 7,376 | 12/4/2017 |
2.0.2 | 6,989 | 12/4/2017 |
2.0.1 | 7,020 | 12/4/2017 |
2.0.0 | 6,937 | 12/4/2017 |
1.7.0 | 1,428 | 10/31/2017 |
1.6.1 | 4,238 | 6/9/2017 |
1.6.0 | 3,476 | 12/3/2015 |
1.5.1 | 1,598 | 7/1/2015 |
1.5.0 | 1,522 | 7/1/2015 |
1.4.0 | 1,459 | 6/18/2015 |
1.3.0 | 1,738 | 1/8/2015 |
1.2.0 | 1,883 | 1/7/2015 |