it-swarm.com.de

Spring MVC: Anzeige formatierter Datumswerte in JSP EL

Hier ist ein einfacher Value-Bean, der mit Spring's neuer (ab 3.0) Bequemlichkeit @DateTimeFormat-Annotation versehen ist (die, wie ich es verstehe, die Notwendigkeit von benutzerdefinierten PropertyEditors gemäß diese SO] - Frage ersetzt:

import Java.time.LocalDate;
import org.springframework.format.annotation.DateTimeFormat;

public class Widget {
  private String name;

  @DateTimeFormat(pattern = "MM/dd/yyyy")
  private LocalDate created;

  // getters/setters excluded
}

Wenn die Werte von einer Formularübergabe an dieses Widget übergeben werden, funktioniert das Datumsformat einwandfrei. Das heißt, nur Datumszeichenfolgen im MM/dd/yyyy-Format können erfolgreich in tatsächliche LocalDate-Objekte konvertiert werden. Toll, wir sind auf halbem Weg.

Ich möchte jedoch auch in der Lage sein, die erstellte LocalDate-Eigenschaft in einer JSP-Ansicht im gleichen MM/dd/yyyy-Format anzuzeigen, wobei JSP EL verwendet wird (vorausgesetzt, mein Feder-Controller hat dem Modell ein Widget-Attribut hinzugefügt):

${widget.created}

Leider wird hier nur das Standardformat toString von LocalDate (im Format yyyy-MM-dd) angezeigt. Ich verstehe, dass, wenn ich die Form-Tags von Spring verwende, das Datum wie gewünscht angezeigt wird:

<form:form commandName="widget">
  Widget created: <form:input path="created"/>
</form:form>

Ich möchte jedoch einfach die formatierte Datumszeichenfolge anzeigen, ohne die Federform-Tags zu verwenden. Oder sogar das fmt:formatDate-Tag von JSTL.

Aus Struts2 stammend, wurde HttpServletRequest in ein StrutsRequestWrapper eingeschlossen, das es EL-Ausdrücken wie diesen ermöglichte, den OGNL-Wertestack tatsächlich abzufragen. Ich frage mich also, ob der Frühling etwas Ähnliches für die Ausführung von Konvertern bereitstellt.

EDIT

Ich stelle auch fest, dass bei Verwendung des eval-Tags von spring das Datum gemäß dem in der @DateTimeFormat-Annotation definierten Muster angezeigt wird:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<spring:eval expression="widget.created"/>

Interessanterweise ruft dieses Tag bei der Verwendung einer benutzerdefinierten PropertyEditor zum Formatieren des Datums NICHT die PropertyEditor-Methode von getAsText auf und verwendet daher standardmäßig DateFormat.SHORT als beschrieben in den Dokumenten . Auf jeden Fall möchte ich trotzdem wissen, ob es eine Möglichkeit gibt, die Datumsformatierung ohne Verwendung eines Tags zu erreichen - nur unter Verwendung von Standard-JSP-EL.

17
Brice Roncace

Ich war entmutigt zu erfahren, dass Spring-Entwickler beschlossen haben, Unified EL (die in JSP 2.1+ verwendete Ausdruckssprache) nicht mit Spring EL zu integrieren.

weder JSP noch JSF haben eine starke Position in Bezug auf unseren Entwicklungsfokus.

Als Inspiration für das zitierte JIRA-Ticket habe ich einen benutzerdefinierten ELResolver erstellt, der versucht, den Java.time.LocalDate-Musterwert abzurufen, wenn der aufgelöste Wert ein Java.time.LocalDateTime oder @DateTimeFormat ist, um den zurückgegebenen String-Wert zu formatieren.

