简体   繁体   中英

Freemarker call static java method from java.lang.Integer

i have a question. I would like to call a static java method Integer.toHexString() in a freemaker template. In my code i implemented following lines:

....
cfg.setSharedVariable("Integer",BeansWrapper.getDefaultInstance().
                                getStaticModels().
                                get("java.lang.Integer");
....
);

My template looks like this

<#list Items.iterator() as var>
  <#assign hex = Integer.toHexString(var) />
  ${hex}
</#list>

But if i execute the code i get following error:

freemarker.core._TemplateModelException: An error has occurred when reading existing sub-variable "Integer"; see cause exception! The type of the containing value was: extended_hash+string (org.json.JSONObject wrapped into f.e.b.StringModel)

----
FTL stack trace ("~" means nesting-related):
    - Failed at: #assign hex = Integer.toHexString(var...  [in template "decToHex.ftl"...]

What am i doing wrong? Thanks.

Based on the error message, your data-model (aka. template context) is a org.json.JSONObject . FreeMarker doesn't know that API, but it discovers that JSONObject has a get(String) method, and tries to use that. Unfortunately, that get method doesn't behave as a Map -style get(key) . FreeMarker first calls JSONObject.get("Integer") to see if the variable is in a data-model. If it isn't, it expects a null to be returned, and then will try to get it from higher scopes (like from there shared variables). But JSONObject.get(String) throws JSONException instead of returning null , which is what you see in the error log (if you look at the whole stack trace, JSONException should be there as the cause exception).

To solve this, you need teach FreeMarker how to deal with JSONObject -s:

  1. Create a class, let's call it JSONObjectAdapter , which implements TemplateHashModelEx2 (or, the much simpler TemplateHashModel can be enough for basic use-cases). Inside that, when implementing TemplateHashModel.get(String) , you must call JSONObject.has(key) to check if the key exists, and if not, return null , otherwise continue with calling JSONObject.get(key) .

  2. Create a class, let's call it DefaultObjectWrapperWithJSONSupport , that extends DefaultObjectWrapper . Your class should wrap JSONObject -s with JSONObjectAdapter .

  3. Where you already configure FreeMarker (NOT before each template processing), specify the objectWrapper to be a DefaultObjectWrapperWithJSONSupport .

There's a few non-obvious things with doing above properly, so I strongly recommend starting out from this example: https://freemarker.apache.org/docs/pgui_datamodel_objectWrapper.html#pgui_datamodel_customObjectWrappingExample

Above linked example does the above 3 steps, but to support the Tupple class, instead of JSONObject . That's a TemplateSequenceModel , instead of TemplateHashModel , but otherwise what has to be done is very similar.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM