宽松 JSON 转换

解析 key=value 花括号结构为 JSON

正在加载工具…

关于 宽松 JSON 转标准 JSON

宽松 JSON 转换工具把 Java 打印出来的伪 JSON 变成真正的 JSON。粘贴一段 Map.toString() 或对象 toString() 产出的 {key=value, nested={...}, list=[...]} 字符串,它会返回一份带缩进的合法 JSON 文档,并把数字、布尔值和 null 还原成正确的类型。

Map.toString() 是一种展示格式,不是序列化格式,它会丢信息。键和值都不带引号,于是「值里恰好有个逗号」和「两个值」长得一模一样;花括号既用于嵌套对象,也出现在消息模板的占位符里;Java 的 null 打印出来就是 null 这四个字母,和内容为 null 的字符串毫无区别。所以恢复结构本质上是在歧义中做重建,本工具用一条写进文档的规则化解最核心的那处歧义:一个标量值在遇到下一个 key= 分隔符时会停下来,但这只在它自己还没吃进过 = 之前成立;一旦吃进了 =,它就被当作自由文本,一直保留逗号和花括号,直到闭合它所在对象的那个花括号为止。

如何使用这个开发者工具

  1. 把字符串粘贴到输入面板。从日志行里一起复制过来的外层引号没关系 —— 工具会先剥掉一层,并解码常见的反斜杠转义,包括 \uXXXX。
  2. 保持「转换数字与布尔值」勾选,可以得到 JSON 的数字、布尔值和 null;当精确文本比类型更重要时,取消勾选让所有标量保持字符串。
  3. 点击转换,读右侧输出。缩进会让恢复出来的嵌套一目了然,而某个值如果吞掉了不该吞的东西,会立刻表现为一条异常长的字符串。
  4. 把所有含 = 的值和原始行对照一遍。那是文档化的启发式规则唯一替你做判断的地方。
  5. 复制结果,并在真正依赖这套结构之前,先用将要消费它的解析器跑一遍。

实例:从日志行里捞出一条告警规则

一段由 Map.toString() 打印出来的服务配置。它同时包含干净的标量、一个列表、一个嵌套对象,以及一个「花括号和逗号都是文本而不是结构」的通知模板。

输入
{name=order-service, port=8080, active=true, tags=[web, api], retry={max=3, delayMs=500}, summary=status={Status}, host={Host}, up={UpMs}ms}
JSON
{
  "name": "order-service",
  "port": 8080,
  "active": true,
  "tags": [
    "web",
    "api"
  ],
  "retry": {
    "max": 3,
    "delayMs": 500
  },
  "summary": "status={Status}, host={Host}, up={UpMs}ms"
}

值得看的是 summary 字段。它的值里有 host= 和 up=,看上去和另外两个键的开头一模一样,按「逗号加空格」粗暴切分会多出三条垃圾条目。但这个值在 status= 处就已经吃进了一个 =,于是它被当作自由文本,一路保留到闭合花括号。retry 和 tags 是结构化的值,靠花括号和方括号配平来识别,启发式规则完全不会碰它们。

各种片段分别会变成什么

类型转换刻意保守:只有当 JSON 数字能原样往返回完全相同的文本时才会转换。凡是往返回来会变样的,一律留作字符串。

