Объединить строки в JSP EL?

У меня есть список bean-компонентов, каждый из которых имеет свойство, которое само по себе является списком адресов электронной почты.

<c:forEach items = "${upcomingSchedule}" var = "conf">
    <div class='scheduled' title = "${conf.subject}" id = "scheduled<c:out value = "${conf.id}"/>">
    ...
    </div>
</c:forEach>

Это отображает один <div> на bean-компонент в списке.

Что касается подсписка, что я хотел бы сделать, так это объединить каждую из записей в списке, чтобы сформировать единую строку, которая будет отображаться как часть атрибута <div>title. Почему? Потому что мы используем библиотеку javascript (mootools), чтобы превратить этот <div> в всплывающую подсказку, а библиотека превращает title в текст всплывающей подсказки.

Итак, если бы ${conf.subject} был «Тема», в конечном итоге я бы хотел, чтобы title<div> был «Тема: [email protected], [email protected] и т. д.», Содержащим все адреса электронной почты суб- список.

Как я могу это сделать с помощью JSP EL? Я стараюсь не помещать блоки скриптлетов в файл jsp.

Возможный дубликат Конкатенация строк JSP EL

BuZZ-dEE 22.10.2015 15:57
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
15
1
50 373
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

Выяснил несколько грязный способ сделать это:

<c:forEach items = "${upcomingSchedule}" var = "conf">
    <c:set var = "title" value = "${conf.subject}: "/>
    <c:forEach items = "${conf.invitees}" var = "invitee">
        <c:set var = "title" value = "${title} ${invitee}, "/>
    </c:forEach>
    <div class='scheduled' title = "${title}" id = "scheduled<c:out value = "${conf.id}"/>">
    ...
    </div>
</c:forEach>

Я просто многократно использую <c:set>, ссылаясь на его собственное значение, для добавления / объединения строк.

Я думаю, что это лучшее, что вы можете сделать без написания собственной функции (что не слишком сложно) в духе fn: join

erickson 17.11.2008 21:50

Вы могли бы это использовать? Похоже, ему нужен массив вместо списка ..

${fn:join(array, ";")}

http://java.sun.com/products/jsp/jstl/1.1/docs/tlddocs/fn/join.fn.html

Если ваш подсписок является ArrayList, и вы делаете это:

<div class='scheduled' title = "${conf.subject}: ${conf.invitees}" id = "scheduled${conf.id}">

вы получаете практически то, что вам нужно.

Единственное отличие состоит в том, что заголовок будет таким: «Тема: [[email protected], [email protected] и т. д.]».

Может быть, тебе хватит.

Это вроде как работает, если базовым списком является ArrayList, но я бы не хотел рисковать, что это была какая-то другая реализация List без той же реализации toString ().

matt b 17.11.2008 22:07

«Чистый» способ сделать это - использовать функцию. Поскольку функция JSTL join не будет работать на Collection, вы можете без особых проблем написать свою собственную и повторно использовать ее повсюду вместо того, чтобы вырезать и вставлять большой кусок кода цикла.

Вам нужна реализация функции и TLD, чтобы ваше веб-приложение знало, где его найти. Поместите их в JAR и поместите в каталог WEB-INF / lib.

Вот схема:

com / x / taglib / core / StringUtil.java

package com.x.taglib.core;

public class StringUtil {

  public static String join(Iterable<?> elements, CharSequence separator) {
    StringBuilder buf = new StringBuilder();
    if (elements != null) {
      if (separator == null)
        separator = " ";
      for (Object o : elements) {
        if (buf.length() > 0)
          buf.append(separator);
        buf.append(o);
      }
    }
    return buf.toString();
  }

}

META-INF / x-c.tld:

<taglib xmlns = "http://java.sun.com/xml/ns/j2ee" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version = "2.0">
  <tlib-version>1.0</tlib-version>
  <short-name>x-c</short-name>
  <uri>http://dev.x.com/taglib/core/1.0</uri>
  <function>
    <description>Join elements of an Iterable into a string.</description>
    <display-name>Join</display-name>
    <name>join</name>
    <function-class>com.x.taglib.core.StringUtil</function-class>
    <function-signature>java.lang.String join(java.lang.Iterable, java.lang.CharSequence)</function-signature>
  </function>
</taglib>

Хотя TLD немного многословен, знание его - хороший навык для любого разработчика, работающего с JSP. И, поскольку вы выбрали для презентации такой стандарт, как JSP, есть большая вероятность, что у вас есть инструменты, которые вам помогут.

Этот подход имеет много преимуществ перед альтернативой добавления дополнительных методов к базовой модели. Эту функцию можно написать один раз и повторно использовать в любом проекте. Он работает со сторонней библиотекой с закрытым исходным кодом. Различные разделители могут поддерживаться в разных контекстах, не загрязняя API модели новым методом для каждого из них.

Самое главное, он поддерживает разделение ролей представления и разработки контроллера модели. Задачи в этих двух областях часто выполняются разными людьми в разное время. Сохранение слабой связи между этими слоями сводит к минимуму сложность и затраты на обслуживание. Когда даже тривиальное изменение, такое как использование другого разделителя в презентации, требует от программиста изменения библиотеки, у вас очень дорогая и громоздкая система.

Класс StringUtil остается неизменным вне зависимости от того, отображается он как функция EL или нет. Единственное необходимое «дополнительное» - это TLD, что тривиально; инструмент может легко его сгенерировать.

Как вы думаете, почему лучше написать собственную функцию jstl, чем еще один метод в компоненте поддержки? Это не провокационный вопрос. В таком случае я бы написал один метод для этого в bean-компоненте, что-то вроде getInviteesAsString (). Что в этом плохого?

