# Java 踩坑——ProcessBuilder 与 Json 字符串参数

随笔 3 / 8
阅读约需 2 分钟
目录

今天在调试一个用到 ProcessBuilder 执行外部指令的 Spring Boot 项目时,踩到了一个和字符串转义有关的坑,记录一下。

问题背景

项目中有需求调用一个外部的 Shell 脚本,脚本有个参数是 Json 字符串,而这个 Json 字符串在脚本内又会被传入另一个程序中调 Gson 解析。不从项目调用而是直接 Shell 运行的时候一切正常,但是在项目中调用时,目标解析 Json 字符串会报错。

问题分析

java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 2 path $

日志已经很清楚了,显然是应该传入的 Json 参数是作为非 Json 字符串出现的。于是怀疑 ProcessBuilder 在传入参数时有问题。

解决

在直接调用时,Json 属性的引号需要转义,因为需要让 Shell 知道如何正确分割参数,因此参数是这样的:

Terminal window
-confProp "{\"key\":\"value\"}"

注意,外侧的两个引号是 Shell 的引号,标识 Json 字符串整体是一个参数,内侧的引号是 Json 的引号

但在 Java 中,传入 ProcessBuilder 进行命令构建的时候无需转义,它是直接分配参数数组的,因此参数应该是这样的:

// 正确
// 应该得到的 command 是 [sh, script.sh, -confProp, {"key":"value"}]
new ProcessBuilder("sh", "script.sh", "-confProp", "{\"key\":\"value\"}");
// 错误, 虽然看似传入的参数和 Shell 一样,但是由于 ProcessBuilder 直接构建
// 参数数组,因此这里的命令实际上是 [sh, script.sh, -confProp, "{\"key\":\"value\"}"]
new ProcessBuilder("sh", "script.sh", "-confProp", "\"{\\\"key\\\":\\\"value\\\"}\");

More

实际上我是把这个配置项写在了 application.properties 中,然后在项目中读取配置项,因此写配置项的时候还需要注意一点,Spring 读取的配置项是不需要转义的,因此配置项应该是这样的:

# 正确
example.conf={"key":"value"}
# 错误
example.conf="{\"key\":\"value\"}"
下一篇: Spring Boot 应用配置小贴士
写下此篇时暂时不是懒狗的星语

这是开发的责任感和前瞻性的问题。不兼容的改变不应该轻易被加入到有许多依赖代码的软件中。升级所付出的代价可能是巨大的。
—— 《语义化版本》


随笔 系列