Freemarker melakukan iterasi pada kunci hashmap

87

Freemarker memiliki dua tipe data koleksi, daftar dan hashmaps Apakah ada cara untuk mengulang kunci hashmap seperti yang kita lakukan dengan daftar?

Jadi jika saya memiliki var dengan data, katakanlah:

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

Saya ingin mencetak semua properti pengguna dengan nilainya. Ini tidak valid, tetapi tujuannya jelas:

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

Jawaban:

106

Sunting: Jangan gunakan solusi ini dengan FreeMarker 2.3.25 dan yang lebih baru, terutama tidak .get(prop). Lihat jawaban lainnya.

Anda menggunakan fungsi tombol built-in , misalnya ini seharusnya berfungsi:

<#list user?keys as prop>
    ${prop} = ${user.get(prop)}
</#list>  
skaffman
sumber
4
sintaksnya berbeda di versi terbaru, seperti yang diilustrasikan dalam tautan yang saya posting di jawaban saya. Saya menyadari ini adalah pertanyaan lama, tetapi muncul dengan peringkat tinggi di Google.
Nick Spacek
26
hanya sebuah catatan - Anda dapat menggunakan ${user[prop]}sebagai singkatan
Bozho
Ini adalah kebocoran kinerja: untuk setiap kunci, ia perlu mengambil nilainya. Iterasi pada entrySet () tidak memiliki masalah itu.
Geoffrey De Smet
4
harus $ {user [prop]}
dns
Dengan konfigurasi default user[prop]bekerja sejauh propyang String(jika tidak Anda butuhkan user?api.get(prop)saat ini), tetapi berhati-hatilah, beberapa kerangka kerja (seperti Struts, saya percaya) menggunakan pengaturan yang sekarang sudah usang di mana nama metode dicampur dengan Mapkunci, dan begitu juga jika nilai propsterjadi pada menjadi nama metode di userobjek Java, Anda mendapatkan metode, bukan apa yang Anda maksud. Itu juga mengapa dalam penyiapan lama itu selalu mereka gunakan user.get(prop).
ddekany
52

FYI, sepertinya sintaks untuk mengambil nilai telah berubah sesuai dengan:

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
sumber
2
Bagaimana sintaks ini berbeda?
Parker
1
jawaban yang bagus ;-) perhatikan bahwa Anda mungkin harus memeriksa null saat mencetak nilai Anda, <#if h [key] ??> $ {key} = $ {h [key]}; </ # if>
Brad Parks
1
Sintaksnya tidak berubah. Keduanya [key]dan .get(key)ada sejak zaman kuno. .get(key)tidak ada yang khusus untuk FTL, itu hanya memanggil metode Java publik itu. Tetapi Anda hanya dapat menggunakannya jika FreeMarker dikonfigurasi untuk mengekspos Mapmetode.
ddekany
Saat melakukan iterasi, saya mendapatkan metode (getClass, hashCode, equals, get, toString, class) ... namun, saya tidak melihat salah satu properti seperti 'id', yang ingin saya daftar. Ada saran bagaimana cara mendapatkan daftar properti non-metode dari hash itu? Saya perlu tahu nama properti itu. : |
MaxRocket
49

Sejak 2.3.25, lakukan seperti ini:

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

Perhatikan bahwa ini juga berfungsi dengan kunci non-string (tidak seperti map[key], yang harus ditulis seperti map?api.get(key)itu).

Sebelum 2.3.25 solusi standarnya adalah:

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

Namun, beberapa integrasi FreeMarker yang sangat lama menggunakan konfigurasi yang aneh, di mana Mapmetode publik (seperti getClass) muncul sebagai kunci. Itu terjadi karena mereka menggunakan murni BeansWrapper(bukan DefaultObjectWrapper) yang simpleMapWrapperpropertinya tertinggal false. Anda harus menghindari pengaturan seperti itu, karena ini mencampur metode dengan Mapentri yang sebenarnya . Tetapi jika Anda mengalami pengaturan malang tersebut, cara untuk melarikan diri situasi ini menggunakan metode Java yang terkena, seperti user.entrySet(), user.get(key), dll, dan tidak menggunakan konstruksi bahasa template seperti ?keysatau user[key].

