Skip to content

Instantly share code, notes, and snippets.

@lucasff
Created November 15, 2021 02:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lucasff/8fae1368387a1f1bd9673c45cb3d570d to your computer and use it in GitHub Desktop.
Save lucasff/8fae1368387a1f1bd9673c45cb3d570d to your computer and use it in GitHub Desktop.
FreeMarker FTL json
#-- The black_list contains bad hash keys. Any hash key which matches a
-- black_list entry is prevented from being displayed.
-->
<#assign black_list = [
"class",
"getclass",
"getmethod",
"getenclosingmethod",
"getcanonicalname",
"hashcode",
"getproviders",
"getdeclaredfields",
"cast",
"equals",
"tostring",
"isdisplayinfo",
"islocalclass",
"isinstance",
"isinterface",
"issynthetic",
"assubclass",
"getfield",
"getcomponenttype",
"isarray",
"isprimitive",
"isannotation",
"getdeclaredconstructors",
"isassignableform",
"isassignablefrom",
"getdeclaringclass",
"package"
] />
<#--
-- The main macro.
-->
<#macro stringify data depth=0>
<#compress>
<#if depth gt 6>
/* stringify: refusing to run ${depth} */
<#return>
</#if>
<#--
-- Test for hashes/objects
-->
<#local isHash = false>
<#attempt>
<#local isHash = data?is_hash || data?is_hash_ex>
<#recover>
/* can't evaluate if object is hash */
undefined
<#return>
</#attempt>
<#if isHash>
<#attempt>
<@printHashEx data depth+1 />,
<#recover>
undefined
/* printHashEx failed */
<#return>
</#attempt>
</#if>
<#--
-- Test for arrays
-->
<#local isEnumerable = false>
<#attempt>
<#local isEnumerable = data?is_enumerable>
<#--/* stringify: data is enumerable? ${isEnumerable?c} */-->
<#recover>
undefined
/* can't evaluate if object is enumerable */
<#return>
</#attempt>
<#if isEnumerable>
<#attempt>
<@printList data />,
<#recover>
undefined
/* printList failed */
<#return>
</#attempt>
</#if>
<#local isMethod = false>
<#attempt>
<#local isMethod = data?is_method>
<#--/* stringify: data is a method? ${isMethod?c} */-->
<#recover>
/* can't test if object is a method */
undefined
<#return>
</#attempt>
<#if isMethod>
undefined
</#if>
<#local isBoolean = false>
<#attempt>
<#local isBoolean = data?is_boolean>
<#--/* stringify: data is a boolean? ${isBoolean?c} */-->
<#recover>
/* can't test if object is a boolean */
undefined
<#return>
</#attempt>
<#if isBoolean>
${data?c}
</#if>
<#local isString = false>
<#attempt>
<#local isString = data?is_string>
<#--/* stringify: data is a boolean? ${isBoolean?c} */-->
<#recover>
/* can't test if object is a boolean */
<#return>
</#attempt>
<#if isString>
<#if data?lower_case?index_of("bean") gte 0>
<#return>
</#if>
<#t>"${data?json_string}"
</#if>
</#compress>
</#macro>
<#macro debug data>
<#attempt>
/* debug: hasContent? ${data?has_content?c} */
/* debug: hasApi? ${data?has_api?c} */
/* debug: isHash? ${data?is_hash?c} */
/* debug: isEnumerable? ${data?is_enumerable?c} */
/* debug: isString? ${data?is_string?c} */
/* debug: isBoolean? ${data?is_boolean?c} */
/* debug: isMethod? ${data?is_method?c} */
/* debug: isSequence? ${data?is_sequence?c} */
/* debug: isCollection? ${data?is_collection?c} */
/* debug: isCollectionEx? ${data?is_collection_ex?c} */
<#recover>
/* can't debug */
<#return>
</#attempt>
</#macro>
<#macro rawStringify data>
/* called rawStringify /*
<#if data?is_enumerable>
<@printList data />
<#elseif data?is_hash>
<@printHashEx data />
</#if>
</#macro>
<#macro printList data>
/* printList start */
<#list data>
[${'\n'}
<#items as item>
<#attempt>
<@printListItem item />
<#recover>
/* printList: couldn't call printListItem */
<#return>
</#attempt>
<#--"${item}"-->
<#sep>,</#sep>
<#--/* isString: "${item?is_string?c}" */-->
<#--/* isMethod: "${item?is_boolean?c}" */-->
<#--/* isBoolean: "${item?is_boolean?c}" */-->
</#items>
]${'\n'}
<#else>
/* printList empty! */
[]
</#list>
/* printList end */
</#macro>
<#macro printHashEx hash depth=0>
${'\t'}
<#if depth gt 7>
/* printHashEx: refusing to run ${depth} */
<#return>
</#if>
<#t>{
<#list hash as key, item>
<#if omit(key?string)>
<#continue>
</#if>
"${key}":<@stringify item!'' depth+1 /><#sep>,</#sep>
<#--"${key}":<#if item??><@printHashItem item key depth /></#if>-->
</#list>}
</#macro>
<#macro printHashItem item key depth>
<#if depth gt 7>
/* printHashItem: refusing to run ${depth} */
undefined
<#return>
</#if>
/* call printHashItem with ${key} */
<#if omit(key?string)>
/* omit: ${omit(key?string)?c} */
undefined
<#return>
</#if>
<#if item?is_enumerable>
/* printHashItem -> printList */
<#t>"${key?js_string}":<@printList item />
<#elseif item?is_hash_ex && omit(key?string)><#-- omit bean-wrapped java.lang.Class objects -->
undefined
<#elseif item?is_hash>
/* printHashItem -> printHashEx */
"${key?js_string}":<@printHashEx item depth+1 />
<#elseif item?is_number>
<#t>"${key?js_string}":${item?string.computer}
<#elseif item?is_string>
<#t>"${key?js_string}":"${item?js_string}"
<#elseif item?is_boolean>
<#t>"${key?js_string}":${item?c}
<#elseif item?is_date>
<#t>"${key?js_string}":"${item?string("yyyy-MM-dd'T'HH:mm:sszzzz")}"
</#if>
</#macro>
<#macro printListItem item>
/* printListItem */
<#if item?is_enumerable>
/* printListItem -> printList */
<#t><@printList item />
<#elseif item?is_hash_ex>
/* printListItem -> printHashEx */
<@printHashEx item />
<#elseif item?is_number>
<#t>${item?string.computer}
<#elseif item?is_string>
<#t>"${item?js_string}"
<#elseif item?is_boolean>
<#t>"${item?c}"
<#elseif item?is_date>
<#t>"${item?string("yyyy-MM-dd'T'HH:mm:sszzzz")}"
</#if>
</#macro>
<#function omit key>
<#local what = key?lower_case>
<#list black_list as item>
<#if what?index_of(item) gte 0>
<#return true>
</#if>
</#list>
<#return false>
</#function>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment