Ітерація Freemarker по клавішах хеш-карти


87

Freemarker має два типи даних колекції, списки та хеш-карти. Чи є спосіб перебирати ключі хеш-карти так само, як це робимо зі списками?

Отже, якщо у мене є var з даними, скажімо:

user : {
  name : "user"
  email : "looser@everything.com"
  homepage : "http://nosuchpage.org"
}

Я хотів би надрукувати всі властивості користувача з їх значенням. Це недійсно, але мета зрозуміла:

<#list user.props() as prop>
  ${prop} = ${user.get(prop)}
</#list>

Відповіді:


106

Редагувати: Не використовуйте це рішення з FreeMarker 2.3.25 і новішими версіями, особливо ні .get(prop). Дивіться інші відповіді.

Ви використовуєте вбудовану функцію клавіш , наприклад, це має працювати:

<#list user?keys as prop>
    ${prop} = ${user.get(prop)}
</#list>  

4
синтаксис відрізняється в останній версії, як показано в посиланні, яке я розмістив у своїй відповіді. Я усвідомлюю, що це давнє запитання, але воно постає з високим рейтингом у Google.
Нік Спейсек,

26
лише примітка - ви можете використати це ${user[prop]}як скорочення
Божо

Це витік продуктивності: для кожного ключа йому потрібно отримати значення. Ітерація над entrySet () не має цієї проблеми.
Джеффрі Де Смет

4
має бути $ {user [prop]}
dns

При конфігурації дефолтів user[prop]робіт, наскільки propце String( в іншому випадку вам потрібно в user?api.get(prop)даний момент), але будьте обережні, деякі структури (як Struts, я вважаю) використовувати в даний час застаріле установки , де імена методів змішуються з Mapключами, і тому , якщо значення propsтрапляється будучи ім'ям методу в userоб'єкті Java, ви отримуєте метод замість того, що мали на увазі. Ось чому в цих застарілих установках вони завжди користуються user.get(prop).
ddekany

52

FYI, схоже, синтаксис отримання значень змінився відповідно до:

http://freemarker.sourceforge.net/docs/ref_builtins_hash.html

<#assign h = {"name":"mouse", "price":50}>
<#assign keys = h?keys>
<#list keys as key>${key} = ${h[key]}; </#list>

2
Чим відрізняється цей синтаксис?
Паркер,

1
хороша відповідь ;-) зауважте, що, можливо, вам доведеться перевірити наявність нуля при друку вашого значення, <#if h [key] ??> $ {key} = $ {h [key]}; </ # if>
Бред Паркс,

1
Синтаксис не змінено. І те, [key]і .get(key)інше існує з давніх часів. .get(key)не є особливим для FTL, це просто виклик цього загальнодоступного методу Java. Але використовувати його можна лише в тому випадку, якщо FreeMarker було налаштовано на викриття Mapметодів.
ddekany

Під час ітерації я отримую методи (getClass, hashCode, equals, get, toString, class) ... однак, я не бачу жодних властивостей, таких як 'id', саме для цього я хочу отримати список. Будь-які пропозиції, як отримати цей список властивостей неметоду з цього хешу? Мені потрібно знати ці назви майна. : |
MaxRocket

47

З 2.3.25 зробіть це так:

<#list user as propName, propValue>
  ${propName} = ${propValue}
</#list>

Зверніть увагу, що це також працює з нерядковими клавішами (на відміну від тих map[key], які мали писатись як map?api.get(key)тоді).

До 2.3.25 стандартним рішенням було:

<#list user?keys as prop>
  ${prop} = ${user[prop]}
</#list>

Однак деякі справді старі інтеграції FreeMarker використовують дивну конфігурацію, де загальнодоступні Mapметоди (наприклад getClass) відображаються як ключі. Це відбувається, оскільки вони використовують чистий BeansWrapper(замість DefaultObjectWrapper), чиє simpleMapWrapperмайно було залишено false. Вам слід уникати такої установки, оскільки вона змішує методи з реальними Mapзаписами. Але якщо ви зіткнетеся з такою невдалою установки, спосіб уникнути ситуації , використовує відкриті методи Java, такі як user.entrySet(), user.get(key)і т.д., а не з допомогою мови шаблонів конструкції , як ?keysабо user[key].


Це працює чудово. Але я бачу помилки в середовищі IDE джерела. Будь-яка ідея, як це виправити? Дякую
harshavmb

