使用富文本编辑器保存数据后进行fastjson格式转换异常及序列化处理

作者: admin 分类: *json 发布时间: 2019-04-16 22:27  阅读: 355 views

最近线上的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格式转换异常及序列化处理

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

更多阅读