Freemarker itération sur les clés de hashmap

87

Freemarker a deux types de données de collection, des listes et des hashmaps. Existe-t-il un moyen d'itérer sur les clés de hashmap comme nous le faisons avec les listes?

Donc, si j'ai un var avec des données, disons:

user : {
  name : "user"
  email : "[email protected]"
  homepage : "http://nosuchpage.org"
}

Je souhaite imprimer toutes les propriétés de l'utilisateur avec leur valeur. Ce n'est pas valide, mais l'objectif est clair:

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

Réponses:

106

Edit: n'utilisez pas cette solution avec FreeMarker 2.3.25 et plus, surtout pas .get(prop). Voir d'autres réponses.

Vous utilisez la fonction des touches intégrées, par exemple cela devrait fonctionner:

<#list user?keys as prop>
    ${prop} = ${user.get(prop)}
</#list>  
skaffman
la source
4
la syntaxe est différente dans la dernière version, comme illustré dans le lien que j'ai posté dans ma réponse. Je me rends compte que c'est une vieille question, mais elle est très bien classée sur Google.
Nick Spacek
26
juste une note - vous pouvez utiliser ${user[prop]}comme sténographie
Bozho
Il s'agit d'une fuite de performances: pour chaque clé, il doit récupérer la valeur. Itérer sur entrySet () n'a pas ce problème.
Geoffrey De Smet
4
devrait être $ {user [prop]}
dns
Avec la configuration par défaut user[prop]fonctionne aussi loin que propc'est a String(sinon vous en avez besoin user?api.get(prop)actuellement), mais attention, certains frameworks (comme Struts, je crois) utilisent une configuration maintenant obsolète où les noms de méthode sont mélangés avec des Mapclés, et donc si la valeur de propsarrive à être un nom de méthode dans l' userobjet Java, vous obtenez la méthode au lieu de ce que vous vouliez dire. C'est aussi pourquoi dans ces anciennes configurations, ils utilisent toujours user.get(prop).
ddekany
52

Pour info, il semble que la syntaxe de récupération des valeurs a changé selon:

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>
Nick Spacek
la source
2
En quoi cette syntaxe est-elle différente?
Parker le
1
bonne réponse ;-) notez que vous devrez peut-être vérifier la valeur nulle lors de l'impression de votre valeur, <#if h [clé] ??> $ {clé} = $ {h [clé]}; </ # if>
Brad Parks
1
La syntaxe n'a pas été modifiée. Les deux [key]et .get(key)existe depuis les temps anciens. .get(key)n'est pas spécial pour FTL, il appelle simplement cette méthode Java publique. Mais vous ne pouvez l'utiliser que si FreeMarker a été configuré pour exposer des Mapméthodes.
ddekany le
Lors de l'itération, j'obtiens des méthodes (getClass, hashCode, equals, get, toString, class) ... cependant, je ne vois aucune des propriétés telles que 'id', ce dont je veux obtenir une liste. Des suggestions pour obtenir cette liste de propriétés non-méthode à partir de ce hachage? J'ai besoin de connaître ces noms de propriétés. : |
MaxRocket
47

Depuis 2.3.25, faites-le comme ceci:

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

Notez que cela fonctionne également avec des clés sans chaîne (contrairement à map[key], qui devait être écrite comme map?api.get(key)alors).

Avant 2.3.25, la solution standard était:

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

Cependant, certaines intégrations FreeMarker vraiment anciennes utilisent une configuration étrange, où les Mapméthodes publiques (comme getClass) apparaissent comme des clés. Cela se produit lorsqu'ils utilisent un pur BeansWrapper(au lieu de DefaultObjectWrapper) dont la simpleMapWrapperpropriété a été laissée active false. Vous devriez éviter une telle configuration, car elle mélange les méthodes avec des Mapentrées réelles . Mais si vous avez cette configuration malheureuse, le moyen d'échapper à la situation en utilisant les méthodes Java exposées, telles que user.entrySet(), user.get(key), etc., et ne pas utiliser les constructions de langage de template comme ?keysou user[key].

