Value Convertors
In the prior section, we created an AwesomeValue
object and set it in the Context. However, you can also push any object to the Context:
context.SetValue("name", "Deane");
How can we do this, if we’ve established that Fluid will only work with FluidValue
objects?
Because before Fluid adds that to a Scope dictionary, it works very hard to convert into an appropriate FluidValue
object.
First, there are a series of SetValue
extension methods for common types – one of them takes a String
and turns it into a StringValue
, etc. The C# engine will always call the most specific method it can find, and this will usually generate the appropriate FluidValue
.
But if there’s no matching extension method, Fluid has a catch-all that will do the following.
- It will serially invoke the custom value converters that you can set on
TemplateOptions.ValueConverters
. If any of these return aFluidValue
, Fluid will use the result. If any of them return anything else, it will skip to Step 2 and try to convert the result there. If a converter returnsnull
, Fluid will try the next converter in the list. - After the list of custom converters, Fluid has several common-sense hard-coded conversions. Strings turn into
StringValues
, numbers of all kinds turn intoNumberValues
, etc. If this works, Fluid will use the result. - For everything else, Fluid will wrap the value in an
ObjectValue
and use that.
The end result is that everything in a Scope dictionary will be some kind of FluidValue
object.
A converter is just a short function that takes any value and returns one of three things:
- A
FluidValue
object, meaning it was successfully converted - Some other object type, meaning this new value should continue running through the custom converters. This allows us to do “chained conversions.” This gets a little confusing, but if we want to get
A
andB
toD
, we can convert them both toC
then have a single conversion fromC
toD
null
, meaning this filter didn’t apply, so continue running the existing value through the custom converters
For example, if we decided that all instances of Person
should be AwesomeValue
objects, using the person’s name, we could do this:
TemplateOptions.Default.ValueConverters.Add((v) => {
if(v is Person person)
{
return new AwesomeValue(person.Name);
}
return null;
}
Now we can pass Person
objects into the Context and they will be auto-converted to AwesomeValue
objects before being placed into a Scope.
var deane = new Person() { Name = "Deane" });
context.SetValue("person", deane);
{{ person }}
Deane is awesome.