Freemarker lặp lại các khóa hashmap


87

Freemarker có hai kiểu dữ liệu thu thập, danh sách và bản đồ băm Có cách nào để lặp lại các khóa bản đồ băm giống như chúng ta làm với danh sách không?

Vì vậy, nếu tôi có một var với dữ liệu, hãy nói:

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

Tôi muốn in tất cả các thuộc tính của người dùng với giá trị của chúng. Điều này không hợp lệ, nhưng mục tiêu rõ ràng:

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

Câu trả lời:


106

Chỉnh sửa: Không sử dụng giải pháp này với FreeMarker 2.3.25 trở lên, đặc biệt là không .get(prop). Xem các câu trả lời khác.

Bạn sử dụng chức năng phím tích hợp, ví dụ như chức năng này sẽ hoạt động:

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

4
cú pháp khác trong phiên bản mới nhất, như được minh họa trong liên kết mà tôi đã đăng trong câu trả lời của mình. Tôi nhận ra đây là một câu hỏi cũ, nhưng nó được xếp hạng cao trên Google.
Nick Spacek

26
chỉ là một ghi chú - bạn có thể sử dụng ${user[prop]}như một tốc ký
Bozho

Đây là một rò rỉ hiệu suất: đối với mọi khóa, nó cần truy xuất giá trị. Lặp lại entrySet () không có vấn đề đó.
Geoffrey De Smet

4
phải là $ {user [prop]}
dns

Với mặc định cấu hình user[prop]hoạt động ở mức độ như prophiện tại String(nếu không thì bạn cần user?api.get(prop)hiện tại), nhưng hãy cẩn thận, một số khung công tác (như Struts, tôi tin rằng) sử dụng thiết lập hiện không được dùng nữa trong đó tên phương thức được trộn với Mapcác khóa và vì vậy nếu giá trị của propsxảy ra với là một tên phương thức trong userđối tượng Java, bạn nhận được phương thức thay vì những gì bạn muốn. Đó cũng là lý do tại sao trong những thiết lập kế thừa đó họ luôn sử dụng user.get(prop).
ddekany

52

FYI, có vẻ như cú pháp để truy xuất các giá trị đã thay đổi theo:

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
Cú pháp này khác nhau như thế nào?
Parker

1
câu trả lời hay ;-) lưu ý rằng bạn có thể phải kiểm tra giá trị rỗng khi in giá trị của mình, <#if h [key] ??> $ {key} = $ {h [key]}; </ # if>
Brad Parks

1
Cú pháp không được thay đổi. Cả hai [key].get(key)tồn tại từ thời cổ đại. .get(key)không có gì đặc biệt đối với FTL, nó chỉ gọi phương thức Java công khai đó. Nhưng bạn chỉ có thể sử dụng nó nếu FreeMarker được cấu hình để hiển thị Mapcác phương thức.
ddekany

Khi lặp lại, tôi nhận được các phương thức (getClass, hashCode, equals, get, toString, class) ... tuy nhiên, tôi không thấy bất kỳ thuộc tính nào như 'id', đó là thứ tôi muốn lấy danh sách. Bất kỳ đề xuất nào về cách lấy danh sách các thuộc tính phi phương thức từ hàm băm đó? Tôi cần biết những tên tài sản đó. : |
MaxRocket

47

Kể từ 2.3.25, hãy làm như thế này:

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

Lưu ý rằng điều này cũng hoạt động với các khóa không phải chuỗi (không giống như map[key], phải được viết như vậy map?api.get(key)).

Trước 2.3.25, dung dịch tiêu chuẩn là:

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

Tuy nhiên, một số tích hợp FreeMarker thực sự cũ sử dụng cấu hình lạ, trong đó các Mapphương thức công khai (như getClass) xuất hiện dưới dạng khóa. Điều đó xảy ra khi họ đang sử dụng nguyên chất BeansWrapper(thay vì DefaultObjectWrapper) có thuộc simpleMapWrappertính được để lại false. Bạn nên tránh thiết lập như vậy, vì nó trộn các phương pháp với Mapcác mục nhập thực . Nhưng nếu bạn chạy vào thiết lập bất hạnh như vậy, con đường để thoát khỏi tình trạng này là sử dụng các phương thức Java tiếp xúc, chẳng hạn như user.entrySet(), user.get(key), vv, và không sử dụng các cấu trúc mẫu ngôn ngữ như ?keyshay user[key].


Điều này hoạt động hoàn hảo. Tuy nhiên, tôi thấy lỗi trong IDE nguồn suối. Bất kỳ ý tưởng làm thế nào để sửa chữa nó? Cảm ơn
harshavmb

