flowable expression和json字符串中的双引号内容

前言

最近做项目,发现了一批特殊的数据,即特殊字符",本身输入双引号也不是什么特殊的字符,毕竟在存储时就是正常字符,只不过在编码的时候需要转义,转义符是\,然而转义符\也是特殊字符,又需要转义。这就造成了json字符串如果需要",需要很可能转义2次,即\\ \"。一般而言单层"只需要转义一次即可,但是json很可能再次嵌套,所以有时候需要多次转义,这里的\转义符在字符串也是有特殊含义的,毕竟内存存储"并不需要转义,这就出现了平时极难理解的情况。

这些其实还是比较好理解的,即:jvm内存的存储"并没有转义符,只不过编码需要,json字符串需要,负责json序列化和反序列化过程就是不可逆的。比如只能序列化,不能反序列化,但是如果Java执行groovy,或者执行了一些express表达式呢,这些数据会发生变化吗。

准备

准备demo,随意写一个对象,并且写一个groovy和express示例。

    <dependencies><dependency><groupId>org.codehaus.groovy</groupId><artifactId>groovy-all</artifactId><version>3.0.23</version><type>pom</type></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.52</version></dependency></dependencies>

然后写一个groovy脚本

package org.exampledef demo = binding.getVariable("demoBean")
println(demo.name)
demo.name = "demo"
demo.no = "000"
println(demo.name)

写一个bean(自行实现-忽略,普通javabean)和main测试类

public class Main {public static void main(String[] args) throws IOException {TestBean testBean = new TestBean();testBean.setNo("1213");testBean.setName("haha\"");Binding binding = new Binding();binding.setVariable("demoBean", testBean);GroovyShell groovyShell = new GroovyShell(binding);System.out.println("1. " + JSON.toJSONString(testBean));groovyShell.evaluate(new File("/Users/huahua/IdeaProjects/groovy-demo/src/main/java/org/example/demo.groovy"));Object object = groovyShell.getVariable("demoBean");System.out.println("2. " + JSON.toJSONString(object));}
}

这里要注意,Java执行groovyshell,需要的绑定关系是 new GroovyShell(binding),这里的binding命令会自动传递到groovy脚本,执行后如下

json

如上的结果分析:这里的json实际上有转义符,就说明了可读取的json实际上是解释形态,并不是编译运行形态,因为运行态的字符串实际上存放在常量池,是有转义符的,通过javap看看main类

javap -c -p -v Main.class 

但是如果字符串存入内存中,即运行态,并没有转义符,这就触发了问题的来源,json需要转义符,严格说是解释态需要,但是内存不需要,在传递的过程很可能出现丢失转义符导致json反序列化失败,因为json自身会对" { } 这些自身字符进行转义以区分是json字符串还是内容字符串,然而字符串的解释也需要,那就会出现再次转义的情况。

但是内存存储没有字符串的转义的说法,存储的原始信息

Grooovy脚本

笔者开始认为groovy脚本的操作可能会丢失\",如示例,因为实际生产有执行groovy,但是因为是内存操作,内存本身就没有转义符\

注释掉groovy对name的操作,可以看到实际上并没有任何变化,jvm内存本身就是没有转义符存储的,此时name字符串仅仅是对象的属性

json序列化后字符串

证明跟原始的数据没区别,转json后转义符\没丢失

express

后来发现对象经过了类似spring的@Value这样的表达式格式化后,出现了转义符丢失,毕竟javabean的值不是固定不变的,设置表达式,会在各个流程中进行格式化具体的表达式,从而动态的执行不同的参数和返回,计算本质就是输入 计算 输出。

模拟一个示例:最常见的就是工作流,在值传递时,实际上规则引擎,spring等都有express的能力,就以工作流为例bpmn2.0的expression

        <dependency><groupId>org.flowable</groupId><artifactId>flowable-engine-common</artifactId><version>6.5.0</version></dependency>

编写测试代码

