Freemarker "Expected a hash, but this evaluated to a string" - freemarker

I'm using Freemarker as an addition with OpenOffice, to process documents with variables.
Therefore, in this case, I want an adress with either a contact person (if there is one) or just the normal contact details (adress).
[#function GetAdr]
[#return (recipient)!"unbekannt" /]
[/#function]
[#function GetAdrWithContact]
[#return (GetAdr().contact)!"unbekannt" /]
[/#function]
[#if (GetAdrWithContact().adress)?? ]
[#if GetAdr().name??]${(GetAdr().name)?xml}<text:line-break />[/#if][#Adr GetAdrWithContact() /]
[#elseif (GetAdr().adress)?? ][#Adr GetAdr() /] [/#if]
[/#macro]
If I want to use this code, I get the
Expected a hash, but this evaluated to a string
error, only for the case if there is no contact person though. It works, as long as there is a contact person.
Edit: I tried to check GetAdrWithContact().adress) with ?has_content or ?is_string, neither worked.

The ! operator (and ?has_content) only handless nulls and missing values, not type errors. And indeed it's strange when you chose a default value that differs in type from what would be there when the value isn't null. Certainly you should use {} (empty hash) as the default, or something like that. Something that's similar to the real thing. Then GetAdr().name?? and such won't be an error.

Related

Assigning empty string if XML node doesn't exist in Freemarker

I have an XML document passed as root to a Freemarker template. I want some values from this XML to be assigned to variables as a string and later concatenate/print them out.
<#assign MyVar = root.child1.child2.child3.mynode>
The issue here is that even when a path doesn't exist MyVar gets assigned with a sequence+hash which cannot be printed out or converted to string. This variable although returns false for ?has_content, it needs an extra step for these checks and I have this same issue with many variables and across template files and modules.
The only solution I have been able to find was
<#assign MyVar = root.child1.child2.child3.mynode>
<#assign MyVar = MyVar ?has_content?then(MyVar , "")>
I am looking for something like the Default Value Operator which also checks for nulls like ?has_content.
Does Freemarker provide any simpler one line function to check if a variable has no content and assign it with a default?
In short:
<#assign myVar = root.child1.child2.child3.mynode[0]!''>
Or just <#assign myVar = root.child1.child2.child3.mynode[0]!> if the implicit multi-typed default value doesn't cause problems (like when you just print it with ${}).
Why: XML queries (just like XPath queries) always return a sequence of matching nodes. There are maybe 0 such nodes (or 1, or multiple). An empty sequence is not a "missing value" according the template language. It's an inconvenient mismatch with the XML data-model. But while the sequence always exists, its 1st element ([0]) doesn't, so you can use all the missing value handler operators with it as usual.

FreeMarker Java 8 Optional value support

Does FreeMarker support Optional value in Java 8?
e.g. I have String id, and its getter method is like:
public Optional<String> getId() {
return Optional.ofNullable(Id);
}
How am I going to reference it in the .ftl file. It seems that {data.id} can not find the correct Optional value. But gives me Optional[1334586]
Well, Freemarker is not supposed to be aware of Optional or it is better to say that its dynamically typed so it works for any object.
Since you calling ${data.id} it's just calls toString on Optional which is totally expected behavior.
If you want to handle null values in your template and for that you want to use Optional, you may choose to set a default value if null, so Optional usage won't be needed:
Synopsis: unsafe_expr!default_expr or unsafe_expr! or (unsafe_expr)!default_expr or (unsafe_expr)!
Example: ${data.id!"No id."}
Or check if it's exists:
<#if data?? && data.id??>
Id found
<#else>
Id not found
</#if>
For more info check out the Freemarker docs. Specifically parts: Handling missing values and Missing value test operator.
If you just want to get the value from Optional in your template:
${data.id.orElse('')}
or
${data.id.get()!''}

Freemarker - default value for variable that may be missing or blank?

In Freemarker, I want to treat something that may be missing (not defined on given object or in given Map), have a null value, be an empty string "" or a string with whitespace only (" \t\n"), AKA blank string. Or a real value like "foo".
In case it's anything besides a non-blank string, I want the expression to return a default value.
How can I handle this in Freemarker?
So far, this is what I use:
${ obj.propery???then(obj.property?trim!"default") }
But I can imagine something more ellegant, like
${ obj.property!!?trim!"default" }
and even with trimming (which is quite common operation for templates):
${ obj.property!!!"default" }
Is there something such in Freemarker? (Besides coding my own method or macro.)
There's nothing in FreeMarker for that, at least not in 2.3.24. I think the best way to address that is wiriting a trimToNull function, and then you can write trimToNull(obj.property)!"default" and trimToNull(obj.property)??, etc.
I had the same challenge and came to this solution:
${ (obj.property!"")?trim }
It also trims the empty string but is easier to read than writing an function or a if.

Show default content in a template if an object is nil otherwise show based on the set property

In my template, I would like to include some default meta tags (90% of the time). However, when a specific property is set, I would like to show a different set of text.
I know I can set an anonymous struct and set a property with either "default" or "some-x". However, this means, I need to add an anonymous struct to 90% of my handlers that just currently pass nil.
Is there way to do something like
{{if eq . nil}}
// default meta tag
{{else if eq .MetaValue "some-x"}}
//other
{{end}}
If I try something like my above code, it compiles but doesn't do what I want. Appreciate any suggestions on how to handle it properly without adding a lot of boiler plate.
Thanks!
{{if not .}}
output when . is nil or otherwise empty including
false, 0, and any array, slice, map, or string of length zero
{{else if eq .MetaValue "some-x"}}
// some-x case
{{else}}
// other case
{{end}}
If you want to ensure you're only checking against nil and not 0, false, the empty string, or any other falsey type, you can use the kindIs function to accomplish this.
{{ if kindIs "invalid" . }}
// only if variable is literally nil. falsey values will fallthrough.
{{ else if eq .MetaValue "some-x" }}
// other
{{ else }}
// final case, if any
{{ end }}
I've been recently facing an issue with identifying nil vs 0 values in a Helm Chart (which uses Go templates, including sprig) and haven't found any solutions posted, so I thought I'd add mine here.
I came up with a kind of ugly solution which is to quote the value and then check for a string that matches "<nil>" (with quotes, so you'd actually be checking (quote .Values.thing | eq "\"<nil>\"")). This allows differentiating tests against empty values vs defined 0 values. In my case, I was trying to build a config file where some default options were non-0, so when 0 was explicitly set, I wanted to know that 0 was set instead of just omitted.
Hopefully this can be a help to someone else.
It would be nice to have a better way to do this, but so far I haven't found anything that doesn't require creating and adding my own template functions.

Freemarker: assign interpreted variable to other variable

I would like to assign the result of an interpreted variable to another varialbe.
Freemarker provides the built-in ?interpret to interpret a variable holding an ftl expression. See http://freemarker.sourceforge.net/docs/ref_builtins_expert.html#ref_builtin_interpret
If I do
[#if var1?has_content && var1?starts_with(r"${")]
[#assign interpretedValue = var1?interpret!""]
[#interpretedValue/]
[/#if]
The [#interpretedValue/] will output the interpreted value.
However, I'd like to assign the value of the interpreted value to a variable (in order to do some things such as ?has_content in the rest of my code). I tried [#assign varInterpretedValue = #interpretedValue] but this does not work.
Is this possible?
Yes, like this:
[#assign capturedOutput][#(var1!'')?interpret /][/#assign]
${capturedOutput} [#-- Attention! Put this into #noescape if you are inside #escape! --]
Note that the !'' suff has to before the ?interpret, otherwise it doesn't do anything (since the result of ?interpret is always non-null).

Resources