Freemarker iterujący po kluczach typu hashmap


87

Freemarker ma dwa typy danych kolekcji, listy i hashmapy. Czy istnieje sposób na iterację po kluczach hashmap, tak jak robimy to z listami?

Więc jeśli mam zmienną z danymi, powiedzmy:

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

Chciałbym wydrukować wszystkie właściwości użytkownika wraz z ich wartością. To jest nieprawidłowe, ale cel jest jasny:

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

Odpowiedzi:


106

Edycja: nie używaj tego rozwiązania z FreeMarker 2.3.25 i nowszymi wersjami, a zwłaszcza nie .get(prop). Zobacz inne odpowiedzi.

Używasz wbudowanej funkcji klawiszy , np. To powinno działać:

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

4
składnia jest inna w najnowszej wersji, co ilustruje link, który zamieściłem w mojej odpowiedzi. Zdaję sobie sprawę, że to stare pytanie, ale jest wysoko oceniane w Google.
Nick Spacek,

26
tylko uwaga - możesz użyć ${user[prop]}skrótu
Bozho

To jest wyciek wydajności: dla każdego klucza musi pobrać wartość. Iterowanie po entrySet () nie powoduje tego problemu.
Geoffrey De Smet

4
powinno być $ {user [prop]}
dns

Przy domyślnej konfiguracji user[prop]robót o ile propjest String(poza tym, czego potrzeba user?api.get(prop)obecnie), ale uwaga, niektóre ramy (jak Struts, wierzę) użyć teraz przestarzałej konfiguracji gdzie nazwy metod są zmieszane z Mapklawiszy, a więc wtedy, gdy wartość propsdzieje być nazwą metody w userobiekcie Java, otrzymasz metodę zamiast tego, co miałeś na myśli. Dlatego też w tych starszych konfiguracjach, których zawsze używają user.get(prop).
ddekany

52

FYI, wygląda na to, że składnia pobierania wartości zmieniła się zgodnie z:

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
Czym różni się ta składnia?
Parker

1
dobra odpowiedź ;-) zwróć uwagę, że podczas drukowania wartości może być konieczne sprawdzenie wartości null, <#if h [klucz] ??> $ {klucz} = $ {h [klucz]}; </ # if>
Brad Parks,

1
Składnia nie została zmieniona. Zarówno [key]i .get(key)istnieje od czasów starożytnych. .get(key)nie jest specjalnie dla FTL, po prostu wywołuje tę publiczną metodę Java. Ale możesz go używać tylko wtedy, gdy FreeMarker został skonfigurowany do ujawniania Mapmetod.
ddekany,

Podczas iteracji otrzymuję metody (getClass, hashCode, equals, get, toString, class) ... jednak nie widzę żadnych właściwości, takich jak `` id '', których chcę uzyskać listę. Jakieś sugestie, jak uzyskać listę właściwości niebędących metodami z tego skrótu? Muszę znać nazwy tych nieruchomości. : |
MaxRocket

47

Od 2.3.25 zrób to w ten sposób:

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

Zwróć uwagę, że działa to również z kluczami niebędącymi łańcuchami (w przeciwieństwie do tych map[key], które musiały być zapisane tak jak map?api.get(key)wtedy).

Przed 2.3.25 standardowym rozwiązaniem było:

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

Jednak niektóre naprawdę stare integracje FreeMarker używają dziwnej konfiguracji, w której Mapmetody publiczne (takie jak getClass) pojawiają się jako klucze. Dzieje się tak, ponieważ używają one czystego BeansWrapper(zamiast DefaultObjectWrapper), którego simpleMapWrapperwłaściwość została pozostawiona false. Powinieneś unikać takiej konfiguracji, ponieważ łączy ona metody z prawdziwymi Mapwpisami. Ale jeśli napotkasz takim niefortunnym konfiguracji, tak aby uniknąć sytuacji korzysta odsłonięte metod Java, takich jak user.entrySet(), user.get(key)itp, a nie przy użyciu konstrukcji językowych jak szablon ?keyslub user[key].