public class Main {public static void main(String[] args) throws IOException {TestBean testBean = new TestBean();testBean.setNo("1213");testBean.setName("haha\"");Map<String, Object> map = new HashMap<>();map.put("testBean", testBean);map.put("testBean.name", "haha\"");map.put("testBean.no", testBean.getNo());ExpressionManager expressionManager = new DefaultExpressionManager();Expression expression = expressionManager.createExpression("{\"name\":\"${testBean.name}\",\"no\":\"${testBean.no}\"}");Object obj = expression.getValue(new VariableContainerWrapper(map));System.out.println(obj);}
}

然后执行

可以看到转义符消失,原因实际上已经很明确了express是表达式替换,并不是类似json的解释性格式,jvm内存是没有转义符的,Java本质上是解释性语言,所以class文件有转义符,所以"前的\转义符丢失,如果把这个json送给json反序列化一定报错,识别不了内容双引号的含义。

原因分析

org.flowable.common.engine.impl.de.odysseus.el.tree.impl.ast.AstComposite

其实是在这个示例中,使用字符串拼接的方式执行了替换,内存并没有相关的转义符,这里使用了bean的解析器,毕竟数据是从Javabean获取的

	public Object eval(Bindings bindings, ELContext context) {StringBuilder b = new StringBuilder(16);for (int i = 0; i < getCardinality(); i++) {b.append(bindings.convert(nodes.get(i).eval(bindings, context), String.class));}return b.toString();}

然后解析器实际上有多种

    public Object getValue(ELContext context, Object base, Object property) {context.setPropertyResolved(false);for (int i = 0, l = resolvers.size(); i < l; i++) {Object value = resolvers.get(i).getValue(context, base, property);if (context.isPropertyResolved()) {return value;}}return null;}

如果使用json解析器,那么是不是可以正常呢

 

看看json解析器的判断

懵,果然可以,但是这仅支持jackson,来试一下,确实可以了,但是结果依旧

public class Main {private static ObjectMapper reader = new ObjectMapper();public static void main(String[] args) throws IOException {TestBean testBean = new TestBean();testBean.setNo("1213");testBean.setName("haha\"");Map<String, Object> map = new HashMap<>();JsonNode jsonNode = reader.readTree("{\"name\":\"haha\\\"\",\"no\":\"1213\"}");map.put("testBean", jsonNode);map.put("testBean.name", "haha\"");map.put("testBean.no", testBean.getNo());ExpressionManager expressionManager = new DefaultExpressionManager();Expression expression = expressionManager.createExpression("{\"name\":\"${testBean.name}\",\"no\":\"${testBean.no}\"}");Object obj = expression.getValue(new VariableContainerWrapper(map));System.out.println(obj);}
}

看看结果 

根源还是因为express在flowable的工具类中是字符串拼接,且本身是字符串 

所以仅仅是支持了不同的输入对象逻辑罢了,最终结果并不会有任何变化

总结

通过示例可以看到字符串包括json需要对字符串的"内容进行转义,包括代码编写,class文件,但是jvm内存是不认"的转义符的,存储的就是真实的值,不存在转义的说法,而类似groovy脚本这样的类class语言实际上也是如此,毕竟操作在内存操作,class虚拟机不会有任何不同,毕竟class不一定能反编译Java,但是Java一定是编译为class,所以groovy并不会影响值操作的"结果。

关键点,我们会经常使用expression,不一定是工作流,以flowable为例,flowable支持各种输入传入,表达式也是标准的,但是expression的结果是字符串拼接的,不会考虑解释态,类似json这样的格式,所以输出的结果会丢弃转义符(实际上在字符串载入内存就丢弃了),expression仅仅是真实的还原内存数据,但是这确不是我们特定场景需要的结果,如果传给json反序列化bean就会报错。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/10320.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习数据结构(5)单向链表的实现