Ddekany
la source
Cela fonctionne parfaitement. Mais, je vois des erreurs dans l'IDE springsource. Une idée de comment le réparer? Merci
harshavmb
@harshavmb Quelles erreurs? Utilise-t-il peut-être un plugin FreeMarker obsolète, qui est livré avec une ancienne version de FreeMarker?
ddekany
Ne pense pas. J'ai téléchargé le dernier à partir des outils jboss. Je vais essayer une autre machine et vous le faire savoir.
harshavmb
@harshavmb Si vous entrez quelque chose comme ${x?nosuchthing}et que vous survolez, le message d'erreur affiché indiquera quelle version de FreeMarker il utilise. Ça devrait être2.3.25-incubating .
ddekany
étrange, je viens d'essayer sous Mac et je n'ai pas pu reproduire le problème. Le problème semble être uniquement avec ma vm. Je vais jeter un oeil sur la version jar. Cependant, il ne s'agit que d'une erreur de l'éditeur, mais le code a été exécuté correctement.
harshavmb
12

Si vous utilisez un BeansWrapper avec un niveau d'exposition Expose.SAFE ou Expose.ALL, l'approche Java standard d'itération de l'ensemble d'entrées peut être utilisée:

Par exemple, ce qui suit fonctionnera dans Freemarker (depuis au moins la version 2.3.19):

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

Dans Struts2, par exemple, une extension du BeanWrapper est utilisée avec le niveau d'exposition par défaut pour permettre cette manière d'itération.

rees
la source
3
Avez-vous réellement essayé cela? Parce que j'ai eu un InvalidReferenceExceptionquand je l'ai essayé, pendant que je map?keystravaillais.
kdgregory
4
Cela ne fonctionne que lors de l'utilisation freemarker.ext.beans.BeansWrappercomme wrapper d'objet. Sinon, Maps sera automatiquement enveloppé dans un SimpleHashobjet qui ne prend pas en charge #entrySet(). (voir freemarker.sourceforge.net/docs/api/freemarker/template/… )
Koraktor
Vous avez raison, et j'ai mis à jour ma réponse pour refléter votre commentaire. Belle vue!
rees
1
Ce qui précède ne fonctionnera pas très bien pour le hachage créé dans le FTL, en particulier si vous utilisez le résolveur Spring Freemarker avec BeanWrapper. Le hachage déclaré à l'intérieur du fichier Ftl n'est pas encapsulé et sera toujours juste un hachage itérable à l'aide des touches?.
skipy
1
N'utilisez pas pure BeansWrapper, du moins pas avec ses valeurs par défaut, où simpleMapWrapperest false. Cela devient très déroutant, car il mélange des clés avec des noms de méthodes. Si vous avez besoin d'appeler entrySet(), continuez simplement à utiliser un wrapper d'objet "propre", comme celui par défaut, et écrivez map?api.entrySet()si vous avez besoin d'accéder à l'API Java au lieu des clés.
ddekany
2

Objets itératifs

Si vos clés de carte sont un objet et non une chaîne, vous pouvez l'itérer à l'aide de Freemarker.

1) Convertissez la carte en une liste dans le contrôleur:

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

2) Itérez la carte dans le modèle Freemarker, en accédant à l'objet dans la clé et à l'objet dans la valeur:

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

Pour être complet, il convient de mentionner qu'il existe une gestion décente des collections vides dans Freemarker depuis récemment.

Le moyen le plus pratique d'itérer une carte est donc:

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

Fini les <#if ...>emballages.

Ondra Žižka
la source
Meilleure réponse. Merci.
egemen le
0

Vous pouvez utiliser un guillemet simple pour accéder à la clé que vous avez définie dans votre programme Java.

Si vous définissez une carte en Java comme ceci

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);

Ensuite, vous pouvez accéder aux membres de 'hash' dans Freemarker comme ceci -

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

Production :

a
b
Ashish Chhabria
la source
qui montre comment adresser des clés individuelles, mais la question demande comment itérer
Lambart