Freemarker iteriert über Hashmap-Schlüssel

86

Freemarker verfügt über zwei Sammlungsdatentypen, Listen und Hashmaps. Gibt es eine Möglichkeit, über Hashmap-Schlüssel zu iterieren, genau wie bei Listen?

Wenn ich also eine Variable mit Daten habe, sagen wir:

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

Ich möchte alle Eigenschaften des Benutzers mit ihrem Wert drucken. Dies ist ungültig, aber das Ziel ist klar:

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

Antworten:

106

Bearbeiten: Verwenden Sie diese Lösung nicht mit FreeMarker 2.3.25 und höher, insbesondere nicht .get(prop). Siehe andere Antworten.

Sie verwenden die eingebaute Tastenfunktion , zB sollte dies funktionieren:

<#list user?keys as prop>
    ${prop} = ${user.get(prop)}
</#list>  
Skaffman
quelle
4
Die Syntax ist in der neuesten Version anders, wie in dem Link dargestellt, den ich in meiner Antwort gepostet habe. Mir ist klar, dass dies eine alte Frage ist, die jedoch bei Google einen hohen Stellenwert hat.
Nick Spacek
26
Nur eine Notiz - Sie können ${user[prop]}als Kurzschrift verwenden
Bozho
Dies ist ein Leistungsverlust: Für jeden Schlüssel muss der Wert abgerufen werden. Das Iterieren über entrySet () hat dieses Problem nicht.
Geoffrey De Smet
4
sollte $ {user [prop]} sein
DNS
Mit den user[prop]Konfigurationsstandardwerken soweit propeine String(sonst müssen Sie user?api.get(prop)zur Zeit), aber Vorsicht, einige Frameworks (wie Struts, glaube ich) verwenden , um einen jetzt veralteten Setup wo Methodennamen werden gemischt mit MapTasten, und so , wenn der Wert von propsgeschieht Wenn Sie einen Methodennamen im userJava-Objekt haben, erhalten Sie die Methode anstelle der von Ihnen gemeinten. Das ist auch der Grund, warum sie in diesen Legacy-Setups immer verwendet werden user.get(prop).
Ddekany
52

Zu Ihrer Information, es sieht so aus, als ob sich die Syntax zum Abrufen der Werte geändert hat gemäß:

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
quelle
2
Wie unterscheidet sich diese Syntax?
Parker
1
gute Antwort ;-) Beachten Sie, dass Sie beim Drucken Ihres Werts möglicherweise nach einer Null suchen müssen. <#if h [key] ??> $ {key} = $ {h [key]}; </ # if>
Brad Parks
1
Die Syntax wurde nicht geändert. Beides [key]und .get(key)existiert seit der Antike. .get(key)ist nichts Besonderes für FTL, es ruft nur diese öffentliche Java-Methode auf. Sie können es jedoch nur verwenden, wenn FreeMarker so konfiguriert wurde, dass MapMethoden verfügbar gemacht werden.
ddekany
Beim Iterieren erhalte ich Methoden (getClass, hashCode, equals, get, toString, class) ... ich sehe jedoch keine der Eigenschaften wie 'id', von denen ich eine Liste erhalten möchte. Irgendwelche Vorschläge, wie man diese Liste von Nicht-Methoden-Eigenschaften aus diesem Hash erhält? Ich muss diese Eigenschaftsnamen kennen. : |
MaxRocket
45

Machen Sie es seit 2.3.25 folgendermaßen:

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

Beachten Sie, dass dies auch mit Schlüsseln ohne Zeichenfolge funktioniert (im Gegensatz zu den Schlüsseln map[key], die wie map?api.get(key)damals geschrieben werden mussten ).

Vor 2.3.25 war die Standardlösung:

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

Einige wirklich alte FreeMarker-Integrationen verwenden jedoch eine seltsame Konfiguration, bei der die öffentlichen MapMethoden (wie getClass) als Schlüssel angezeigt werden. Dies geschieht, wenn sie ein reines BeansWrapper(anstelle von DefaultObjectWrapper) verwenden, dessen simpleMapWrapperEigentum belassen wurde false. Sie sollten ein solches Setup vermeiden, da es die Methoden mit echten MapEinträgen mischt . Aber wenn Sie in einem solche unglücklichen Setup ausführen, die Möglichkeit , die Situation zu entkommen , wird mit den freiliegenden Java - Methoden, wie zum Beispiel user.entrySet(), user.get(key)usw. und nicht die Konstrukte Template - Sprache wie?keys oder user[key].

