.netf#dotliquid

Is it possible to access a nested dictionary from Dotliquid?


I was wondering if it was possible to access a nested dictionary with the dotliquid library.

What I have got so far is this:

type Document = {
    template : string
    elements : Map<string, obj>
}with  
    static member Test = {
        template = "{% for item in test %} {{ item[\"name\"] }} {% endfor %}"
        elements = 
            Map.empty 
            |> Map.add "test" ([Map.empty |> Map.add "name" "Foo"] :> obj) 
    }

let genTemplate = 
    let doc = Document.Test
    let template = Template.Parse(doc.template)
    template.Render(Hash.FromDictionary(doc.elements |> Map.toSeq |> dict))

The expected out of this would be: Foo but somewhat unsurprisingly I simply get an empty string. if I simply access item as opposed to item["name"] I get the output ["name", "Foo"]. So is there any way to do this in dotliquid? Any help would be most appreciated.


Solution

  • A relatively easy trick is to register a filter getName which then lets you access the name property of the nested dictionary using {{ item | getName }}. A filter can be written as an F# method:

    type Filters() = 
      static member getName(map:Map<string, string>) = map.["name"]
    
    Template.RegisterFilter(typeof<Filters>)
    

    If you now use the filter in your test template. Note that depending on your configured DotLiquid naming convention, you might need to use get_name as the name (which is also the default):

    type Document = 
      { template : string
        elements : Map<string, obj> }
      static member Test = 
        { template = "{% for item in test %} {{ item | get_name }} {% endfor %}"
          elements = 
              Map.empty 
              |> Map.add "test" ([Map.empty |> Map.add "name" "Foo"] :> obj) }
    

    With this, you get the result you were expecting:

    let genTemplate = 
        let doc = Document.Test
        let template = Template.Parse(doc.template)
        template.Render(Hash.FromDictionary(doc.elements |> Map.toSeq |> dict))
    

    Unfortunately, I'm not sure if there is a way to create parameterized filters - it might be that you'll have to write something like getName for each key that you want to access.