MVC de printemps: comment afficher les valeurs de date formatées dans JSP EL

voici un haricot de valeur simple annoté avec la nouvelle (à partir de 3.0) convenance de Spring @DateTimeFormat annotation (qui, comme je comprends remplace le pré-3.0 besoin de custom PropertyEditor s selonce DONC, la question):

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
}

quand on bide les valeurs d'une soumission de formulaire à ce widget, le format de date fonctionne parfaitement. C'est, uniquement date de chaînes dans le MM/dd/yyyy le format convertira avec succès enLocalDate objets. Super, nous sommes à mi-chemin y.

cependant, je voudrais aussi pouvoir afficher le LocalDate propriété dans une vue JSP dans le même MM/dd/yyyy format utilisant JSP EL comme so (en supposant que mon contrôleur de printemps a ajouté un attribut widget au model):

${widget.created}

malheureusement, cela n'affichera que la valeur par défaut toString format de LocalDateyyyy-MM-dd format). Je comprends que si j'utilise du printemps balises de formulaire la date s'affiche comme vous le souhaitez:

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

Mais j'aimerais il suffit d'afficher la chaîne de date formatée sans utiliser les étiquettes de forme de ressort. Ou même JSTL!--13--> balise.

venant de Struts2, le HttpServletRequest était enveloppé dans un StrutsRequestWrapper qui permettait à des expressions EL comme celle-ci d'interroger la pile de valeurs D'OGNL. Donc je me demande si spring fournit quelque chose de similaire à ceci pour permettre aux convertisseurs d'exécuter?

EDIT

je me rends compte aussi que lors de l'utilisation du ressort eval balise la date s'affichera selon le motif défini dans le @DateTimeFormat note:

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

fait intéressant, lorsque l'on utilise une coutume PropertyEditor pour formater la date, cette balise n'invoque pas que PropertyEditorgetAsText et donc par défaut à la méthode DateFormat.SHORT décrit dans les docs. Quoi qu'il en soit, j'aimerais savoir s'il y a un moyen d'obtenir le formatage de la date sans avoir à utiliser une étiquette--en utilisant seulement le JSPEL standard.

17
demandé sur Community 2014-12-17 03:42:19

4 réponses

vous pouvez utiliser l'étiquette pour vous fournir ce genre de formatage, tels que l'argent, les données, le temps, et beaucoup d'autres.

vous pouvez ajouter sur vous JSP la référence: <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

et utiliser le formatage comme suit:: <fmt:formatDate pattern="yyyy-MM-dd" value="${now}" />

voici une référence:

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

7
répondu Eduardo Mioto 2015-03-11 22:49:03

pour préciser la réponse D'Eduardo:

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

<fmt:formatDate pattern="MM/dd/yyyy" value="${widget.created}" />
4
répondu Remy Mellet 2015-03-24 22:59:57

j'ai été découragés d'apprendre que les développeurs spring ont décidé de ne pas intégrer El unifié (le langage d'expression utilisé dans JSP 2.1+) avec El Spring en déclarant:

ni JSP ni JSF n'ont une position forte en termes de développement.

mais en m'inspirant du billet JIRA Cité, j'ai créé une coutume ELResolver qui, si la valeur résolue est un java.time.LocalDate ou java.time.LocalDateTime, va tenter pour tirer le @DateTimeFormat valeur de motif afin de formater leString valeur.

Voici le ELResolver (avec ServletContextListener utilisé pour l'amorçage):

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

Enregistrer le ELResolver dans le web.xml:

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

Et maintenant, quand j'ai ${widget.created} dans mon jsp, la valeur affichée sera formatée selon le @DateTimeFormat annotation!

en outre, si le LocalDate ou LocalDateTime l'objet est nécessaire au jsp (et pas seulement le formaté) String representation), vous pouvez toujours accéder à l'objet lui-même en utilisant la méthode directe invocation comme: ${widget.getCreated()}

2
répondu Brice Roncace 2016-05-23 14:25:05

je préfère aussi ne pas faire de formatage via des tags. Je me rends compte que ce n'est peut-être pas la solution que vous recherchez et que vous cherchez un moyen de le faire via des annotations de printemps. Néanmoins, Dans le passé, j'ai utilisé le contourner suivante:

Créer un nouveau getter avec la signature suivante:

public String getCreatedDateDisplay

(vous pouvez modifier le nom du getter si vous préférez.)

dans le getter, formater le created attribut date tel que désiré en utilisant un formatteur tel que SimpleDateFormat.

alors vous pouvez appeler ce qui suit à partir de votre JSP

${widget.createDateDisplay}
1
répondu applejack42 2015-01-06 00:36:33