ddekany
quelle
Das funktioniert perfekt. Ich sehe jedoch Fehler in der Springsource-IDE. Irgendeine Idee, wie man es repariert? Vielen Dank
harschavmb
@harshavmb Welche Fehler? Verwendet es vielleicht ein veraltetes FreeMarker-Plugin, das mit einer alten FreeMarker-Version geliefert wird?
Ddekany
Glaube nicht. Habe die neueste Version von jboss tools heruntergeladen. Ich werde es auf einer anderen Maschine versuchen und dich wissen lassen.
harschavmb
@harshavmb Wenn Sie etwas wie eingeben ${x?nosuchthing}und den Mauszeiger darüber halten, zeigt die angezeigte Fehlermeldung an, welche FreeMarker-Version verwendet wird. Es sollte sein 2.3.25-incubating.
Ddekany
Seltsam, ich habe es gerade auf dem Mac versucht und konnte das Problem nicht replizieren. Problem scheint nur mit meinem VM zu sein. Ich werde mir die Glasversion ansehen. Es ist jedoch nur ein Fehler im Editor, aber der Code wurde korrekt ausgeführt.
harschavmb
12

Wenn Sie einen BeansWrapper mit einer Belichtungsstufe von Expose.SAFE oder Expose.ALL verwenden, kann der Standard-Java-Ansatz zum Iterieren des Eintragssatzes verwendet werden:

In Freemarker funktioniert beispielsweise Folgendes (seit mindestens Version 2.3.19):

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

In Struts2 wird beispielsweise eine Erweiterung des BeanWrapper verwendet, wobei die Belichtungsstufe standardmäßig festgelegt ist, um diese Art der Iteration zu ermöglichen.

rees
quelle
3
Hast du das tatsächlich versucht? Weil ich eine bekam, InvalidReferenceExceptionals ich es versuchte, während ich map?keysarbeitete.
kdgregory
4
Dies funktioniert nur bei Verwendung freemarker.ext.beans.BeansWrapperals Objekt-Wrapper. Andernfalls wird Maps automatisch in ein SimpleHashObjekt eingeschlossen, das nicht unterstützt wird #entrySet(). (siehe freemarker.sourceforge.net/docs/api/freemarker/template/… )
Koraktor
Sie haben Recht, und ich habe meine Antwort aktualisiert, um Ihren Kommentar wiederzugeben. Gut aussehend!
Rees
1
Das Obige funktioniert nicht so gut für den in der FTL erstellten Hash, insbesondere wenn Sie Spring Freemarker Resolver mit BeanWrapper verwenden. Der in der Ftl-Datei deklarierte Hash wird nicht umbrochen und ist immer noch nur ein mit? -Tasten iterierbarer Hash.
überspringen
1
Verwenden Sie nicht rein BeansWrapper, zumindest nicht mit seinen Standardeinstellungen, wo simpleMapWrapperist false. Es wird sehr verwirrend, da Schlüssel mit Methodennamen gemischt werden. Wenn Sie aufrufen müssen entrySet(), verwenden Sie einfach weiterhin einen "sauberen" Objekt-Wrapper wie den Standard-Wrapper und schreiben map?api.entrySet()Sie , wenn Sie anstelle der Schlüssel auf die Java-API zugreifen müssen.
ddekany
2

Objekte iterieren

Wenn Ihre Kartenschlüssel ein Objekt und keine Zeichenfolge sind, können Sie sie mit Freemarker iterieren.

1) Konvertieren Sie die Karte in eine Liste in der Steuerung:

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

2) Iterieren Sie die Karte in der Freemarker-Vorlage und greifen Sie auf das Objekt im Schlüssel und das Objekt im Wert zu:

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

Der Vollständigkeit halber ist zu erwähnen, dass es in Freemarker seit kurzem einen anständigen Umgang mit leeren Sammlungen gibt.

Der bequemste Weg, eine Karte zu iterieren, ist:

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

Keine <#if ...>Wrapper mehr .

Ondra Žižka
quelle
Beste Antwort. Danke dir.
Egemen
0

Sie können ein einfaches Anführungszeichen verwenden, um auf den Schlüssel zuzugreifen, den Sie in Ihrem Java-Programm festgelegt haben.

Wenn Sie eine Karte in Java wie folgt einstellen

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

Dann können Sie wie folgt auf die Mitglieder von 'Hash' in Freemarker zugreifen -

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

Ausgabe :

a
b
Ashish Chhabria
quelle
das zeigt, wie man einzelne Schlüssel adressiert, aber die Frage fragt, wie man iteriert
Lambart