To działa doskonale. Ale widzę błędy w springsource IDE. Masz pomysł, jak to naprawić? Dzięki
harshavmb

@harshavmb Jakie błędy? Czy może używa przestarzałej wtyczki FreeMarker, która jest dostarczana ze starą wersją FreeMarker?
ddekany

Nie myśl tak. Pobrałem najnowszą wersję z jboss tools. Spróbuję na innej maszynie i dam Ci znać.
harshavmb

@harshavmb Jeśli wpiszesz coś podobnego ${x?nosuchthing}i najedziesz na to kursorem, wyświetlony komunikat o błędzie powie, której wersji FreeMarker używa. Tak powinno być 2.3.25-incubating.
ddekany

dziwne, właśnie próbowałem na komputerze Mac i nie mogłem odtworzyć problemu. Wydaje się, że problem dotyczy tylko mojego vm. Rzucę okiem na wersję słoiczkową. Jednak to tylko błąd w edytorze, ale kod został wykonany poprawnie.
harshavmb

12

Jeśli używasz BeansWrapper z poziomem ekspozycji Expose.SAFE lub Expose.ALL, możesz zastosować standardowe podejście Java do iteracji zestawu wpisów:

Na przykład następujące elementy będą działać w Freemarker (od wersji co najmniej 2.3.19):

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

Na przykład w Struts2 używane jest rozszerzenie BeanWrapper z domyślnym poziomem ekspozycji, aby umożliwić ten sposób iteracji.


3
Czy faktycznie próbowałeś tego? Ponieważ dostałem, InvalidReferenceExceptionkiedy próbowałem, podczas map?keyspracy.
kdgregory

4
Działa to tylko wtedy, gdy jest używane freemarker.ext.beans.BeansWrapperjako opakowanie obiektu. W przeciwnym razie Maps zostanie automatycznie zawinięty w SimpleHashobiekt, który nie obsługuje #entrySet(). (patrz freemarker.sourceforge.net/docs/api/freemarker/template/... )
Koraktor

Masz rację i zaktualizowałem moją odpowiedź, aby odzwierciedlić Twój komentarz. Dobry wygląd!
rees

1
Powyższe nie będzie działać zbyt dobrze dla skrótu utworzonego wewnątrz FTL, szczególnie jeśli używasz resolvera Spring Freemarker z BeanWrapper. Skrót zadeklarowany w pliku Ftl nie jest opakowany i nadal będzie po prostu hashem iterowalnym przy użyciu kluczy?
skipy

1
Nie używaj czystego BeansWrapper, przynajmniej nie z jego domyślnymi ustawieniami, gdzie simpleMapWrapperjest false. Robi się to bardzo zagmatwane, ponieważ miesza klucze z nazwami metod. Jeśli chcesz zadzwonić entrySet(), po prostu używaj „czystego” opakowania obiektu, takiego jak domyślny, i napisz, map?api.entrySet()jeśli chcesz uzyskać dostęp do Java API zamiast kluczy.
ddekany

2

Iterowanie obiektów

Jeśli twoje klucze mapy są obiektem, a nie łańcuchem, możesz iterować za pomocą Freemarkera.

1) Konwertuj mapę na listę w kontrolerze:

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

2) Iteruj mapę w szablonie Freemarker, uzyskując dostęp do obiektu w kluczu i obiektu w wartości:

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

1

Dla kompletności warto wspomnieć, że od niedawna Freemarker przyzwoicie obsługuje puste kolekcje.

Najwygodniejszym sposobem iteracji mapy jest więc:

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

Żadnych więcej <#if ...>opakowań.


Najlepsza odpowiedź. Dziękuję Ci.
egemen

0

Możesz użyć pojedynczego cudzysłowu, aby uzyskać dostęp do klucza ustawionego w programie Java.

Jeśli ustawisz mapę w Javie w ten sposób

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

Następnie możesz uzyskać dostęp do członków 'hash' w Freemarker w ten sposób -

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

Wynik :

a
b

który pokazuje, jak adresować poszczególne klucze, ale pytanie dotyczy iteracji
Lambart
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.