alexmeia 18.11.2008 01:04

Я добавил ответ на свой ответ. Я бы задала вам тот же вопрос: как вы думаете, почему лучше изменить компонент поддержки?

erickson 18.11.2008 03:39

Я подумал, что это лучше, потому что он поддерживает разделение ролей разработки и модели-контроллера и оставляет в представлении как можно меньше логики. Но, добавив к вашему ответу, я понимаю, что ваш метод также поддерживает это разделение, а также имеет другие преимущества. Спасибо за добавление.

alexmeia 18.11.2008 12:08

Думаю, это то, что вам нужно:

<c:forEach var = "tab" items = "${tabs}">
 <c:set var = "tabAttrs" value='${tabAttrs} ${tab.key} = "${tab.value}"'/>
</c:forEach>

В этом случае у меня была хэш-карта с идентификатором вкладки (ключ) и URL (значение). Переменная tabAttrs до этого не задана. Таким образом, он просто устанавливает значение в текущее значение tabAttrs ('' для начала) плюс выражение ключ / значение.

ОП уже ответил на свой вопрос :) Отметьте сообщение большой зеленой галочкой.

BalusC 23.01.2010 04:09

Просто поместите строку рядом с переменной с сервера, например:

<c:forEach items = "${upcomingSchedule}" var = "conf">
    <div class='scheduled' title = "${conf.subject}" 

         id = "scheduled${conf.id}">

    ...
    </div>
</c:forEach>

Слишком поздно!!!

Способ реализации библиотек тегов, похоже, значительно изменился с тех пор, как этот ответ был первоначально опубликован, поэтому я в конечном итоге внес некоторые радикальные изменения, чтобы все работало. Мой окончательный результат был:

Файл библиотеки тегов:

<?xml version = "1.0" encoding = "UTF-8"?>
<taglib version = "2.1" xmlns = "http://java.sun.com/xml/ns/javaee" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd">
  <tlib-version>1.0</tlib-version>
  <short-name>string_util</short-name>
  <uri>/WEB-INF/tlds/string_util</uri>
  <info>String Utilities</info>
  <tag>
    <name>join</name>
    <info>Join the contents of any iterable using a separator</info>
    <tag-class>XXX.taglib.JoinObjects</tag-class>
    <body-content>tagdependent</body-content>
    <attribute>
      <name>iterable</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
      <type>java.lang.Iterable</type>
    </attribute>
    <attribute>
      <name>separator</name>
      <required>false</required>
      <rtexprvalue>false</rtexprvalue>
      <type>java.lang.String</type>
    </attribute>
  </tag>

  <tag>
    <name>joinints</name>
    <info>Join the contents of an integer array using a separator</info>
    <tag-class>XXX.taglib.JoinInts</tag-class>
    <body-content>tagdependent</body-content>
    <attribute>
      <name>integers</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
      <type>java.lang.Integer[]</type>
    </attribute>
    <attribute>
      <name>separator</name>
      <required>false</required>
      <rtexprvalue>false</rtexprvalue>
      <type>java.lang.String</type>
    </attribute>
  </tag>
</taglib>

JoinInts.java

public class JoinInts extends TagSupport {

    int[] integers;
    String separator = ",";

    @Override
    public int doStartTag() throws JspException {
        if (integers != null) {
            StringBuilder buf = new StringBuilder();
            if (separator == null) {
                separator = " ";
            }
            for (int i: integers) {
                if (buf.length() > 0) {
                    buf.append(separator);
                }
                buf.append(i);
            }
            try {
                pageContext.getOut().print(buf);
            } catch (IOException ex) {
                Logger.getLogger(JoinInts.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return SKIP_BODY;
    }

    @Override
    public int doEndTag() throws JspException {
        return EVAL_PAGE;
    }

    public int[] getIntegers() {
        return integers;
    }

    public void setIntegers(int[] integers) {
        this.integers = integers;
    }

    public String getSeparator() {
        return separator;
    }

    public void setSeparator(String separator) {
        this.separator = separator;
    }
}

Чтобы использовать это:

<%@ taglib prefix = "su" uri = "/WEB-INF/tlds/string_util.tld" %>

[new Date(${row.key}), <su:joinints integers = "${row.value}" separator = "," />],

Вы можете использовать EL 3.0 Stream API. Например, если у вас есть список строк,

<div>${stringList.stream().reduce(",", (n,p)->p.concat(n))}</div>

Если у вас есть список объектов напр. Person (firstName, lastName), и вы хотите объединить только одно свойство из них (например, firstName), вы можете использовать карту,

<div>${personList.stream().map(p->p.getFirstName()).reduce(",", (n,p)->p.concat(n))}</div>

В вашем случае вы можете использовать что-то подобное (также удалите последний ','),

<c:forEach items = "${upcomingSchedule}" var = "conf">
    <c:set var = "separator" value = ","/>
    <c:set var = "titleFront" value = "${conf.subject}: "/>
    <c:set var = "titleEnd" value = "${conf.invitees.stream().reduce(separator, (n,p)->p.concat(n))}"/>
    <div class='scheduled' title = "${titleFront} ${titleEnd.isEmpty() ? "" : titleEnd.substring(0, titleEnd.length()-1)}" id = "scheduled<c:out value = "${conf.id}"/>">
    ...
    </div>
</c:forEach>

Будь осторожен!EL 3.0 Stream API был завершен до Java 8 Stream API, и он отличается от этого. Они не могут использовать оба API, потому что это нарушит обратную совместимость.

Другие вопросы по теме