You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Common interface for all implementations of a conversion service, to be used during template execution.
Thymeleaf conversion services work in a way similar to Spring Framework's ConversionService interface, but this is a generic mechanism (not dependent on Spring).
Default implementation —registered by org.thymeleaf.standard.StandardDialect— is StandardConversionService, which performs some standard conversions, but the Spring Standard Dialect used by the Thymeleaf + Spring integration module automatically registers an implementation of this interface that delegates on any existing Spring ConversionService objects (thus using the Converters and Formatters registered at the Spring Application Context).
Important: there is one conversion that implementations of this interface should always implement, because it is heavily used at the Thymeleaf core: conversion of any Object to String.
The implementation of this interface that should be used is specified as an execution attribute by the Standard Dialects (see org.thymeleaf.standard.StandardDialect.getExecutionAttributes()).
Implementations of this interface should be thread-safe.
Since:
2.1.0
Author:
Daniel Fernández
Per docs, it looks like we can customize IStandardConversionService for further usages. I know that we can register Formatters and it will be applied for toString calls. But I want to have two template engines that will allow me to use different rules for formatting. In that case, I create a new SpringTemplateEngine for internal usages. And register SpringStandardDialect witgh customized IStandardConversionService.
It looks like
SpringTemplateEngine engine = new SpringTemplateEngine();
SpringStandardDialect dialect = new SpringStandardDialect();
dialect.setConversionService(new CustomConversionService());
engine.setDialects(Set.of(dialect);
engine.setEnableSpringELCompiler(true);
engine.setCacheManager(null);
thymeleaf/thymeleaf#223 implements conversions for placeholders like this "${{placeholder}}".
So CustomConversionService should be called for processing toString call for my placeholder.
Surprise!
SpringStandardDialect has its own property of IStandardConversionService.
public class SpringStandardDialect extends StandardDialect {
public static final String NAME = "SpringStandard";
public static final String PREFIX = "th";
public static final int PROCESSOR_PRECEDENCE = 1000;
public static final boolean DEFAULT_ENABLE_SPRING_EL_COMPILER = false;
public static final boolean DEFAULT_RENDER_HIDDEN_MARKERS_BEFORE_CHECKBOXES = false;
private boolean enableSpringELCompiler = DEFAULT_ENABLE_SPRING_EL_COMPILER;
private boolean renderHiddenMarkersBeforeCheckboxes = DEFAULT_RENDER_HIDDEN_MARKERS_BEFORE_CHECKBOXES;
private static final Map<String,Object> REACTIVE_MODEL_ADDITIONS_EXECUTION_ATTRIBUTES;
// This execution attribute will force the asynchronous resolution of the WebSession (not the real creation of a
// persisted session) before view execution. This will avoid the need to block in order to obtain the WebSession
// from the ServerWebExchange during template execution.
// NOTE here we are not using the constant from the ReactiveThymeleafView class (instead we replicate its same
// value "ThymeleafReactiveModelAdditions:" so that we don't force the initialisation of that class in
// non-WebFlux environments.
private static final String WEB_SESSION_EXECUTION_ATTRIBUTE_NAME =
"ThymeleafReactiveModelAdditions:" + SpringContextUtils.WEB_SESSION_ATTRIBUTE_NAME;
// These variables will be initialized lazily following the model applied in the extended StandardDialect.
private IExpressionObjectFactory expressionObjectFactory = null;
private IStandardConversionService conversionService = null;
But StandardDialect has its own property too. So invoking the setter for SpringStandardDialect will set the property to StandardDialect.
public class StandardDialect
extends AbstractProcessorDialect
implements IExecutionAttributeDialect, IExpressionObjectDialect {
public static final String NAME = "Standard";
public static final String PREFIX = "th";
public static final int PROCESSOR_PRECEDENCE = 1000;
// We will avoid setting this variableExpressionEvaluator variable to "OgnlVariableExpressionEvaluator.INSTANCE"
// in order to not cause this OGNL-related class to initialize, therefore introducing a forced dependency on OGNL
// to Spring users (who don't need OGNL at all).
private IStandardVariableExpressionEvaluator variableExpressionEvaluator = null;
// These variables will be initialized lazily if needed (because no value is set to them via their setters)
// This should improve startup times (esp. Jackson for the JS serializer) and avoid losing time initializing
// objects that might not be used after all if they are overridden via setter.
private IStandardExpressionParser expressionParser = null;
private IStandardConversionService conversionService = null;
private IStandardJavaScriptSerializer javaScriptSerializer = null;
private IStandardCSSSerializer cssSerializer = null;
// Note this is not settable - just lazily initialized
private IExpressionObjectFactory expressionObjectFactory = null;
So calling of getConversionService() method will not return the value of set property due to the getter implementation.
@Override
public IStandardConversionService getConversionService() {
if (this.conversionService == null) {
this.conversionService = new SpringStandardConversionService();
}
return this.conversionService;
}
In that case, the calling of setConversionService has no effect... It will use SpringStandardConversionService as a conversion service.
In my opinion, such behaviour is unclear... Better to throw an exception if you don't want to allow to set your own conversion service.
The workaround is overriding getter:
public class CustomStandardDialect extends SpringStandardDialect {
private final IStandardConversionService conversionService = new CustomConversionService();
@Override
public IStandardConversionService getConversionService() {
return conversionService;
}
}
Hi,
IStandardConversionService docs:
Per docs, it looks like we can customize IStandardConversionService for further usages. I know that we can register Formatters and it will be applied for toString calls. But I want to have two template engines that will allow me to use different rules for formatting. In that case, I create a new SpringTemplateEngine for internal usages. And register SpringStandardDialect witgh customized IStandardConversionService.
It looks like
thymeleaf/thymeleaf#223 implements conversions for placeholders like this "${{placeholder}}".
So CustomConversionService should be called for processing toString call for my placeholder.
Surprise!
SpringStandardDialect has its own property of IStandardConversionService.
But StandardDialect has its own property too. So invoking the setter for SpringStandardDialect will set the property to StandardDialect.
So calling of getConversionService() method will not return the value of set property due to the getter implementation.
In that case, the calling of setConversionService has no effect... It will use SpringStandardConversionService as a conversion service.
In my opinion, such behaviour is unclear... Better to throw an exception if you don't want to allow to set your own conversion service.
The workaround is overriding getter:
Version: The latest version of thymeleaf
https://github.com/thymeleaf/thymeleaf-spring/blob/3.0-master/thymeleaf-spring5/src/main/java/org/thymeleaf/spring5/dialect/SpringStandardDialect.java
The text was updated successfully, but these errors were encountered: