JSTL如何提取位置动态变化的category::value格式token值
问题背景
待处理原始字符串格式如下,开头为时间戳,后续通过!分隔不定数量的分类名::值格式键值对,键值对的排列位置、总数量均不固定,最少1个最多可达10个:
22-04-19 20:34:00!Color::blue!Manufacturer::Ford!Seats::4!Climatronic::yes
原有实现通过fn:contains判断键名存在后,两次截断字符串取值,存在明显缺陷:目标键不存在时不会重置变量,会复用之前已赋值的变量值填充空单元格,同时基于索引遍历的c:forTokens方案无法适配键值对数量浮动的场景。原有问题代码如下:
<c:if test="${fn:contains(dataset, 'Color')}"> <c:set var="color1" value="${fn:substringAfter(dataset, 'Color::')}" /> <c:set var="color2" value="${fn:substringBefore(color1, '!')}" /> </c:if>
实现方案
方案1:双层分割+Map存储(推荐,无依赖适配任意数量键值对)
核心逻辑是先按!拆分全字符串,跳过第一个时间片段后,对每个属性片段按::拆分出键和值,存入独立Map结构,后续直接按键取值即可,完全避免旧值污染。
<%-- 每次处理前初始化空Map,从根源避免旧值残留 --%> <c:set var="attrMap" value="${{} }" /> <%-- 第一层分割:按!拆分所有片段 --%> <c:forTokens items="${dataset}" delims="!" var="segment" varStatus="segStatus"> <%-- 跳过第一个片段,也就是开头的时间戳 --%> <c:if test="${not segStatus.first}"> <%-- 第二层分割:对属性片段按::拆分键和值 --%> <c:forTokens items="${segment}" delims="::" var="part" varStatus="partStatus"> <c:choose> <c:when test="${partStatus.index == 0}"> <c:set var="currentKey" value="${part}" /> </c:when> <c:when test="${partStatus.index == 1}"> <c:set target="${attrMap}" property="${currentKey}" value="${part}" /> </c:when> </c:choose> </c:forTokens> </c:if> </c:forTokens> <%-- 后续取值直接从Map获取即可,不存在的键返回null,不会串值 --%> <tr> <td>颜色:${attrMap.Color}</td> <td>厂商:${attrMap.Manufacturer}</td> <td>座位数:${attrMap.Seats}</td> <td>自动空调:${attrMap.Climatronic}</td> </tr>
该方案优势:
- 不需要提前预知键值对数量,也不依赖键值对的固定排列顺序
- 所有键值对一次性解析完成,后续取值不需要重复做字符串截断
- 完全基于JSTL核心标签和标准EL表达式实现,不需要引入第三方标签库
方案2:取值前重置变量(轻量场景适配)
如果不想引入Map存储全量键值对,可以在每个键的取值逻辑前,先把对应变量手动重置为null,再做存在性判断和截断,即可解决旧值复用的问题。注意判断键存在时要把::后缀带上,避免键名部分匹配导致的误判(比如存在OuterColor键时,仅判断Color会出现误匹配)。
<%-- 取颜色值,先重置变量 --%> <c:set var="colorVal" value="${null}" /> <c:if test="${fn:contains(dataset, 'Color::')}"> <c:set var="colorVal" value="${fn:substringBefore(fn:substringAfter(dataset, 'Color::'), '!')}" /> </c:if> <%-- 取厂商值,先重置变量 --%> <c:set var="manufacturerVal" value="${null}" /> <c:if test="${fn:contains(dataset, 'Manufacturer::')}"> <c:set var="manufacturerVal" value="${fn:substringBefore(fn:substringAfter(dataset, 'Manufacturer::'), '!')}" /> </c:if> <%-- 其余键按相同逻辑实现即可 --%>
内容的提问来源于stack exchange,提问作者Serpher
相关产品推荐
相关产品推荐

