使用富文本编辑器保存数据后进行fastjson格式转换异常及序列化处理
最近线上的ELK日志监控爆出几个异常问题,jsonException的解析问题。如下:
message: 2019-04-10 23:37:43,952 ERROR aop.AspectAdvice eid=410724004 not match : - ,
com.alibaba.fastjson.JSONException: not match : - , at
com.alibaba.fastjson.parser.JSONLexerBase.nextTokenWithChar(JSONLexerBase.java:369) at
com.alibaba.fastjson.parser.JSONLexerBase.nextTokenWithColon(JSONLexerBase.java:347) at
com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseExtra(JavaBeanDeserializer.java:411) at
com.alibaba.fastjson.parser.deserializer.ASMJavaBeanDeserializer.parseField(ASMJavaBeanDeserializer.java:69) at
com.alibaba.fastjson.parser.deserializer.ASMJavaBeanDeserializer$InnerJavaBeanDeserializer.parseField(ASMJavaBeanDeserializer.java:86) at
com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:310) at
com.alibaba.fastjson.parser.deserializer.ASMJavaBeanDeserializer.parseRest(ASMJavaBeanDeserializer.java:96) at
Fastjson_ASM_BBSPostVo_369.deserialze(Unknown Source) at
com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:513) at
com.alibaba.fastjson.JSON.parseObject(JSON.java:244) at
com.alibaba.fastjson.JSON.parseObject(JSON.java:220) at
com.alibaba.fastjson.JSON.parseObject(JSON.java:179) at
com.alibaba.fastjson.JSON.parseObject(JSON.java:323) at
.....
由于代码已经在线上运行了2个月左右,突然爆出这样的错误,猜想应该是由输入的信息引起。
这里的业务逻辑是查出一个数据列表,存入redis缓存,第二次查询的时候,如果缓存中有值,则读取缓存中的值。
需要将缓存值解析为对应的对象。对象中有富文本编辑属性的字段
//类似如下结构、报错位置在cacheService.get位置.
public Result<List<BBSPostVo>> getChosen(int type,String objectId){
String cacheString = cacheService.get(CommonCacheKey.BBS_CHOSEN+"_"+type+"_"+objectId);
Result<List<BBSPostVo>> result = new Result<List<BBSPostVo>>();
if(!StringUtils.isEmpty(cacheString)){
result.setData(JSONObject.parseArray(cacheString, BBSPostVo.class));
}else{
result = DubboService.getChosen(type,objectId);
String str = "";
List<BBSPostVo> list = result.getData();
for(BBSPostVo vo :list) {
str += vo.getId() + ",";
}
cacheService.set(CommonCacheKey.BBS_CHOSEN_ID, str,600);
cacheService.set(CommonCacheKey.BBS_CHOSEN+"_"+type+"_"+objectId, JSON.toJSONString(result.getData()),600);
}
return dealForumName(result);
}
在查下redis的 get\set方法如下
public void set(String key, String value, int seconds)
{
if (this.localCache != null) {
this.localCache.set(key, value);
}
Jedis jedis = null;
try
{
jedis = this.pool.getResource();
jedis.setex(key, seconds, value);
}
catch (Exception e)
{
logger.error("redis set error key=" + key, e);
}
finally
{
if (jedis != null) {
jedis.close();
}
}
}
public String get(String key)
{
if (this.localCache != null)
{
Object value = this.localCache.get(key);
if (value != null) {
return (String)value;
}
}
Jedis jedis = null;
try
{
jedis = this.pool.getResource();
String value = jedis.get(key);
if ((value != null) && (this.localCache != null)) {
this.localCache.set(key, value);
}
return value;
}
catch (Exception e)
{
String str1;
return null;
}
finally
{
if (jedis != null) {
jedis.close();
}
}
}
看起来没有什么毛病,且测试环境中测试没有报出如上问题。后来查出,原来是富文本编辑器中可能出现一些特殊字符,在set时,转换为json或存入redis中后,无法正常的读取出来。所以后来采用hession序列化将数据转化成二进制进行存储。后续没有出现解析异常
//这个是Hession工具类
package com.kaishiba.cache.util;
import com.caucho.hessian.io.AbstractHessianInput;
import com.caucho.hessian.io.AbstractHessianOutput;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class HessianUtil
{
private static SerializerFactory serializerFactory = new SerializerFactory();
public static byte[] serialize(Object obj)
throws IOException
{
os = new ByteArrayOutputStream();
AbstractHessianOutput out = new Hessian2Output(os);
out.setSerializerFactory(serializerFactory);
try
{
out.writeObject(obj);
return os.toByteArray();
}
catch (IOException e)
{
throw e;
}
finally
{
if (out != null) {
try
{
out.close();
}
catch (IOException localIOException2) {}
}
}
}
public static Object deserialize(byte[] bytes)
throws IOException
{
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
AbstractHessianInput in = new Hessian2Input(is);
in.setSerializerFactory(serializerFactory);
value = null;
try
{
return in.readObject();
}
catch (IOException e)
{
throw e;
}
finally
{
if (in != null) {
try
{
in.close();
}
catch (IOException localIOException2) {}
}
}
}
}
//这是新的get方法
public <T> T get(String key, Class<T> clazz)
{
Jedis jedis = null;
try
{
jedis = this.pool.getResource();
byte[] value = jedis.get(key.getBytes());
return (T)(value != null ? HessianUtil.deserialize(value) : null);
}
catch (Exception e)
{
T ?;
logger.error("get cache exception,cacheKey:" + key, e);
return null;
}
finally
{
if (jedis != null) {
jedis.close();
}
}
}
//这是新的set方法
public void set(String key, Object value, int seconds)
{
if ((key == null) || (value == null)) {
throw new CacheException("cache key and value cannot be null");
}
Jedis jedis = null;
try
{
jedis = this.pool.getResource();
jedis.setex(key.getBytes(), seconds, HessianUtil.serialize(value));
}
catch (Exception e)
{
logger.error("redis set error key=" + key, e);
}
finally
{
if (jedis != null) {
jedis.close();
}
}
}
在调用的地方变更调用的方法
//方法调用变更后
public Result<List<BBSPostVo>> getChosen(int type,String objectId){
List<BBSPostVo> cacheString = cacheService.getList(CommonCacheKey.BBS_CHOSEN+"_"+type+"_"+objectId, BBSPostVo.class);
Result<List<BBSPostVo>> result = new Result<List<BBSPostVo>>();
if(null != cacheString){
result.setData(cacheString);
}else{
result = DubboService.getChosen(type,objectId);
String str = "";
List<BBSPostVo> list = result.getData();
for(BBSPostVo vo :list) {
str += vo.getId() + ",";
}
cacheService.set(CommonCacheKey.BBS_CHOSEN+"_"+type+"_"+objectId, result.getData(),600);
}
return dealForumName(result);
}
这样的话,问题解决。
注意这里有用到缓存,key值如果没有变化的情况下,对值进行了hession序列化。这样在线上更新的时候会出现异常。旧的数据用新的方法解析。 最好换用新的缓存key值。
原创文章,转载请标明本文链接: 使用富文本编辑器保存数据后进行fastjson格式转换异常及序列化处理