@harshavmb Lỗi gì? Có lẽ nó sử dụng plugin FreeMarker đã lỗi thời, đi kèm với phiên bản FreeMarker cũ?
ddekany

Đừng nghĩ vậy. Đã tải xuống một cái mới nhất từ ​​các công cụ jboss. Tôi sẽ thử trên máy khác và cho bạn biết.
harshavmb

@harshavmb Nếu bạn nhập nội dung nào đó như ${x?nosuchthing}và di chuột qua nó, thông báo lỗi hiển thị sẽ cho biết phiên bản FreeMarker mà nó sử dụng. Nó nên được 2.3.25-incubating.
ddekany

kỳ lạ, tôi vừa thử trong Mac và không thể tái tạo sự cố. Vấn đề dường như chỉ xảy ra với vm của tôi. Tôi sẽ xem xét phiên bản jar. Tuy nhiên, đó chỉ là một lỗi trong trình chỉnh sửa, nhưng mã đã được thực thi chính xác.
harshavmb

12

Nếu sử dụng BeansWrapper với mức độ phơi sáng là Expose.SAFE hoặc Expose.ALL, thì phương pháp Java tiêu chuẩn để lặp lại tập mục nhập có thể được sử dụng:

Ví dụ: phần sau sẽ hoạt động trong Freemarker (ít nhất là phiên bản 2.3.19):

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

Ví dụ: trong Struts2, một phần mở rộng của BeanWrapper được sử dụng với mức phơi sáng được mặc định để cho phép cách lặp này.


3
Bạn đã thực sự thử điều này chưa? Bởi vì tôi đã nhận được InvalidReferenceExceptionkhi tôi thử nó, trong khi map?keyslàm việc.
kdgregory

4
Điều này chỉ hoạt động khi sử dụng freemarker.ext.beans.BeansWrapperlàm trình bao bọc đối tượng. Nếu không, Maps sẽ tự động được bao bọc thành một SimpleHashđối tượng không hỗ trợ #entrySet(). (xem freemarker.sourceforge.net/docs/api/freemarker/template/… )
Koraktor

Bạn đúng, và tôi đã cập nhật câu trả lời của mình để phản ánh nhận xét của bạn. Tốt nhìn ra!
rees

1
Ở trên sẽ không hoạt động tốt đối với hàm băm được tạo bên trong FTL, đặc biệt nếu bạn đang sử dụng trình phân giải Spring Freemarker với BeanWrapper. Hàm băm được khai báo bên trong tệp Ftl không được bao bọc và sẽ vẫn chỉ là một hàm băm có thể lặp lại bằng cách sử dụng các phím?.
lười biếng

1
Không sử dụng tinh khiết BeansWrapper, ít nhất là không có giá trị mặc định của nó, nơi simpleMapWrapperfalse. Nó rất khó hiểu, vì nó trộn các khóa với tên phương thức. Nếu bạn cần gọi entrySet(), chỉ cần tiếp tục sử dụng trình bao bọc đối tượng "sạch", như trình bao bọc mặc định và viết map?api.entrySet()nếu bạn cần truy cập API Java thay vì các khóa.
ddekany

2

Lặp lại các đối tượng

Nếu các khóa bản đồ của bạn là một đối tượng chứ không phải một chuỗi, bạn có thể lặp lại nó bằng cách sử dụng Freemarker.

1) Chuyển đổi bản đồ thành một danh sách trong bộ điều khiển:

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

2) Lặp lại bản đồ trong mẫu Freemarker, truy cập vào đối tượng trong Khóa và Đối tượng trong Giá trị:

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

1

Để hoàn thiện, điều đáng nói là gần đây Freemarker đã xử lý tốt các bộ sưu tập trống.

Vì vậy, cách thuận tiện nhất để lặp lại bản đồ là:

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

Không có <#if ...>giấy gói nào nữa .


Câu trả lời tốt nhất. Cảm ơn bạn.
egemen

0

Bạn có thể sử dụng một dấu ngoặc kép để truy cập vào khóa mà bạn đặt trong chương trình Java của mình.

Nếu bạn thiết lập một Bản đồ trong Java như thế này

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

Sau đó, bạn có thể truy cập các thành viên của 'hash' trong Freemarker như thế này -

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

Đầu ra:

a
b

rằng chương trình làm thế nào để giải quyết các phím riêng biệt, nhưng câu hỏi hỏi làm thế nào để lặp
Lambart
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.