数据转JSONString异常,write javaBean error


本文链接:https://blog.csdn.net/pange1991/article/details/79175448
起因
一位同事在接收RocketMQ消息后,为了方便以后排查问题,顺便就用fastjson将消息转成JSONString来打log。模拟代码如下

public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
    MessageExt msg = msgs.get(0);
    logger.info("收到MQ消息,msg={}", JSON.toJSONString(msg));
    ...
}
1
2
3
4
5
然后就报错了,JSON.toJSONString(msg)异常后,代码就不往下走了,关键是业务逻辑也不能执行了。 

本人也喜欢在一些场景下将对象转JSONString打log来作为日后快速排查问题的依据,有时候因为业务需要,甚至将对象转JSONString后存储数据库,而且用的都是fastjson。

所以菊花一紧,觉得有必要排查下这个问题。

猜测
刚开始猜测是不是消息内容有序列化的问题,但是消息能够正常解析和使用,所以排除这种可能。

转而猜测会不会是fastjson的bug,当前使用的fastjson版本是1.2.7,果断升到官方最新版本1.2.44试了下,还是报一样的错。

改用google的gson来解析,成功解析并输出。所以fastjson被打上重点嫌疑人便签。

Do
最后直接分析fastjson源码,最终fastjson表示这个锅它不背。那为什么fastjson不行,而gson可以呢? 
这是由于它们各自的解析方式不一样的导致的,fastjson解析的数据是来自对象的get方法,而gson的则来自于对象的属性。

之前的报错恰恰是由于MessageExt对象的get方法引起的。

MessageExt对象中有两个有趣的get方法,getBornHostBytes()和getStoreHostBytes()。

两个方法差不多,就拿getBornHostBytes()来说事吧。

public ByteBuffer getBornHostBytes() {
    return socketAddress2ByteBuffer(this.bornHost);
}

public static ByteBuffer socketAddress2ByteBuffer(SocketAddress socketAddress) {
    ByteBuffer byteBuffer = ByteBuffer.allocate(8);
    return socketAddress2ByteBuffer(socketAddress, byteBuffer);
}

private static ByteBuffer socketAddress2ByteBuffer(SocketAddress socketAddress, ByteBuffer byteBuffer) {
    InetSocketAddress inetSocketAddress = (InetSocketAddress)socketAddress;
    byteBuffer.put(inetSocketAddress.getAddress().getAddress(), 0, 4);
    byteBuffer.putInt(inetSocketAddress.getPort());
    byteBuffer.flip();
    return byteBuffer;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
具体方法功能就不多说了,问题就出在方法的返回类型ByteBuffer。fastjson会继续解析这个方法的实际返回类型HeapByteBuffer。

最终的问题就出在HeapByteBuffer的get方法上(ps:fastjson解析的都是无参的get方法) 


public char getChar() {
    return Bits.getChar(this, ix(nextGetIndex(2)), bigEndian);
}

public int getInt() {
    return Bits.getInt(this, ix(nextGetIndex(4)), bigEndian);
}

public double getDouble() {
    return Bits.getDouble(this, ix(nextGetIndex(8)), bigEndian);
}
...

 
每解析一个get方法,都会从ByteBuffer中读取相应数量的字节数据,当ByteBuffer的remaining长度小于要获取的字节数时就会抛BufferUnderflowException,

是不是有点眼熟呢,没错,在最开始贴出的异常日志就出现了它的身影 

举个更直观的例子,假设你的ByteBuffer字节长度就是8,第一次你用getInt()获取到了4个字节的数据,第二次你用getDouble()想获取8个字节的数据时就会抛这个异常了,因为remaining这个时候的值是4,小于想获取的长度。

more
fastjson实际上是可以通过设置SerializerFeature规避这个问题的

方法一:

JSON.toJSONString(msg, SerializerFeature.IgnoreNonFieldGetter); 
getBornHostBytes()方法在MessageExt并没有对应的属性bornHostBytes, 设置后,fastjson就会跳过getBornHostBytes()的解析。 
方法二:

JSON.toJSONString(msg, SerializerFeature.IgnoreErrorGetter); 
fastjson会忽略有问题的get异常解析,返回其它正常的解析数据。

SerializerFeature.IgnoreErrorGetter 在fastjson 1.2.7版本中没有,1.2.44版本中有。

另外,JSONObject.toJSONString和JSON.toJSONString本质上是相同的。

总结
吃一堑长一智,在我们实际开发中,DTO对象尽量使用失血模型,不要在get方法中做些不必要的操作。还有不要将DTO的get方法返回类型设为ByteBuffer哦。

如果没有特殊情况,RocketMQ的消息就打印body部分(真正的消息内容)就够看了,不要打印MessageExt对象。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
com.alibaba.fastjson.JSONException 是一个 JSON 解析/生成异常类,通常是由 fastjson 库在处理 JSON 字符串时发生错误导致的。 出现 "com.alibaba.fastjson.JSONException: write JavaBean error" 错误一般是由以下原因引起的: 1. JavaBean 对象和 JSON 字符串之间的属性不匹配。例如,JavaBean 对象缺少某些属性或 JSON 字符串包含了多余的属性,导致无法正确地将 JSON 字符串换成 JavaBean 对象,或者无法将 JavaBean 对象换成相应的 JSON 字符串。 解决方法:检查 JavaBean 对象和 JSON 字符串之间的属性是否一一对应并正确设置。 2. JavaBean 对象的属性和 JSON 字符串的属性类型不匹配。例如,JavaBean 对象中的属性是长整型,但是 JSON 字符串中对应的属性是字符串类型,导致类型换错误。 解决方法:确保 JavaBean 对象的属性类型和 JSON 字符串中对应的属性类型一致。 3. JavaBean 对象中存在循环引用。即 JavaBean 对象的属性中包含了对自身的引用,导致 fastjson 无法正确处理。 解决方法:避免在 JavaBean 对象中出现循环引用,可以通过在 JavaBean 对象中使用 @JSONField(serialize=false) 注解来标记该属性不被序列化。 总之,"com.alibaba.fastjson.JSONException: write JavaBean error" 错误通常是由于 fastjson 在处理 JSON 字符串和 JavaBean 对象之间的换时出现了问题。要解决这个错误,需要检查 JavaBean 对象和 JSON 字符串之间的属性是否匹配,并且确保属性类型一致。另外,要避免在 JavaBean 对象中出现循环引用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值