输入片段开启转换(默认)关闭转换
port=80808080(数字)"8080"
active=truetrue(布尔值)"true"
ca=nullnull"null"
id=007"007" —— 转成数字会丢掉前导零"007"
ratio=1.50"1.50" —— 1.5 不是同一串文本"1.50"
big=9007199254740993"9007199254740993" —— 超出双精度可精确表示的范围"9007199254740993"
retries=-3-3(数字)"-3"
note=""""
tags=[web, api]["web", "api"]["web", "api"]
retry={max=3}{"max": 3}{"max": "3"}
expression=SuccessRate < 60"SuccessRate < 60" —— 没有 =,因此正常切分同左
msg=a=1, b=2"a=1, b=2" —— 是一个值,不是两个同左

常见问题排查

Lombok @ToString 的输出被整个返回成了一个长字符串。

成因 Lombok 以及 Java record 自动生成的 toString,打印的是带圆括号的 ClassName(field=value, ...)。本解析器是为 Map.toString() 的花括号形式写的,只有遇到 { 才会开始一个对象。

解决办法 把开头的 ClassName( 换成 {,把与之配对的结尾 ) 换成 },内部每一层嵌套的 ClassName( 同样处理。中间的 field=value, 部分本来就是解析器期望的形状,只需要改这两处。

某个值把它后面的键全吞了。

成因 自由文本规则。含 = 的值会一路保留到所在对象的闭合花括号,而带查询串的 URL 会立刻触发它:在 {url=https://api.example.com/v1/jobs?state=failed, method=GET, timeoutMs=3000} 里,state= 让 url 变成了自由文本,于是 method 和 timeoutMs 都被卷了进去。

解决办法 转换前把这个字段挪到所在对象的最后,后面就没有东西可以被吞了。没有任何局部规则能区分「真正的键」和「文本里的片段」,正因如此,这个行为选择了写进文档,而不是搞得很聪明。

一个数字输出成了字符串。

成因 这正是保守规则在起作用。007、1.50 和 9007199254740993 都通不过往返检查:它们会分别变回 7、1.5 和 9007199254740992,因此转换器保留了原始文本。

解决办法 如果你想要值原封不动,那就什么都不用做 —— 补零的订单号或 19 位的 Snowflake ID 本来就该是字符串。确实需要数字时,请在消费端的代码里转换,那里才知道这个字段的含义。

转换失败,只给出一个位置而没有输出。

成因 解析器在那个偏移量处遇到了无法解释的内容:没有 = 的条目、始终没有闭合的花括号或方括号,或者 —— 对日志数据来说最常见 —— 一行被日志框架从中间截断了。

解决办法 对着报出的偏移量往回数括号。被截断的行通常手工补上缺失的右花括号即可;如果这种事发生不止一次,请调高日志框架的字段长度上限。

适合用这个工具的场景

  • 把只以日志形式存在过的告警配置或 Spring 配置,变成可读、可搜索的东西。
  • 从堆栈跟踪或工单里恢复出结构化载荷 —— 那种谁都没保留原始 JSON 的场景。
  • 把 Map.toString() 的转储变成可以直接贴进测试的 JSON 夹具。
  • 快速确认打印出来的对象到底带了哪些字段,以及哪些是「缺失」而不是「为空」。

应该换用别的工具的场景

  • 格式化或校验真正的 JSON。如果文档的键带引号、用的是冒号,请用 JSON 格式化与校验工具,它严格解析并报出精确的错误偏移量。
  • 解析 JSON5、HJSON 或 JSONC。那些是有明确规范和文法的语言;本工具面向的是一种没有规范的调试打印格式,它用的启发式规则放到那些语言上就是错的。
  • 把数据往返回 Java。输出是尽力而为的重建结果,请用它来阅读和排查,而不是当作服务之间的传输格式。

隐私与数据处理

解析完全在当前浏览器标签页内运行,字符串不会上传,也不会跨访问保存。这类字符串来自告警配置和应用日志,因此常常夹带内嵌令牌的 webhook 地址、内网主机名和用户标识。粘贴前请先脱敏,用完请清空输入框。

相关开发者工具

常见问题

为什么消息类的值会保留那些看起来像键的逗号和花括号?

因为一个已经吃进过 = 的标量会被当作自由文本,一路保留到闭合花括号为止。这条规则保住了 summary=status={Status}, host={Host} 这样的模板,没把它撕成好几个键;而干净的值仍然能正常切分。

它支持 Lombok @ToString 吗?

不能直接支持。Lombok 打印的是带圆括号的 ClassName(a=1, b=2),而解析器期望花括号。把最外层的 ClassName( ... ) 连同内部嵌套的一起换成 { ... },其余部分就能原样转换。

关闭类型转换改变了什么?

所有标量都保持字符串,包括 true、false、null 和普通整数。当你要在文本层面比对两个打印出来的对象,或者消费方会被「时而数字时而字符串」的字段搞糊涂时,就用它。

结果可信到能直接提交吗?

用来阅读和排查是可信的。但在把它作为夹具或配置提交之前,请把每一个含 = 的值和原始行重新核对一遍,因为那是解析器替你做决定的唯一地方。

日志行在字符串外面加的引号,它认吗?

认。工具会剥掉一层配对的单引号或双引号,并在解析前解码标准转义 —— \n、\t、\"、\\ 和 \uXXXX。从 JSON 格式的日志记录里复制一个字段出来,拿到的正是这种形状。

为什么不给这种格式写一套正经文法?

因为根本没有文法可写。Map.toString() 的文档明确说它是展示格式、不做任何转义,因此一个含「逗号 key=」的值与两个条目在字节层面完全相同。任何解析器都只能猜;工具能做的有用的事,是每次都用同样的方式猜,并且把规则告诉你。