I have this program
private static void RunUsingILiquidizable()
{
const string templateString = @"TopInt prop: '{{TopInt}}'; Child.Prop prop: '{{L1Prop.L1Int}}'; Child.Child.Prop prop: '{{L1Prop.L2Prop.L2Int}}'; Dict item: '{{ExtendedProps.Key1}}'";
Template.NamingConvention = new CSharpNamingConvention();
Template.RegisterValueTypeTransformer(typeof(DateTime), (v) => ((DateTime)v).ToString("MM=dd=yy"));
var t = Template.Parse(templateString);
var model = new TopModel()
{
TopInt = 23,
L1Prop = new L1Model()
{
L1Int = 34,
L2Prop = new L2Model() { L2Int = 98 }
},
ExtendedProps = new Dictionary<string, object>() { { "Key1", DateTime.Now } }
};
string output = t.Render(Hash.FromAnonymousObject(model));
Console.WriteLine("RunUsingILiquidizable -->" + output);
}
Top Model definition:
public class TopModel : ILiquidizable
{
public int TopInt { get; set; }
public L1Model L1Prop { get; set; }
public Dictionary<string, object> ExtendedProps { get; set; }
public object ToLiquid()
{
return new { TopInt, L1Prop, ExtendedProps };
}
}
Output:
RunUsingILiquidizable -->TopInt prop: '23'; Child.Prop prop: '34'; Child.Child.P rop prop: '98'; Dict item: '08=27=19'
My problem is - Template.RegisterValueTypeTransformer
does global type format and template.Render(Hash.FromAnonymousObject(model), MyFormatProvider)
formats all dates, numbers the same.
What I need is to format each specific token differently when needed by supplying format. Especially important for dictionary ExtendedProps
.
I tried to do filters as well but is there a way to pass something like {{ExtendedProps.Key1 | SpecialFormat("dd--MM")}}
??
I encourage you to examine the source code for the StandardFilters
class. Custom filters can do more than the wiki docs would have you believe, and the standard filters demonstrate much of it. If there's something you've seen the standard filters do, you can write a custom filter that does the same thing.
Here's an example custom_date
filter:
public static class CustomFilter
{
public static string CustomDate(Context context, object input, string format = null, string culture = null)
{
if (input is DateTime dt)
{
IFormatProvider formatProvider = !string.IsNullOrEmpty(culture)
? CultureInfo.GetCultureInfo(culture)
: context.FormatProvider;
return dt.ToString(format, formatProvider);
}
return null;
}
}
Points of interest:
It accepts an initial Context
parameter, which includes a FormatProvider
property of type IFormatProvider
(which CultureInfo
implements). You only need to include this parameter if you need something from it.
It accepts multiple parameters:
a. A format, like certain ToString
overloads, and just passes it through.
b. A locale for which it retrieves the CultureInfo
or, if null
or ""
, defaults to context.FormatProvider
, which is either the optional IFormatProvider
passed to Render
or the current culture.
The parameters are defaulted. This is probably a good idea in general because there's no way to make them required in the template.
Example usage
Template.Register(typeof(CustomFilter));
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
var template = Template.Parse("Today is {{ today | custom_date: 'd' }} (current culture), {{ today | custom_date: 'd', 'fr-CA' }} (fr-CA), and {{ today | custom_date: 'd', elsewhere }} ({{ elsewhere }}).");
string output = template.Render(Hash.FromAnonymousObject(new { today = DateTime.Today, elsewhere = "de-DE" }));
Output
Today is 8/30/2019 (current culture), 2019-08-30 (fr-CA), and 30.08.2019 (de-DE).
More points of interest:
The parameters are specified after a colon and separated by a comma if there's more than one.
Values in the "hash" that you pass to Render
can be referenced in the parameters you pass (e.g. elsewhere
).
The "d"
standard format is sensitive to the culture so you can see how the current culture (en-US
), a hard-coded culture (fr-CA
), and a parameterized culture (de-DE
) can all be used.