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 : "looser@everything.com"
  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>

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>  

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>

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].


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.


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>

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.


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

yang menunjukkan bagaimana menangani kunci individu, tetapi pertanyaannya menanyakan bagaimana mengulang
Lambart
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.