Lyo.Formatter
1.0.13
dotnet add package Lyo.Formatter --version 1.0.13
NuGet\Install-Package Lyo.Formatter -Version 1.0.13
<PackageReference Include="Lyo.Formatter" Version="1.0.13" />
<PackageVersion Include="Lyo.Formatter" Version="1.0.13" />
<PackageReference Include="Lyo.Formatter" />
paket add Lyo.Formatter --version 1.0.13
#r "nuget: Lyo.Formatter, 1.0.13"
#:package Lyo.Formatter@1.0.13
#addin nuget:?package=Lyo.Formatter&version=1.0.13
#tool nuget:?package=Lyo.Formatter&version=1.0.13
Lyo.Formatter
SmartFormat.NET templating for user-defined strings: named placeholders, lists, pluralization, and culture-aware formatting, plus a C# subset for DateTime math, ternary, and in-memory LINQ. Built for validate-then-format pipelines. IFormatterService is what Lyo.Api computed fields, Lyo.Job.Worker string parameters, and Lyo.Web.Automation step templates call.
Examples
Register services
using Lyo.Formatter;
using Microsoft.Extensions.DependencyInjection;
services.AddFormatterService();
// Or: services.AddFormatterService(sp => /* custom SmartFormatter */);
ITemplate workflow
var t = formatter.CreateTemplate("{Title} — {Count}")
.WithValue("Title", doc.Title)
.WithValue("Count", doc.Count);
if (!t.TryValidate(out var err))
throw new InvalidOperationException(err);
if (!t.TryValidateContext(out var ctxErr))
throw new InvalidOperationException(ctxErr);
var text = t.Format();
When to use this package
- Turn stored templates (
"{User.Name} {Order.Total:C}") into final text with one or more context objects. - Validate templates before persisting them (
ValidateTemplate,TryValidateTemplate). - List placeholders for dependency analysis (
GetPlaceholders,GetUnresolvedPlaceholders,AllPlaceholdersResolved). - Build context with
IContextBuilder(dates, conditional keys, custom formatters).
Registration
Register FormatterService as a singleton and expose IFormatterService. Use the factory overload when you need extra SmartFormat extensions or custom SmartSettings.
Core types
| Type | Role |
|---|---|
IFormatterService |
Format, validate, inspect placeholders, wrap templates as ITemplate, emit annotated FormatSegments. |
FormatterService |
Default implementation. Uses FormatErrorAction.MaintainTokens so missing data leaves {tokens} in output, which is how unresolved-placeholder detection works. Placeholder matching is case-insensitive. |
ITemplate |
Parse-once workflow: WithContext, AddContext, TryValidateContext, then Format(). |
IContextBuilder |
Dictionary builder passed to Format(template, configure). |
FormatterSegment / FormatterSegmentKind |
Annotated span from FormatSegments: literal text, a resolved replacement, or an unresolved {token}, plus the placeholder key and raw template substring. |
Formatting overloads
Format(template, object? context). One DTO, anonymous object, or any type SmartFormat can reflect over.Format(template, params object?[] contextItems). Multiple sources. Later objects win on duplicate names.Format(template, IReadOnlyDictionary<string, object?>). Explicit name/value map.Format(template, Action<IContextBuilder>). Build the map withAdd,AddIf,AddWhen, typed format strings, or customFunc<,>formatters.
Validation and placeholders
ValidateTemplate/TryValidateTemplate. Parser pass. Catches syntax errors before you save a template. Unknown context identifiers are not syntax errors.TryFormat. Swallows exceptions from SmartFormat and returns false. Prefer validation plus known context.GetPlaceholders. Selector paths and expression member paths the host must supply (amount,lastSuccessJobRun.Timestamp). Drops type names (DateTime) and lambda parameters.AllPlaceholdersResolved/GetUnresolvedPlaceholders. Compare template to formatted output. Relies onMaintainTokensso missing keys stay visible as{Name}.FormatSegments. Walks the parsed template into orderedFormatterSegmentspans (literal / placeholder / unresolved) so UIs can color-link{Name}to its replacement without a second parser.
ITemplate workflow
Use AddContext on the template to layer IContextBuilder steps without allocating a full dictionary at the call site. Format(additionalContext) merges a one-off
context (dictionary or object) on top of the accumulated state for a single render.
ITemplate.TryValidateContext succeeds when the accumulated context keys cover every placeholder name (or supply a parent path like Order for {Order.Total}). Bare CLR objects
passed via WithContext(object?) do not participate in this check (only the merged dictionary and dictionary-shaped extras do), so call WithValue/AddContext or supply a
dictionary when you want the validator to confirm coverage.
SmartFormat behavior
This library does not fork SmartFormat. Simple selectors ({Name}, {Count:N0}, {Items:list:{}|, }) still go through a configured SmartFormatter. See the SmartFormat documentation for list formatting, plural rules, and built-in extensions. {{ / }} emit a literal { / } — they are not a second placeholder form. Write {Name}, not {{Name}}. Lyo.Web.Automation step templates use single-brace placeholders ({page.url}). Legacy {{page.url}} is normalized there.
Expressions
Tokens that are not a plain SmartFormat selector (ternary ? :, =>, comparisons, arithmetic, this., DateTime / TimeSpan / Math / Convert / LINQ) are evaluated with DynamicExpresso against the same context bag. Missing data and unknown methods leave the raw {...} (MaintainTokens). TryValidateTemplate returns syntax errors for the editor; unknown context names (Order in {Order.Total > 6}) are not syntax errors.
Clock (injectable Func<DateTimeOffset> on FormatterService, default real time): {DateTime.Now}, {DateTime.UtcNow}, {DateTime.Today}, {DateTimeOffset.Now} / UtcNow. Offset: {DateTime.Now.AddDays(-1)}, {DateTime.UtcNow - TimeSpan.FromHours(24)}. Format the result with a SmartFormat spec: {DateTime.Now.AddDays(-1):yyyy-MM-dd}.
Context: {amount}, {this.amount}, {client.contact.emailAddress}. Nested {DateTime.UtcNow - {lastSuccessJobRun.Timestamp}} is rewritten innermost-first to keep DateTime types.
Logic / strings: {this.amount > 2 ? "true" : "false"}, {this.nickname ?? this.name}, {string.IsNullOrWhiteSpace(this.nickname) ? this.name : this.nickname}, {string.Join(", ", this.items.Select(x => x.Name))}, {this.items[0].Name}, {Convert.ToInt32(this.qty)}.
LINQ (in-memory Enumerable only): Where / Select / Count / Any / Sum / First / OrderBy / Take / ToList, and the rest of the usual in-memory operators. Not IQueryable, not SQL.
Not in this pass: assignment, new of arbitrary types, extra assemblies, {client.Delete()}, currentTimestamp / 24hrs aliases (use DateTime.* / TimeSpan.*).
WASM
Default Blazor WASM (IL interpreted, no AOT) can run expressions: DynamicExpresso interprets expression trees and does not emit an assembly. Live preview in Lyo.Formatter.Web.Components stays in-process. WASM AOT and Native AOT cannot run the expression engine (no AOT support in DynamicExpresso). SmartFormat-only placeholders still work there; failed expression eval leaves the {token}.
Integration points
Lyo.Api. OptionalIFormatterServiceforComputedFieldson projection/query responses (SmartFormat and expressions over projected rows).Lyo.Job.Worker. OptionalIFormatterServicefor in-memory string parameter placeholders ({jobrun.parameters.startdate},{client.contact.emailAddress},{DateTime.UtcNow.AddDays(-1):yyyy-MM-dd}).Lyo.Web.Automation. OptionalIFormatterServiceto validate automation plans before execution.Lyo.Formatter.Web.Components. Live template editor and annotated preview (FormatSegments). Works on WASM.
Thread safety
FormatterService is safe for concurrent reads if you do not mutate SmartFormatter or Culture from multiple threads without synchronization. Typical ASP.NET Core registration as a singleton treats Culture as ambient per request by setting it at the start of a request. Or leave Culture alone on the shared instance and pass culture-aware data in context instead.
Dependencies
Generated from ProjectReference / PackageReference (same model as docs/Lyo.ProjectGraph.html).
Lyo.Common(direct, lyo)Lyo.Exceptions(direct, lyo)DynamicExpresso.Core2.19.3(direct, third-party)Microsoft.Extensions.DependencyInjection.Abstractions10.0.5(direct, microsoft)SmartFormat.NET3.6.1(direct, third-party)Microsoft.Extensions.Logging.Abstractions10.0.5(transitive, microsoft)System.Memory4.6.3(transitive, microsoft, netstandard2.0)System.Text.Json10.0.5(transitive, microsoft, netstandard2.0)
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. 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 was computed. 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. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .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
- DynamicExpresso.Core (>= 2.19.3)
- Lyo.Common (>= 1.0.13)
- Lyo.Exceptions (>= 1.0.13)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
- SmartFormat.NET (>= 3.6.1)
-
net10.0
- DynamicExpresso.Core (>= 2.19.3)
- Lyo.Common (>= 1.0.13)
- Lyo.Exceptions (>= 1.0.13)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
- SmartFormat.NET (>= 3.6.1)
NuGet packages (5)
Showing the top 5 NuGet packages that depend on Lyo.Formatter:
| Package | Downloads |
|---|---|
|
Lyo.Api
Core API library for building RESTful APIs with Entity Framework Core, caching, and mapping support. |
|
|
Lyo.Web.Automation
Shared browser automation models: element locators, JSON automation plans, session abstraction, and plan runners (engine-neutral). |
|
|
Lyo.Job.Worker
Worker SDK for the Lyo job system. Provides a base class that handles job lifecycle (fetch, start, execute, finish, cancellation) so workers only implement ExecuteAsync. |
|
|
Lyo.Job.Scheduler
Job scheduling and execution service dispatching jobs via RabbitMQ message queue. |
|
|
Lyo.Formatter.Web.Components
WASM-safe Blazor components for live SmartFormat template editing and annotated preview using Lyo.Formatter. |
GitHub repositories
This package is not used by any popular GitHub repositories.