fastjson迁移jackson之解析‘$ref‘

fastjson迁移jackson

性能比较

这是我自己用jmh测试的记录
序列化

对象数量hutoolfastjsonjackson
1000.17s0.17s0.52s
10000.32s0.22s0.59s
100001.73s0.61s0.68s
1000006.30s1.16s0.91s
20000010.95s1.76s1.26s

反序列化

对象数量hutoolfastjsonjackson
1000.18s0.29s0.57s
10000.41s0.26s0.64s
100001.84s0.46s0.68s
1000006.22s1.23s1.24s
20000012.83s2.57s1.83s

可见数据量上去后,fastjson和jackson差距不大

迁移遇到的bug

默认情况下,fastjson会启用jsonpath来解决循环依赖。可以使用SerializerFeature.DisableCircularReferenceDetect来关闭此选项,但不推荐。但输出的带$ref的json不能直接用jackson读取。所以自己封装了一个Moudle:

import com.alibaba.fastjson.JSONPath;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.BeanDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.Objects;

/**
 * @Date : 2022/7/30
 * @Description : 兼容fastjson->jackson 迁移时$ref的path问题
 */
public class BeanRefMoudle extends Module {

    @Override
    public String getModuleName() {
        return "beanRefMoudle";
    }


    @Override
    public Version version() {
        return Version.unknownVersion();
    }


    @Override
    public void setupModule(SetupContext context) {
        context.addBeanDeserializerModifier(new BeanDeserializerModifier() {
            @Override
            public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
                if (deserializer instanceof BeanDeserializer) {
                    return new BeanRefDeserializer((BeanDeserializer) deserializer);
                }
                return deserializer;
            }
        });
    }


}

@Slf4j
class BeanRefDeserializer extends BeanDeserializer {

    private static final String REF="$ref";

    public BeanRefDeserializer(BeanDeserializer src) {
        super(src);
    }

    private static Object getSecondRootValue(JsonStreamContext pctx) {
        if (Objects.isNull(pctx)) {
            return null;
        }
        JsonStreamContext real;
        do {
            real = pctx;
            pctx = pctx.getParent();
        } while (!pctx.inRoot());
        return real.getCurrentValue();
    }

    @Override
    public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) throws IOException {
        if(Objects.equals(REF,p.getCurrentName())){
            return match$ref(p);
        }
        return super.deserializeFromObject(p, ctxt);
    }

    private Object match$ref(JsonParser p) throws IOException {
        p.nextToken();
        try {
            String ref=p.getValueAsString();
            JsonStreamContext pctx = p.getParsingContext();
            return JSONPath.eval(getSecondRootValue(pctx), ref);
        } catch (Exception e) {
            log.error("jackson解析{}异常",REF,e);
            return null;
        }finally {
            p.nextToken();
        }
    }
}

再注册Moudle到SPI
resource文件夹下新建META-INF文件夹,META-INF文件夹下新建services文件夹,services文件夹下新建com.fasterxml.jackson.databind.Module文件,文件内容为你的模块全类名(com.xxx.xxxx.framework.json.BeanRefMoudle)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值