Hier ist die ELResolver (zusammen mit der ServletContextListener, die zum Booten benutzt wird):

    public class DateTimeFormatAwareElResolver extends ELResolver implements ServletContextListener {
      private final ThreadLocal<Boolean> isGetValueInProgress = new ThreadLocal<>();

      @Override
      public void contextInitialized(ServletContextEvent event) {
        JspFactory.getDefaultFactory().getJspApplicationContext(event.getServletContext()).addELResolver(this);
      }

      @Override
      public void contextDestroyed(ServletContextEvent sce) {}

      @Override
      public Object getValue(ELContext context, Object base, Object property) {
        try {
          if (Boolean.TRUE.equals(isGetValueInProgress.get())) {
            return null;
          }

          isGetValueInProgress.set(Boolean.TRUE);
          Object value = context.getELResolver().getValue(context, base, property);
          if (value != null && isFormattableDate(value)) {
            String pattern = getDateTimeFormatPatternOrNull(base, property.toString());
            if (pattern != null) {
              return format(value, DateTimeFormatter.ofPattern(pattern));
            }
          }
          return value;
        }
        finally {
          isGetValueInProgress.remove();
        }
      }

      private boolean isFormattableDate(Object value) {
        return value instanceof LocalDate || value instanceof LocalDateTime;
      }

      private String format(Object localDateOrLocalDateTime, DateTimeFormatter formatter) {
        if (localDateOrLocalDateTime instanceof LocalDate) {
          return ((LocalDate)localDateOrLocalDateTime).format(formatter);
        }
        return ((LocalDateTime)localDateOrLocalDateTime).format(formatter);
      }

      private String getDateTimeFormatPatternOrNull(Object base, String property) {
        DateTimeFormat dateTimeFormat = getDateTimeFormatAnnotation(base, property);
        if (dateTimeFormat != null) {
          return dateTimeFormat.pattern();
        }

        return null;
      }

      private DateTimeFormat getDateTimeFormatAnnotation(Object base, String property) {
        DateTimeFormat dtf = getDateTimeFormatFieldAnnotation(base, property);
        return dtf != null ? dtf : getDateTimeFormatMethodAnnotation(base, property);
      }

      private DateTimeFormat getDateTimeFormatFieldAnnotation(Object base, String property) {
        try {
          if (base != null && property != null) {
            Field field = base.getClass().getDeclaredField(property);
            return field.getAnnotation(DateTimeFormat.class);
          }
        }
        catch (NoSuchFieldException | SecurityException ignore) {
        }
        return null;
      }

      private DateTimeFormat getDateTimeFormatMethodAnnotation(Object base, String property) {
        try {
          if (base != null && property != null) {
            Method method = base.getClass().getMethod("get" + StringUtils.capitalize(property));
            return method.getAnnotation(DateTimeFormat.class);
          }
        }
        catch (NoSuchMethodException ignore) {
        }
        return null;
      }

      @Override
      public Class<?> getType(ELContext context, Object base, Object property) {
        return null;
      }

      @Override
      public void setValue(ELContext context, Object base, Object property, Object value) {
      }

      @Override
      public boolean isReadOnly(ELContext context, Object base, Object property) {
        return true;
      }

      @Override
      public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
        return null;
      }

      @Override
      public Class<?> getCommonPropertyType(ELContext context, Object base) {
        return null;
      }
    }

Registrieren Sie die ELResolver in web.xml:

<listener>
  <listener-class>com.company.el.DateTimeFormatAwareElResolver</listener-class>
</listener>

Und jetzt, wenn ich ${widget.created} in meinem jsp habe, wird der angezeigte Wert entsprechend der @DateTimeFormat-Annotation formatiert!

Wenn das LocalDate- oder LocalDateTime-Objekt von jsp (und nicht nur der formatierten String-Darstellung) benötigt wird, können Sie darüber hinaus mit dem direkten Methodenaufruf wie folgt auf das Objekt selbst zugreifen: ${widget.getCreated()}

2
Brice Roncace

Sie können das Tag verwenden, um diese Art von Formatierungen bereitzustellen, z. B. Geld, Daten, Zeit und viele andere.

Sie können in JSP die Referenz hinzufügen: <%@ taglib prefix="fmt" uri="http://Java.Sun.com/jsp/jstl/fmt" %>

Und verwenden Sie die Formatierung als: <fmt:formatDate pattern="yyyy-MM-dd" value="${now}" />

Folgt unter einer Referenz:

http://www.tutorialspoint.com/jsp/jstl_format_formatdate_tag.htm

7
Eduardo Mioto

Um Eduardo genau zu beantworten:

<%@ taglib prefix="fmt" uri="http://Java.Sun.com/jsp/jstl/fmt" %>

<fmt:formatDate pattern="MM/dd/yyyy" value="${widget.created}" />
4
Remy Mellet

Ich möchte auch keine Formatierung über Tags vornehmen. Ich stelle fest, dass dies möglicherweise nicht die Lösung ist, die Sie suchen, und sucht nach einer Möglichkeit, dies über Federkommentare zu tun. Trotzdem habe ich in der Vergangenheit die folgende Arbeit verwendet:

Erstellen Sie einen neuen Getter mit der folgenden Signatur:

public String getCreatedDateDisplay

(Sie können den Namen des Getters ändern, wenn Sie möchten.)

Formatieren Sie im Getter das Datumsattribut created nach Wunsch mit einem Formatierungsprogramm wie SimpleDateFormat.

Dann können Sie von Ihrer JSP aus folgendes aufrufen

${widget.createDateDisplay}
1
applejack42