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
ObjectValueand 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
FluidValueobject, 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
AandBtoD, we can convert them both toCthen have a single conversion fromCtoD 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.