&#xff08;1&#xff09;头部插入 &#xff08;2&#xff09;尾部删除 &#xff08;3&#xff09;头部删除 &#xff08;4&#xff09;查找 &#xff08;5&#xff09;在指定位置之前插入节点 &#xff08;6&#xff09;在指定位置之后插入节点 &#xff08;7&#xff09;删除…

14-8C++STL的queue容器

一、queue容器 (1)queue容器的简介 queue为队列容器&#xff0c;“先进先出”的容器 (2)queue对象的构造 queue<T>q; queue<int>que Int;//存放一个int的queue容器 queue<string>queString;//存放一个string的queue容器 (3)queue容器的push()与pop()方…

算法基础学习——快排与归并(附带java模版)

快速排序和归并排序是两种速度较快的排序方式&#xff0c;是最应该掌握的两种排序算法&#xff0c; &#xff08;一&#xff09;快速排序&#xff08;不稳定的&#xff09; 基本思想&#xff1a;分治 平均时间复杂度&#xff1a;O(nlogn) / 最慢O(n^2) / 最快O(n) 步骤&…

团体程序设计天梯赛-练习集——L1-028 判断素数

前言 一道10分的题目&#xff0c;相对来说比较简单&#xff0c;思考的时候要仔细且活跃&#xff0c;有时候在写代码的时候一些代码的出现很多余&#xff0c;并且会影响最后的结果 L1-028 判断素数 本题的目标很简单&#xff0c;就是判断一个给定的正整数是否素数。 输入格式…

安卓(android)订餐菜单【Android移动开发基础案例教程(第2版)黑马程序员】

一、实验目的&#xff08;如果代码有错漏&#xff0c;可查看源码&#xff09; 1.掌握Activity生命周的每个方法。 2.掌握Activity的创建、配置、启动和关闭。 3.掌握Intent和IntentFilter的使用。 4.掌握Activity之间的跳转方式、任务栈和四种启动模式。 5.掌握在Activity中添加…

阿里云 - RocketMQ入门

前言 云消息队列 RocketMQ 版产品具备异步通信的优势&#xff0c;主要应用于【异步解耦】、【流量削峰填谷】等场景对于同步链路&#xff0c;需要实时返回调用结果的场景&#xff0c;建议使用RPC调用方案阿里云官网地址RocketMQ官网地址 模型概述 生产者生产消息并发送至服务…

MySQL注入中load_file()函数的使用

前言 在Msql注入中&#xff0c;load_file()函数在获得webshell以及提权过程中起着十分重要的作用&#xff0c;常被用来读取各种配置文件 而load_file函数只有在满足两个条件的情况下才可以使用&#xff1a; 文件权限&#xff1a;chmod ax pathtofile 文件大小&#xff1a;必须…

HTML<hgroup>标签

例子&#xff1a; 使用hgroup元素标记标题和段落是相关的&#xff1a; <hgroup> <h2>Norway</h2> <p>The land with the midnight sun.</p> </hgroup> 定义和用法&#xff1a; 标签<hgroup>用于包围标题和一个或多个<p&g…

深度学习的应用

目录 一、机器视觉 1.1 应用场景 1.2 常见的计算机视觉任务 1.2.1 图像分类 1.2.2 目标检测 1.2.3 图像分割 二、自然语言处理 三、推荐系统 3.1 常用的推荐系统算法实现方案 四、图像分类实验补充 4.1 CIFAR-100 数据集实验 实验代码 4.2 CIFAR-10 实验代码 深…

Redis篇 Redis如何清理过期的key以及对应的解决方法

Redis设置Key过期时间 在 Redis 中&#xff0c;可以通过特定的命令为 Key 设置过期时间&#xff0c;使得 Key 在一定时间后自动删除&#xff0c;这对于管理缓存、验证码等临时数据非常有用。 解决方法 1. Redis过期删除策略 1.1 如何实现过期策略 对一个 key 设置了过期时间…

Oracle Primavera P6 最新版 v24.12 更新 2/2

