java:json-path支持fastjson作为JSON解析提供者的技术实现

1. 简介

1.1 json-path

json-path 是一个强大的JSON查询库,允许用户使用类似XPath的语法来查询JSON数据。它提供了灵活的API,可以在Java应用中方便地处理JSON数据。

1.2 fastjson

fastjson 是阿里巴巴开源的高性能JSON处理库,具有快速的序列化和反序列化能力,广泛应用于Java项目中。

1.3 为什么需要让json-path支持fastjson?

json-path默认支持多种JSON解析库,如jackson、gson等,但不直接支持fastjson。通过扩展json-path,使其支持fastjson,可以让项目在使用json-path的同时,继续享受fastjson的高性能优势。

2. 技术实现原理

2.1 核心接口

json-path通过以下两个核心接口来扩展JSON解析支持:

  1. JsonProvider:负责JSON解析和基本的JSON操作
  2. MappingProvider:负责JSON对象与Java对象之间的映射

2.2 实现架构

我们的实现包括以下几个核心类:

类名作用实现方式
FastjsonJsonProvider实现JsonProvider接口扩展AbstractJsonProvider
FastjsonMappingProvider实现MappingProvider接口直接实现接口
XJsonPathConfiguration自动检测和配置默认解析器静态初始化+自动检测

2.3 关键实现细节

2.3.1 FastjsonJsonProvider

FastjsonJsonProvider扩展了AbstractJsonProvider,使用JSON.parseJSON.parseObject实现JSON解析:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.ParserConfig;
import com.google.common.base.MoreObjects;
import com.jayway.jsonpath.spi.json.AbstractJsonProvider;

import java.io.InputStream;
import java.nio.charset.Charset;

/**
 * Fastjson 实现 {@link com.jayway.jsonpath.spi.json.JsonProvider}
 */
public class FastjsonJsonProvider extends AbstractJsonProvider {
	private final ParserConfig parserConfig;
    public FastjsonJsonProvider() {
        this(null);
    }
    public FastjsonJsonProvider(ParserConfig parserConfig) {
        this.parserConfig = MoreObjects.firstNonNull(parserConfig, ParserConfig.getGlobalInstance());
    }
    @Override
    public Object parse(String json) {
        return JSON.parse(json, parserConfig);
    }