ddekany
sumber
Ini bekerja dengan sempurna. Tapi, saya melihat kesalahan dalam IDE springsource. Ada ide bagaimana memperbaikinya? Terima kasih
harshavmb
@harshav Kesalahan apa? Apakah itu menggunakan plugin FreeMarker yang sudah ketinggalan zaman, yang hadir dengan versi FreeMarker lama?
ddekany
Menurutku tidak. Telah mengunduh yang terbaru dari alat jboss. Saya akan mencoba di komputer lain dan memberi tahu Anda.
harshavmb
@harshavmb Jika Anda memasukkan sesuatu seperti ${x?nosuchthing}dan Anda mengarahkan kursor ke atasnya, pesan kesalahan yang ditampilkan akan memberi tahu versi FreeMarker mana yang digunakannya. Seharusnya begitu 2.3.25-incubating.
ddekany
aneh, saya baru saja mencoba di Mac dan tidak dapat meniru masalah tersebut. Masalah tampaknya hanya dengan vm saya. Saya akan melihat versi jar. Namun, ini hanya kesalahan di editor, tetapi kode dijalankan dengan benar.
harshavmb
12

Jika menggunakan BeansWrapper dengan tingkat eksposur Expose.SAFE atau Expose.ALL, maka pendekatan Java standar untuk mengiterasi set entri dapat digunakan:

Misalnya, berikut ini akan berfungsi di Freemarker (setidaknya sejak versi 2.3.19):

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

Dalam Struts2, misalnya, ekstensi BeanWrapper digunakan dengan tingkat eksposur default untuk memungkinkan cara iterasi ini.

buluh
sumber
3
Apakah Anda benar-benar mencoba ini? Karena saya mendapat InvalidReferenceExceptionsaat saya mencobanya, saat map?keysbekerja.
kdgregory
4
Ini hanya berfungsi saat digunakan freemarker.ext.beans.BeansWrappersebagai pembungkus objek. Jika tidak, Maps secara otomatis akan dibungkus menjadi SimpleHashobjek yang tidak mendukung #entrySet(). (lihat freemarker.sourceforge.net/docs/api/freemarker/template/… )
Koraktor
Anda benar, dan saya telah memperbarui jawaban saya untuk mencerminkan komentar Anda. Bagus dilihat!
rees
1
Hal di atas tidak akan bekerja dengan baik untuk hash yang dibuat di dalam FTL khususnya jika Anda menggunakan resolver Spring Freemarker dengan BeanWrapper. Hash yang dideklarasikan di dalam file Ftl tidak dibungkus dan akan tetap menjadi hash yang dapat diulang menggunakan tombol?
skipy
1
Jangan gunakan murni BeansWrapper, setidaknya tidak dengan default-nya, di mana simpleMapWrapperadalah false. Ini menjadi sangat membingungkan, karena mencampur kunci dengan nama metode. Jika Anda perlu menelepon entrySet(), tetap gunakan pembungkus objek "bersih", seperti yang default, dan tulis map?api.entrySet()jika Anda perlu mengakses Java API sebagai ganti kuncinya.
ddekany
2

Objek Iterasi

Jika kunci peta Anda adalah objek dan bukan string, Anda dapat mengulangnya menggunakan Freemarker.

1) Ubah peta menjadi daftar di pengontrol:

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

2) Iterasi peta di template Freemarker, akses ke objek di Key dan Objek di Value:

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

Untuk kelengkapan, perlu disebutkan ada penanganan koleksi kosong yang layak di Freemarker sejak baru-baru ini.

Jadi, cara paling mudah untuk mengulang peta adalah:

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

Tidak ada lagi <#if ...>pembungkus.

Ondra Žižka
sumber
Jawaban Terbaik. Terima kasih.
egemen
0

Anda dapat menggunakan satu kutipan untuk mengakses kunci yang Anda setel di program Java Anda.

Jika Anda mengatur Peta di Jawa seperti ini

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

Kemudian Anda dapat mengakses anggota 'hash' di Freemarker seperti ini -

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

Keluaran:

a
b
Ashish Chhabria
sumber
yang menunjukkan bagaimana menangani kunci individu, tetapi pertanyaannya menanyakan bagaimana mengulang
Lambart