Freemarker che esegue l'iterazione sulle chiavi hashmap


87

Freemarker ha due tipi di dati di raccolta, elenchi e hashmap Esiste un modo per iterare sulle chiavi hashmap proprio come facciamo con gli elenchi?

Quindi, se ho una variabile con i dati, diciamo:

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

Vorrei stampare tutte le proprietà dell'utente con il loro valore. Questo non è valido, ma l'obiettivo è chiaro:

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

Risposte:


106

Modifica: non utilizzare questa soluzione con FreeMarker 2.3.25 e versioni successive, soprattutto no .get(prop). Vedi altre risposte.

Utilizzi la funzione tasti incorporata, ad esempio dovrebbe funzionare:

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

4
la sintassi è diversa nell'ultima versione, come illustrato nel link che ho postato nella mia risposta. Mi rendo conto che questa è una vecchia domanda, ma risulta molto apprezzata su Google.
Nick Spacek

26
solo una nota - puoi usare ${user[prop]}come abbreviazione
Bozho

Questa è una perdita di prestazioni: per ogni chiave, è necessario recuperare il valore. L'iterazione su entrySet () non ha questo problema.
Geoffrey De Smet

4
dovrebbe essere $ {user [prop]}
dns

Con le impostazioni predefinite di configurazione user[prop]funziona fino propa String(altrimenti è necessario user?api.get(prop)attualmente), ma attenzione, alcuni framework (come Struts, credo) utilizzano una configurazione ormai deprecata in cui i nomi dei metodi sono mescolati con le Mapchiavi, e quindi se il valore di propsaccade a essere un nome di metodo usernell'oggetto Java, ottieni il metodo invece di ciò che intendevi. Questo è anche il motivo per cui in quelle configurazioni legacy usano sempre user.get(prop).
ddekany

52

Cordiali saluti, sembra che la sintassi per il recupero dei valori sia cambiata in base a:

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
In che modo questa sintassi è diversa?
Parker

1
buona risposta ;-) nota che potresti dover controllare un valore nullo quando stampi il tuo valore, <#if h [key] ??> $ {key} = $ {h [key]}; </ # if>
Brad Parks,

1
La sintassi non è stata modificata. Entrambi [key]ed .get(key)esiste fin dai tempi antichi. .get(key)non è speciale per FTL, sta solo chiamando quel metodo Java pubblico. Ma puoi usarlo solo se FreeMarker è stato configurato per esporre i Mapmetodi.
ddekany

Durante l'iterazione, ottengo metodi (getClass, hashCode, equals, get, toString, class) ... tuttavia, non vedo nessuna delle proprietà come 'id', che è ciò di cui voglio ottenere un elenco. Qualche suggerimento su come ottenere quell'elenco di proprietà non di metodo da quell'hash? Ho bisogno di conoscere quei nomi di proprietà. : |
MaxRocket

49

Dalla 2.3.25, fallo in questo modo:

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

Nota che funziona anche con chiavi non stringa (a differenza di map[key], che dovevano essere scritte come map?api.get(key)allora).

Prima della 2.3.25 la soluzione standard era:

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

Tuttavia, alcune integrazioni FreeMarker molto vecchie utilizzano una configurazione strana, in cui i Mapmetodi pubblici (come getClass) appaiono come chiavi. Ciò accade mentre usano un puro BeansWrapper(invece di DefaultObjectWrapper) la cui simpleMapWrapperproprietà è stata lasciata attiva false. Dovresti evitare una tale configurazione, poiché mescola i metodi con Mapvoci reali . Ma se si esegue in tale infelice configurazione, il modo per sfuggire alla situazione sta usando i metodi Java esposti, come ad esempio user.entrySet(), user.get(key)e così via, e non utilizzando i costrutti del linguaggio modello simile ?keyso user[key].


Funziona perfettamente. Ma vedo errori nell'IDE di Springsource. Qualche idea su come risolverlo? Grazie
harshavmb

@harshavmb Quali errori? Forse utilizza un plug-in FreeMarker obsoleto, fornito con una vecchia versione di FreeMarker?
ddekany

Non credo proprio. Ho scaricato l'ultimo da jboss tools. Proverò su un'altra macchina e ti farò sapere.
harshavmb

@harshavmb Se inserisci qualcosa di simile ${x?nosuchthing}e ci passi sopra, il messaggio di errore mostrato indicherà quale versione di FreeMarker utilizza. Dovrebbe essere 2.3.25-incubating.
ddekany

strano, ho appena provato su Mac e non sono riuscito a replicare il problema. Il problema sembra essere solo con il mio vm. Darò uno sguardo alla versione jar. Tuttavia, è solo un errore nell'editor, ma il codice è stato eseguito correttamente.
harshavmb

12

Se si utilizza un BeansWrapper con un livello di esposizione di Expose.SAFE o Expose.ALL, è possibile utilizzare l'approccio Java standard di iterazione del set di voci:

Ad esempio, quanto segue funzionerà in Freemarker (almeno dalla versione 2.3.19):

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

In Struts2, ad esempio, viene utilizzata un'estensione del BeanWrapper con il livello di esposizione predefinito per consentire questo modo di iterazione.


3
Hai davvero provato questo? Perché l'ho ricevuto InvalidReferenceExceptionquando l'ho provato, mentre map?keysfunzionava.
kdgregory

4
Funziona solo quando si utilizza freemarker.ext.beans.BeansWrappercome wrapper oggetto. In caso contrario, Mapi messaggi verranno automaticamente inseriti in un SimpleHashoggetto che non supporta #entrySet(). (vedi freemarker.sourceforge.net/docs/api/freemarker/template/… )
Koraktor

Hai ragione e ho aggiornato la mia risposta per riflettere il tuo commento. Bello guardare fuori!
rees

1
Quanto sopra non funzionerà molto bene per l'hash creato all'interno dell'FTL, specialmente se stai usando il risolutore Spring Freemarker con BeanWrapper. L'hash dichiarato all'interno del file Ftl non è racchiuso e sarà comunque solo un hash iterabile usando? Chiavi.
skipy

1
Non utilizzare puro BeansWrapper, almeno non con i suoi valori predefiniti, in cui simpleMapWrapperè false. Diventa molto confuso, poiché mescola le chiavi con i nomi dei metodi. Se devi chiamare entrySet(), continua a utilizzare un object wrapper "pulito", come quello predefinito, e scrivi map?api.entrySet()se devi accedere all'API Java invece delle chiavi.
ddekany

2

Oggetti iteranti

Se le chiavi della mappa sono un oggetto e non una stringa, puoi iterarlo utilizzando Freemarker.

1) Converti la mappa in un elenco nel controller:

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

2) Iterare la mappa nel template Freemarker, accedendo all'oggetto nella Chiave e all'Oggetto nel Valore:

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

1

Per completezza, vale la pena ricordare che c'è una discreta gestione delle raccolte vuote in Freemarker da poco tempo.

Quindi il modo più conveniente per iterare una mappa è:

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

Niente più <#if ...>wrapper.


Migliore risposta. Grazie.
egemen

0

È possibile utilizzare una sola virgoletta per accedere alla chiave impostata nel programma Java.

Se imposti una mappa in Java come questa

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

Quindi puoi accedere ai membri di 'hash' in Freemarker in questo modo -

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

Produzione :

a
b

che mostra come indirizzare singole chiavi, ma la domanda chiede come iterare
Lambart
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.