Thymeleaf 3.1中XML模板th:text转义问题及IPostProcessorDialect实现咨询
解决方案:取消Thymeleaf 3.1 XML模板中th:text的默认XML转义
方案一:替换默认的StandardTextTagProcessor(推荐)
直接替换Thymeleaf默认的文本处理器,从根源跳过转义逻辑,比反转义更可靠:
- 实现自定义的TextTagProcessor,继承
StandardTextTagProcessor,重写produceEscapedOutput方法跳过转义:
import org.thymeleaf.context.ITemplateContext; import org.thymeleaf.engine.AttributeName; import org.thymeleaf.model.IProcessableElementTag; import org.thymeleaf.standard.processor.StandardTextTagProcessor; import org.thymeleaf.templatemode.TemplateMode; public class NoEscapeTextTagProcessor extends StandardTextTagProcessor { public NoEscapeTextTagProcessor(final TemplateMode templateMode, final String dialectPrefix) { super(templateMode, dialectPrefix); } @Override protected void produceEscapedOutput( final ITemplateContext context, final IProcessableElementTag tag, final AttributeName attributeName, final String attributeValue) { // 直接输出原始值,跳过XML转义 outputText(context, tag, attributeValue); } }
- 创建自定义Dialect,替换默认的StandardTextTagProcessor:
import org.thymeleaf.dialect.AbstractProcessorDialect; import org.thymeleaf.processor.IProcessor; import org.thymeleaf.standard.StandardDialect; import org.thymeleaf.templatemode.TemplateMode; import java.util.HashSet; import java.util.Set; public class NoEscapeStandardDialect extends AbstractProcessorDialect { public static final String DIALECT_NAME = "No-Escape Standard Dialect"; public NoEscapeStandardDialect() { // 继承StandardDialect的前缀和优先级 super(DIALECT_NAME, StandardDialect.PREFIX, StandardDialect.PROCESSOR_PRECEDENCE); } @Override public Set<IProcessor> getProcessors(final String dialectPrefix) { final Set<IProcessor> processors = new HashSet<>(); // 替换XML模板模式下的TextTagProcessor processors.add(new NoEscapeTextTagProcessor(TemplateMode.XML, dialectPrefix)); // 若需覆盖其他模板模式,可添加对应处理器 return processors; } }
- 在Spring Boot中注册自定义Dialect:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.thymeleaf.dialect.IDialect; @Configuration public class ThymeleafConfig { @Bean public IDialect noEscapeStandardDialect() { return new NoEscapeStandardDialect(); } }
此方法直接替换默认处理器,所有XML模板的th:text都会输出原始文本,无需修改任何模板文件。
方案二:通过IPostProcessorDialect实现反转义(适配你的现有思路)
若坚持使用PostProcessor方式,需确保正确捕获转义文本并反转义(注意:反转义存在特殊字符还原出错的风险,不如方案一可靠):
- 实现自定义TextPostProcessor:
import org.thymeleaf.engine.ITemplateHandler; import org.thymeleaf.postprocessor.ITemplateHandlerPostProcessor; import org.thymeleaf.templatemode.TemplateMode; public class UnescapeTextPostProcessor implements ITemplateHandlerPostProcessor { @Override public TemplateMode getTemplateMode() { // 仅针对XML模板模式 return TemplateMode.XML; } @Override public int getPrecedence() { // 设置高优先级,确保在默认PostProcessor之后执行 return Integer.MAX_VALUE; } @Override public ITemplateHandler postProcess(final ITemplateHandler templateHandler) { return new UnescapeTemplateHandler(templateHandler); } }
- 实现自定义TemplateHandler处理反转义:
import org.thymeleaf.engine.AbstractDelegatingTemplateHandler; import org.thymeleaf.util.XmlEscape; public class UnescapeTemplateHandler extends AbstractDelegatingTemplateHandler { public UnescapeTemplateHandler(final ITemplateHandler delegate) { super(delegate); } @Override public void handleText(final char[] text, final int offset, final int len) { // 反转义还原原始文本 final String originalText = XmlEscape.unescapeXml(new String(text, offset, len)); super.handleText(originalText.toCharArray(), 0, originalText.length()); } }
- 实现IPostProcessorDialect并注册PostProcessor:
import org.thymeleaf.dialect.AbstractDialect; import org.thymeleaf.dialect.IPostProcessorDialect; import org.thymeleaf.postprocessor.IPostProcessor; import java.util.Collections; import java.util.Set; public class UnescapePostProcessorDialect extends AbstractDialect implements IPostProcessorDialect { public static final String DIALECT_NAME = "Unescape PostProcessor Dialect"; public UnescapePostProcessorDialect() { super(DIALECT_NAME); } @Override public Set<IPostProcessor> getPostProcessors() { return Collections.singleton(new UnescapeTextPostProcessor()); } }
- 注册该Dialect到Spring容器:
@Configuration public class ThymeleafConfig { @Bean public IDialect unescapePostProcessorDialect() { return new UnescapePostProcessorDialect(); } }
注意事项
- 优先选择方案一,从根源避免转义,规避反转义可能带来的异常。
- 若模板包含多种模式(如XML+HTML),可在自定义Processor中针对特定
TemplateMode处理,避免影响其他模板。 - 测试时需覆盖含特殊字符(
<>&'"")的文本场景,验证输出是否符合预期。
内容的提问来源于stack exchange,提问作者Sagar
相关产品推荐
相关产品推荐

