Accessing data model maps that use a non-string key

This article describes how to access data model maps (such as gScopeCounts) that use non-string keys.

Details

Normally you can access a map value (e.g. rmcs counts) using something like response.resultPacket.rmcs["key"]

However, this will only work if the key is a string type.

i.e. if you look at the XML you will see the rmcs and gScopeCounts maps from the data model.

<rmcs>
	<entry>
		<string>type:Publication</string>
		<int>17</int>
	</entry>
</rmcs>
<gScopeCounts>
	<entry>
		<int>1</int>
		<int>76</int>
	</entry>
</gScopeCounts>

For the gScopeCounts field you will see that the key and value are both integers.

This is due to how Freemarker wraps Java objects within the template. See: https://freemarker.apache.org/docs/app_faq.html#faq_nonstring_keys for further information.

Example and workaround

The following freemarker macro is designed to return the gscope count given the gscope number. The gscope counts are a map, but with an integer key.

<#-- @begin Gscope facet item -->
<#---
Prints item containing a specified gscope facet and the corresponding count.
-->
<#macro gScopeFacetItem gParam facet="" category="">
	<#if response?exists && response.facets?exists>
		<#assign facetKey="f.${facet}|${gParam}"/>
		<#assign gp=gParam?number>
		<#if response.resultPacket.GScopeCounts[gp]?exists>
			<input type="checkbox" name=${facetKey}" <#if question.inputParameterMap[facetKey]?exists> checked="checked"</#if> value="${category}"> ${category} (${response.resultPacket.GScopeCounts[gp?int]})
		<#else>
			<input type="checkbox" name="${facetKey}" disabled="disabled" value="${category}"> ${category} (0)
		</#if>
	</#if>
</#macro>

However when you use the square bracket notation e.g. response.resultPacket.GScopeCounts[gParam] freemarker automatically converts the key supplied to a string meaning the check will fail.

The above code generates an error.

Expected number, sequence, or string. response.resultPacket.GScopeCounts evaluated instead to freemarker.template.SimpleHash on line 88, column 14 in conf/example/example.ftl.

Other versions of the <#if> statement also fail:

<#if response.resultPacket.GScopeCounts[gp?int]?exists>
<#if response.resultPacket.GScopeCounts(gp)?exists>
<#if response.resultPacket.GScopeCounts(gp?int)?exists>

The following is a workaround, but it’s a bit messy:

<#-- @begin Gscope facet item -->
<#---
Prints item containing a specified gscope facet and the corresponding count.
-->
<#macro gScopeFacetItem gParam facet="" category="">
	<#if response?exists && response.facets?exists>
		<#assign facetKey="f.${facet}|${gParam}"/>
		<#assign keys = response.resultPacket.GScopeCounts?keys>
		<#assign vals = response.resultPacket.GScopeCounts?values>
		<#if keys?seq_contains(gParam?number)>
			<#list keys as key>
				<#if key?number == gParam?number>
					<input type="checkbox" name=${facetKey}" <#if question.inputParameterMap[facetKey]?exists> checked="checked"</#if> value="${category}"> ${category} (${vals[key_index]})
				</#if>
			</#list>
		<#else>
			<input type="checkbox" name="${facetKey}" disabled="disabled" value="${category}"> ${category} (0)
		</#if>
	</#if>
</#macro>