写点什么

jackson1 处理特殊字符有什么问题?

用户头像
BUG侦探
关注
发布于: 2021 年 08 月 02 日
jackson1处理特殊字符有什么问题?

说明

处理 json 是我们日常开发经常需要面对的问题,在经历过多种 json 工具的使用校验下,目前我们主要选择使用 jackson 作为处理 json 的工具。

目前 jackson 主要的两个大版本为 jackson1 和 jackson2,jackson1 已经将近是 10 年前的产物了,最新的一次更新也已经是 5 年前,可谓是古董级别的项目。

下面主要来聊聊使用 jackson1 特殊字符所遇到的问题。

jackson1 支不支持特殊字符?

这里的特殊字符怎么定义?

是指使用 4 个字节表示的表情,比如 emoji 表情

写个 demo 测试

1、首先定义一个 bean,里面包含 name 和 ext 属性:


@JsonIgnoreProperties(ignoreUnknown = true)public class TestBean {    private String name;    private String ext;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getExt() { return ext; }
public void setExt(String ext) { this.ext = ext; }}
复制代码


2、使用 demo


    String testJson = "{"            + "\"name\": \"送 ✨花颜醉\uD83D\uDC9E 招优质歌手 \",  "            + " \"ext\"   : \"送 ✨花颜醉\uD83D\uDC9E 招优质歌手 \""            + "}";
ObjectMapper mapper = new ObjectMapper();
byte[] testGBBytes = testJson.getBytes();
TestBean bean = mapper.readValue(testGBBytes, 0, testGBBytes.length, TestBean.class);
System.out.println("TestBean=" + bean);
复制代码


3、测试结果



从测试结果来看 jackson1 将 json 字符串转成 bean 的时候,如果 json 字符串中的 key 在 bean 中都存在的情况下,jackson1 是可以正常处理特殊字符的。

所以大家正常情况使用 jackson1 是可以正常解析含有特殊字符串的 json。

jackson1 何时不支持特殊字符?

jackson1 也不是所有情况都支持特殊字符,比如在待解析 json 字符串中增加一个 address,而 bean 中不存在此属性:

 String testJson = "{"                + "\"name\": \"送 ✨花颜醉\uD83D\uDC9E 招优质歌手 \",  "                + " \"ext\"   : \"送 ✨花颜醉\uD83D\uDC9E 招优质歌手 \""                + " \"address\"   : \"送 ✨花颜醉\uD83D\uDC9E 招优质歌手 \""                + "}";
ObjectMapper mapper = new ObjectMapper();
byte[] testGBBytes = testJson.getBytes();
TestBean bean = mapper.readValue(testGBBytes, 0, testGBBytes.length, TestBean.class);
System.out.println("TestBean=" + bean);
复制代码


测试结果:



从测试结果中我们发现结果异常了。。。所以我们从测试结果中可以得出以下结论:


  • jackson1 能够处理 bean 中存在的属性值具有特殊字符的情况

  • jackson1 不能够处理 bean 中不存在的属性值具有特殊字符的情况


于是我们需要考虑为什么,bean 中不存在的属性值中有特殊字符就会发生异常呢?

为什么会异常?

下面我们通过追踪 jackson 处理的方式来看看为什么会异常。

首先我们先简化需要测试 json 字符串(h 的具体值是:?):


String testJson = "{\"h\":\"\uD83D\uDE0D\"}";
复制代码


以下将会通过 jackson 将上述 testJson 反序列化成 TestBean,经过分析我们将 testJson 的 byte 数组格式描述如下:



其中蓝色部分,7-10 为特殊字符?部分。

jackson1 在处理“h”的的时候会进入以下部分:



jackson1 在读取到 propName="h" 发现不再 bean 中的 prop 会继续执行到 jp.nextToken 中执行_skipString():



进入_skipString()方法以后会执行到以下部分:



这里主要逻辑是:jackson1 在读取到 bean 中不存在的属性,则会进入跳过这个 key 的 value 的逻辑,测试中需要从第 7 个位置跳到 10 个位置,如以下:



在执行 jackson1 中_skipUtf8_4()前:



在执行 jackson1 中_skipUtf8_4()后:



这里居然只到了 9 的位置,然后对比了_skipUtf8_3 和_skipUtf8_4,发现_skipUtf8_3 中_inputPtr 进行了两次自增:



_skipUtf8_4 也只进行了 2 次自增:



我们再次验证是否是 jackson1 中_skipUtf8_4 是否少执行了一次以下逻辑:


int d = (int) _inputBuffer[_inputPtr++];
复制代码


使用 jackson2 再次验证这个问题发现 jackson2 中逻辑:



发现在 jackson2 中多执行了一次,并且从 debug 结果来看 jackson2 中的_inputPtr 执行完以后变成了 10:



结论


自此可以看出是因为 jackson1 执行 skipstring 操作逻辑中,如果是 4 个 bytes 的特殊字符会少操作一次,导致下次读取的 byte 错误,从而抛出了异常,但是在 jackson2 中修复了这个 bug。


这个问题来源自:业务消费 kafka 消息默认使用 jackson1 反序列化后异常引发的问题,业务方在 bean 中只写了一个 getTextOfExt()方法,导致把 bean toJson 的时候将 bean 中 ext 的特殊字符引入到 textOfExt 中,但是 bean 中并没有 textOfExt 这个属性,于是 json 反序列化 bean 的时候因 bean 中不存在的属性 textOfExt 值中存在 4 个 bytes 的特殊字符,导致抛出异常。


当然根本原因还是 jackson1 对 4 个 byte 字符解析问题,但是也告诉我们在处理 json 问题不需要的属性方法最好还是不要写入需要序列化/反序列化的 bean 中亦或者使用注解屏蔽掉,尽可能保证 bean 的简单性以及 bean 中属性的有效性。


补充


这个 bug 是 jackson 编号[JACKSON-738],并在 2011.12.17 解决了这个 bug,在这之后发布的版本均修复了这个 bug。



查看了之后发布的版本:

  • >=1.8.7

  • >=1.9.3

  • >=2.0

均修复了这个问题,上述问题描述基于使用的 1.6.0 版本的 jackson 遇到的问题。







发布于: 2021 年 08 月 02 日阅读数: 9
用户头像

BUG侦探

关注

还未添加个人签名 2021.06.08 加入

专注于发掘程序员/工程师的有趣灵魂,对工作中的思路与总结进行闪光播报。

评论

发布
暂无评论
jackson1处理特殊字符有什么问题?