    @Override
    public Object parse(InputStream is, String charset) {
        try {
            return JSON.parseObject(is, Charset.forName(charset), JSON.class, parserConfig);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Object createMap() {
        return new JSONObject();
    }

    @Override
    public Object createArray() {
        return new JSONArray();
    }

    @Override
    public String toJson(Object obj) {
        return JSON.toJSONString(obj);
    }
}
2.3.2 FastjsonMappingProvider

FastjsonMappingProvider实现了MappingProvider接口,使用fastjson的TypeUtils.cast方法简化实现:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.util.TypeUtils;
import com.google.common.base.MoreObjects;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.spi.mapper.MappingProvider;
import com.jayway.jsonpath.TypeRef;

/**
 * Fastjson 实现 {@link MappingProvider}
 */
public class FastjsonMappingProvider implements MappingProvider {
    private final ParserConfig parserConfig;
    public FastjsonMappingProvider() {
        this(ParserConfig.getGlobalInstance());
    }
    public FastjsonMappingProvider(ParserConfig parserConfig) {
        this.parserConfig = MoreObjects.firstNonNull(parserConfig, ParserConfig.getGlobalInstance());
    }
    @Override
    public <T> T map(Object source, Class<T> targetType, Configuration configuration) {
        if (targetType.isInstance(source)) {
            return targetType.cast(source);
        }
        if (source instanceof String) {
        	// 对于字符串,直接使用JSON.parseObject解析
            return JSON.parseObject((String) source, targetType, parserConfig);
        }
        // 统一使用TypeUtils.cast解析
        return TypeUtils.cast(source, targetType, parserConfig);
    }

    @Override
    public <T> T map(Object source, TypeRef<T> targetType, Configuration configuration) {
        if (source instanceof String) {
            // 对于字符串,直接使用JSON.parseObject解析
            return JSON.parseObject((String) source, targetType.getType(), parserConfig);
        }
        // 统一使用TypeUtils.cast解析
        return TypeUtils.cast(source, targetType.getType(), parserConfig);
    }
}
2.3.3 XJsonPathConfiguration

XJsonPathConfiguration负责自动检测和配置默认解析器:

import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.spi.json.JsonProvider;
import com.jayway.jsonpath.spi.mapper.JsonSmartMappingProvider;
import com.jayway.jsonpath.spi.mapper.MappingProvider;

import java.util.Collections;
import java.util.Set;

/**
 * JsonPath配置扩展类<br>
 * 作为{@link Configuration}的扩展<br>
 * 自动检测系统中可用的JSON解析库并设置默认配置<br>
 * 优先级为:fastjson > jackson > gson > jettison > org.json > TapestryJson
 */
public final class XJsonPathConfiguration {
    /**
     * 标记是否已经自动设置默认配置<br>
     * 防止重复初始化
     */
    private static boolean autoSet = false;
    /**
     * 静态初始化块,在类加载时自动设置默认配置
     */
    static {
        autoSetDefaults();
    }
    
    private XJsonPathConfiguration() {
        // 私有构造方法,防止实例化
    }

    /**
     * 自动检测并设置JsonPath默认配置<br>
     * 根据系统中可用的JSON解析库,优先级为:fastjson > jackson > gson > jettison > org.json > TapestryJson<br>
     * 该方法只会执行一次,后续调用将不再执行该方法
    */
    public static synchronized void autoSetDefaults() {
        if (autoSet) {
            return ;
        }
        autoSet = true;
        Configuration.Defaults defaults = null;
        
        // 检测fastjson是否可用
        if (isFastjsonAvailable()) {
            // 使用createDefaults方法创建fastjson的默认配置
            defaults = createDefaults(new FastjsonJsonProvider(), 
                                      new FastjsonMappingProvider());
        } else if (isJacksonAvailable()) {
            // 使用jackson作为默认JSON解析库
            defaults = createDefaults("com.jayway.jsonpath.spi.json.JacksonJsonProvider", 
                                      "com.jayway.jsonpath.spi.mapper.JacksonMappingProvider");
        } else if (isGsonAvailable()) {
            // 使用gson作为默认JSON解析库
            defaults = createDefaults("com.jayway.jsonpath.spi.json.GsonJsonProvider", 
                                      "com.jayway.jsonpath.spi.mapper.GsonMappingProvider");
        } else if (isJettisonAvailable()) {
            // 使用jettison作为默认JSON解析库,mappingProviderClassName设为null,将使用默认的JsonSmartMappingProvider
            defaults = createDefaults("com.jayway.jsonpath.spi.json.JettisonProvider", null);
        } else if (isOrgJsonAvailable()) {
            // 使用org.json作为默认JSON解析库
            defaults = createDefaults("com.jayway.jsonpath.spi.json.JsonOrgJsonProvider", 
                                      "com.jayway.jsonpath.spi.mapper.JsonOrgMappingProvider");
        } else if (isTapestryJsonAvailable()) {
            // 使用TapestryJson作为默认JSON解析库
            defaults = createDefaults("com.jayway.jsonpath.spi.json.TapestryJsonProvider", 
                                      "com.jayway.jsonpath.spi.mapper.TapestryMappingProvider");
        }
        
        if (defaults != null) {
            Configuration.setDefaults(defaults);
        }
    }

    /**
     * 获取默认配置,与{@link Configuration#defaultConfiguration()}类似
     * 但会使用XJsonPathConfiguration自动检测到的JSON解析库
     * @return 默认配置实例
     */
    public static Configuration defaultConfiguration() {
        // 静态初始化块已经自动设置了默认配置,直接返回Configuration.defaultConfiguration()即可
        return Configuration.defaultConfiguration();
    }

    /**
     * 根据类名创建JSON提供者和映射提供者的默认配置
     * @param jsonProviderClassName JSON提供者类名
     * @param mappingProviderClassName 映射提供者类名,可为null,为空则使用JsonSmartMappingProvider
     * @return Configuration.Defaults实例,如果创建失败返回null
     */
    private static Configuration.Defaults createDefaults(String jsonProviderClassName, String mappingProviderClassName) {
        try {
            Class<?> jsonProviderClass = Class.forName(jsonProviderClassName);
            final JsonProvider jsonProvider = (JsonProvider) jsonProviderClass.newInstance();
            final MappingProvider mappingProvider;
            if (mappingProviderClassName != null) {
                // 如果提供了映射提供者类名,通过反射实例化
                Class<?> mappingProviderClass = Class.forName(mappingProviderClassName);
                mappingProvider = (MappingProvider) mappingProviderClass.newInstance();
            } else {
                // 如果没有提供映射提供者类名,使用默认的JsonSmartMappingProvider
                mappingProvider = new JsonSmartMappingProvider();
            }
            return createDefaults(jsonProvider, mappingProvider);
        } catch (Exception e) {
            // 如果实例化失败,返回null,继续检测下一个JSON解析库
            return null;
        }
    }
    /**
     * 根据JSON提供者和映射提供者创建默认配置
     * @param jsonProvider JSON提供者实例
     * @param mappingProvider 映射提供者实例,可为null,为空则使用JsonSmartMappingProvider
     * @return Configuration.Defaults实例,如果创建失败返回null
     */
    private static Configuration.Defaults createDefaults(final JsonProvider jsonProvider, MappingProvider mappingProvider) {
    	final MappingProvider _mappingProvider = mappingProvider == null ? new JsonSmartMappingProvider() :mappingProvider;
    	return new Configuration.Defaults() {
    		@Override
    		public JsonProvider jsonProvider() {
    			return jsonProvider;
    		}
    		
    		@Override
    		public MappingProvider mappingProvider() {
    			return _mappingProvider;
    		}
    		
    		@Override
    		public Set<Option> options() {
    			return Collections.emptySet();
    		}
    	};
    }

    /**
     * 检测多个类是否都可用
     * @param classNames 类名字符串数组
     * @return true if all classes are available, otherwise false
     */
    private static boolean isClassesAvailable(String... classNames) {
        try {
            for (String className : classNames) {
                Class.forName(className);
            }
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }

    /**
     * 检测fastjson是否可用
     * @return true if fastjson is available
     */
    private static boolean isFastjsonAvailable() {
        return isClassesAvailable(
                "com.alibaba.fastjson.JSON",
                "com.alibaba.fastjson.JSONObject"
        );
    }

    /**
     * 检测jackson是否可用
     * @return true if jackson is available
     */
    private static boolean isJacksonAvailable() {
        return isClassesAvailable(
                "com.fasterxml.jackson.databind.ObjectMapper",
                "com.jayway.jsonpath.spi.json.JacksonJsonProvider",
                "com.jayway.jsonpath.spi.mapper.JacksonMappingProvider"
        );
    }

    /**
     * 检测gson是否可用
     * @return true if gson is available
     */
    private static boolean isGsonAvailable() {
        return isClassesAvailable(
                "com.google.gson.Gson",
                "com.jayway.jsonpath.spi.json.GsonJsonProvider",
                "com.jayway.jsonpath.spi.mapper.GsonMappingProvider"
        );
    }
    
    /**
     * 检测jettison是否可用
     * @return true if jettison is available
     */
    private static boolean isJettisonAvailable() {
        return isClassesAvailable(
                "org.codehaus.jettison.json.JSONObject",
                "com.jayway.jsonpath.spi.json.JettisonProvider"
        );
    }
    
    /**
     * 检测org.json是否可用
     * @return true if org.json is available
     */
    private static boolean isOrgJsonAvailable() {
        return isClassesAvailable(
                "org.json.JSONObject",
                "com.jayway.jsonpath.spi.json.JsonOrgJsonProvider"
        );
    }
    
    /**
     * 检测TapestryJson是否可用
     * @return true if TapestryJson is available
     */
    private static boolean isTapestryJsonAvailable() {
        return isClassesAvailable(
                "org.apache.tapestry5.json.JSONObject",
                "com.jayway.jsonpath.spi.json.TapestryJsonProvider"
        );
    }
}

2.4 自动检测机制

XJsonPathConfiguration实现了自动检测机制,可以根据系统中可用的JSON解析库自动选择最合适的解析器,优先级为:

fastjson > jackson > gson > jettison > org.json > TapestryJson

3. json-path与fastjson内置JsonPath的区别

特性json-pathfastjson内置JsonPath
设计目标通用JSON查询库,支持多种解析器仅fastjson内部使用
语法类XPath语法,更丰富类XPath语法,相对简单
扩展性支持多种JSON解析器仅支持fastjson
功能更全面,支持复杂查询、映射等基础查询功能
性能取决于底层解析器与fastjson深度集成,性能可能更高
使用场景跨平台、需要灵活切换解析器仅使用fastjson的项目

4. json-path支持fastjson的使用场景

4.1 现有项目已使用json-path,但希望使用fastjson提高性能

如果项目中已经大量使用了json-path,但对性能有更高要求,可以通过我们的实现无缝切换到底层使用fastjson,无需修改现有代码。

4.2 需要在不同环境下灵活切换JSON解析器

通过XJsonPathConfiguration的自动检测机制,可以在不同环境下自动选择可用的最佳解析器,提高项目的适应性和兼容性。

4.3 同时需要json-path的强大功能和fastjson的高性能

json-path提供了丰富的查询功能,而fastjson提供了高性能,结合两者可以在复杂JSON查询场景下获得更好的性能表现。

4.4 项目中已有fastjson依赖,不希望引入额外的JSON解析库

如果项目中已经使用了fastjson,可以通过我们的实现让json-path复用fastjson,避免引入额外的依赖,减小项目体积。

5. 使用示例

5.1 基本使用

import net.facelib.cell.json.XJsonPathConfiguration;
// 自动初始化,无需手动配置
XJsonPathConfiguration.autoSetDefaults();
// 直接使用json-path API
String json = "{\"name\":\"test\",\"age\":20}";
String name = JsonPath.read(json, "$.name");
System.out.println(name); // 输出: test

5.2 对象映射

import net.facelib.cell.json.XJsonPathConfiguration;
// 自动初始化,无需手动配置
XJsonPathConfiguration.autoSetDefaults();
// 定义User类
class User {
    private String name;
    private int age;
    // getter/setter
}

// 使用默认配置进行映射
String json = "{\"name\":\"test\",\"age\":20}";
User user = JsonPath.parse(json).read("$", User.class);
System.out.println(user.getName()); // 输出: test

5.3 泛型映射

import net.facelib.cell.json.XJsonPathConfiguration;
// 自动初始化,无需手动配置
XJsonPathConfiguration.autoSetDefaults();
// List泛型映射
String listJson = "[{\"name\":\"user1\",\"age\":20},{\"name\":\"user2\",\"age\":30}]";
List<User> userList = JsonPath.parse(listJson).read("$", new TypeRef<List<User>>() {});
System.out.println(userList.size()); // 输出: 2

// Map泛型映射
String mapJson = "{\"user1\":{\"name\":\"user1\",\"age\":20},\"user2\":{\"name\":\"user2\",\"age\":30}}";
Map<String, User> userMap = JsonPath.parse(mapJson).read("$", new TypeRef<Map<String, User>>() {});
System.out.println(userMap.size()); // 输出: 2

6. 总结

通过实现FastjsonJsonProviderFastjsonMappingProvider,我们成功地让json-path支持了fastjson作为底层JSON解析库。这种实现方式具有以下优势:

  1. 高性能:利用fastjson的高性能特性,提高json-path的解析速度
  2. 灵活性:可以根据系统环境自动选择最佳解析器
  3. 兼容性:无需修改现有json-path代码,无缝集成
  4. 易用性:自动初始化,无需手动配置
  5. 扩展性:可以方便地支持更多JSON解析库

json-path支持fastjson为开发者提供了更多选择,可以根据项目需求灵活选择合适的JSON处理方案,在保持json-path强大功能的同时,享受fastjson的高性能优势。

7. 参考资料

  1. json-path官方文档
  2. fastjson官方文档
  3. json-path API文档
<component name="ArtifactManager"> <artifact type="exploded-war" name="ssms1924:war exploded"> <output-path>$PROJECT_DIR$/target/ssms1924</output-path> <properties id="maven-jee-properties"> <options> <exploded>true</exploded> <module>ssms1924</module> <packaging>war</packaging> </options> </properties> <root id="root"> <element id="directory" name="WEB-INF"> <element id="directory" name="classes"> <element id="module-output" name="ssms1924" /> </element> <element id="directory" name="lib"> <element id="library" level="project" name="Maven: org.springframework:spring-core:5.0.0.RELEASE" /> <element id="library" level="project" name="Maven: org.springframework:spring-jcl:5.0.0.RELEASE" /> <element id="library" level="project" name="Maven: org.springframework:spring-context:5.0.0.RELEASE" /> <element id="library" level="project" name="Maven: org.springframework:spring-expression:5.0.0.RELEASE" /> <element id="library" level="project" name="Maven: org.springframework:spring-context-support:5.0.0.RELEASE" /> <element id="library" level="project" name="Maven: org.springframework:spring-tx:5.0.0.RELEASE" /> <element id="library" level="project" name="Maven: org.springframework:spring-aop:5.0.0.RELEASE" /> <element id="library" level="project" name="Maven: org.springframework:spring-beans:5.0.0.RELEASE" /> <element id="library" level="project" name="Maven: org.springframework:spring-jdbc:5.0.0.RELEASE" /> <element id="library" level="project" name="Maven: org.springframework:spring-web:5.0.0.RELEASE" /> <element id="library" level="project" name="Maven: org.springframework:spring-webmvc:5.0.0.RELEASE" /> <element id="library" level="project" name="Maven: org.aspectj:aspectjweaver:1.8.8" /> <element id="library" level="project" name="Maven: commons-fileupload:commons-fileupload:1.3.1" /> <element id="library" level="project" name="Maven: javax.servlet:jstl:1.2" /> <element id="library" level="project" name="Maven: com.baomidou:mybatis-plus:2.3" /> <element id="library" level="project" name="Maven: com.baomidou:mybatis-plus-support:2.3" /> <element id="library" level="project" name="Maven: com.baomidou:mybatis-plus-core:2.3" /> <element id="library" level="project" name="Maven: com.github.jsqlparser:jsqlparser:1.1" /> <element id="library" level="project" name="Maven: org.mybatis:mybatis-spring:1.3.2" /> <element id="library" level="project" name="Maven: org.mybatis:mybatis:3.4.6" /> <element id="library" level="project" name="Maven: com.baomidou:mybatis-plus-generate:2.3" /> <element id="library" level="project" name="Maven: mysql:mysql-connector-java:8.0.25" /> <element id="library" level="project" name="Maven: com.google.protobuf:protobuf-java:3.11.4" /> <element id="library" level="project" name="Maven: com.microsoft.sqlserver:mssql-jdbc:6.2.0.jre8" /> <element id="library" level="project" name="Maven: com.alibaba:druid:1.1.0" /> <element id="library" level="project" name="Maven: com.alibaba:fastjson:1.2.8" /> <element id="library" level="project" name="Maven: log4j:log4j:1.2.17" /> <element id="library" level="project" name="Maven: org.slf4j:slf4j-api:1.7.19" /> <element id="library" level="project" name="Maven: org.slf4j:slf4j-log4j12:1.7.19" /> <element id="library" level="project" name="Maven: org.springframework:spring-test:5.0.0.RELEASE" /> <element id="library" level="project" name="Maven: org.apache.commons:commons-lang3:3.0" /> <element id="library" level="project" name="Maven: javax.validation:validation-api:2.0.1.Final" /> <element id="library" level="project" name="Maven: commons-io:commons-io:2.5" /> <element id="library" level="project" name="Maven: cn.hutool:hutool-all:4.0.12" /> <element id="library" level="project" name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.29" /> <element id="library" level="project" name="Maven: org.apache.tomcat:tomcat-annotations-api:9.0.29" /> <element id="library" level="project" name="Maven: commons-beanutils:commons-beanutils:1.8.0" /> <element id="library" level="project" name="Maven: commons-logging:commons-logging:1.1.1" /> <element id="library" level="project" name="Maven: com.baidu.aip:java-sdk:4.4.1" /> <element id="library" level="project" name="Maven: org.json:json:20160810" /> <element id="library" level="project" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.10.1" /> <element id="library" level="project" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.10.1" /> <element id="library" level="project" name="Maven: com.fasterxml.jackson.core:jackson-core:2.10.1" /> <element id="library" level="project" name="Maven: org.codehaus.jackson:jackson-mapper-asl:1.9.13" /> <element id="library" level="project" name="Maven: org.codehaus.jackson:jackson-core-asl:1.9.13" /> </element> </element> <element id="directory" name="META-INF"> <element id="file-copy" path="$PROJECT_DIR$/target/ssms1924/META-INF/MANIFEST.MF" /> </element> <element id="javaee-facet-resources" facet="ssms1924/web/Web" /> </root> </artifact> </component>
05-20
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值