目录 一. 引言 二. P6 EPPM 更新内容 1. 用户管理改进 2. 更轻松地标准化用户设置 3. 摘要栏标签汇总数据字段 4. 将里程碑和剩余最早开始日期拖到甘特图上 5. 轻松访问审计数据 6. 粘贴数据时排除安全代码 7. 改进了状态更新卡片视图中的筛选功能 8. 直接从活动电子…

UE5.3 C++ CDO的初步理解

一.UObject UObject是所有对象的基类&#xff0c;往上还有UObjectBaseUtility。 注释&#xff1a;所有虚幻引擎对象的基类。对象的类型由基于 UClass 类来定义。 这为创建和使用UObject的对象提供了 函数&#xff0c;并且提供了应在子类中重写的虚函数。 /** * The base cla…

进阶数据结构——高精度运算

目录 前言一、高精度运算的定义与背景二、高精度运算的实现方式三、高精度运算的算法实现四、高精度运算的应用场景五、代码模版&#xff08;c&#xff09;六、经典例题1.[高精度加法](https://www.lanqiao.cn/problems/1516/learning/?page1&first_category_id1&name…

使用 cmake

使用前注意 : CMake是一种跨平台的构建系统&#xff0c;它用于管理软件构建过程&#xff0c;尤其适合多语言、多配置的项目。CMake不直接构建软件&#xff0c;而是生成特定构建工具&#xff08;如Makefile或Visual Studio项目&#xff09;所需的配置文件。 如果仅仅使用 qt 编…

(1)Linux高级命令简介

Linux高级命令简介 在安装好linux环境以后第一件事情就是去学习一些linux的基本指令&#xff0c;我在这里用的是CentOS7作演示。 首先在VirtualBox上装好Linux以后&#xff0c;启动我们的linux&#xff0c;输入账号密码以后学习第一个指令 简介 Linux高级命令简介ip addrtou…

【ArcMap零基础训练营】03 常用数据网站的数据下载及处理

03 常见数据网站的数据下载及处理 230108直播录像 常见数据下载及nc文件批处理 数据源网站汇总 名称网址备注RESDChttps://www.resdc.cn/中科院地理科学与资源研究所&#xff0c;非会员用户每日5次下载&#xff0c;大部分有用的资源均收费。TPDChttps://data.tpdc.ac.cn/国家青…

MySQL数据库(二)

一 DDL (一 数据库操作 1 查询-数据库&#xff08;所有/当前&#xff09; 1 所有数据库&#xff1a; show databases; 2 查询当前数据库&#xff1a; select database(); 2 创建-数据库 可以定义数据库的编码方式 create database if not exists ax1; create database ax2…

步入响应式编程篇(三)之spring webFlux与R2DBC

spring webFlux与R2DBC 前言Spring webFlux与spring mvc的对比spring mvcspring webFluxSSE的demo R2DBC 前言 前面介绍响应式编程主要用到基于Stream API的Reactor API的方式&#xff0c;但如今业务操作需结合springboot&#xff0c;所以spring webFlux使用的更多&#xff0c…

19.Word:小马-校园科技文化节❗【36】

目录 题目​ NO1.2.3 NO4.5.6 NO7.8.9 NO10.11.12索引 题目 NO1.2.3 布局→纸张大小→页边距&#xff1a;上下左右插入→封面&#xff1a;镶边→将文档开头的“黑客技术”文本移入到封面的“标题”控件中&#xff0c;删除其他控件 NO4.5.6 标题→原文原文→标题 正文→手…

redis数据安全与性能保障

数据安全与性能保障 1、持久化1.1 快照持久化1.2 AOF持久化1.3 重写/压缩AOF文件 2、复制2.1 Redis复制的启动过程2.2 主从链 3、处理系统故障3.1 验证快照文件和AOF文件 4、事务4.1 java中的redis事务使用 如有侵权&#xff0c;请联系&#xff5e; 如有错误&#xff0c;也欢迎…