@harshavmb Які помилки? Чи використовує він, можливо, застарілий плагін FreeMarker, який поставляється зі старою версією FreeMarker?
ddekany

Не думайте. Завантажили останню з інструментів jboss. Я спробую на іншій машині і дам вам знати.
harshavmb

@harshavmb Якщо ви введете щось на зразок ${x?nosuchthing}і наведете на нього курсор, відображене повідомлення про помилку покаже, яку версію FreeMarker вона використовує. Це повинно бути 2.3.25-incubating.
ddekany

дивно, я просто спробував у Mac і не зміг повторити проблему. Проблема, схоже, лише з моєю vm. Я подивлюся на версію jar. Однак це просто помилка в редакторі, але код був виконаний правильно.
harshavmb

12

Якщо використовується BeansWrapper з рівнем експозиції Expose.SAFE або Expose.ALL, тоді можна застосувати стандартний підхід Java для ітерації набору записів:

Наприклад, у Freemarker (з принаймні версії 2.3.19) працюватиме наступне:

<#list map.entrySet() as entry>  
  <input type="hidden" name="${entry.key}" value="${entry.value}" />
</#list>

Наприклад, у Struts2 використовується розширення BeanWrapper із встановленим за замовчуванням рівнем експозиції, щоб дозволити такий спосіб ітерації.


3
Ви насправді пробували це? Тому що я отримав, InvalidReferenceExceptionколи спробував, map?keysпрацюючи.
kdgregory

4
Це працює лише при використанні freemarker.ext.beans.BeansWrapperяк обгортки об'єктів. В іншому випадку Maps автоматично перейде в SimpleHashоб'єкт, який не підтримує #entrySet(). (див. freemarker.sourceforge.net/docs/api/freemarker/template/… )
Koraktor

Ви маєте рацію, і я оновив свою відповідь, щоб відобразити ваш коментар. Хороший вигляд!
rees

1
Вищевказане не буде працювати так добре для хешу, створеного всередині FTL, особливо якщо ви використовуєте резольвер Spring Freemarker з BeanWrapper. Хеш, оголошений у файлі Ftl, не загортається, і все одно буде просто хешем, що підлягає ітерації за допомогою клавіш?.
skipy

1
Не використовуйте чистий BeansWrapper, принаймні не з типовими значеннями, де simpleMapWrapperє false. Це стає дуже заплутаним, оскільки змішує ключі з іменами методів. Якщо вам потрібно зателефонувати entrySet(), просто продовжуйте використовувати "чисту" обгортку об'єкта, як за замовчуванням, і пишіть, map?api.entrySet()якщо вам потрібен доступ до Java API замість ключів.
ddekany

2

Ітераційні об'єкти

Якщо ключі вашої карти - це об’єкт, а не рядок, ви можете повторити це за допомогою Freemarker.

1) Перетворіть карту в список у контролері:

List<Map.Entry<myObjectKey, myObjectValue>> convertedMap  = new ArrayList(originalMap.entrySet());

2) Ітерація карти в шаблоні Freemarker, доступ до об’єкта в ключі та об’єкта у значенні:

<#list convertedMap as item>
    <#assign myObjectKey = item.getKey()/>
    <#assign myObjectValue = item.getValue()/>
    [...]
</#list>

1

Для повноти варто згадати, що з недавнього часу у Freemarker є гідна обробка порожніх колекцій.

Отже, найзручніший спосіб ітерації карти:

<#list tags>
<ul class="posts">
    <#items as tagName, tagCount>
        <li>{$tagName} (${tagCount})</li>
    </#items>
</ul>
<#else>
    <p>No tags found.</p>
</#list>

Більше немає <#if ...>обгортки.


Краща відповідь. Дякую.
egemen

0

Ви можете використовувати одну лапку для доступу до ключа, який ви встановили у своїй програмі Java.

Якщо ви встановите карту в Java на зразок цього

Map<String,Object> hash = new HashMap<String,Object>();
hash.put("firstname", "a");
hash.put("lastname", "b");

Map<String,Object> map = new HashMap<String,Object>();
map.put("hash", hash);

Тоді ви зможете отримати доступ до членів "хешу" у Freemarker таким чином -

${hash['firstname']}
${hash['lastname']}

Вихід:

a
b

це показує, як звертатися до окремих клавіш, але питання задає спосіб ітерації
Ламбарт,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.