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的同時,繼續(xù)享受fastjson的高性能優(yōu)勢。
2. 技術實現原理
2.1 核心接口
json-path通過以下兩個核心接口來擴展JSON解析支持:
- JsonProvider:負責JSON解析和基本的JSON操作
- MappingProvider:負責JSON對象與Java對象之間的映射
2.2 實現架構
我們的實現包括以下幾個核心類:
| 類名 | 作用 | 實現方式 |
|---|---|---|
| FastjsonJsonProvider | 實現JsonProvider接口 | 擴展AbstractJsonProvider |
| FastjsonMappingProvider | 實現MappingProvider接口 | 直接實現接口 |
| XJsonPathConfiguration | 自動檢測和配置默認解析器 | 靜態(tài)初始化+自動檢測 |
2.3 關鍵實現細節(jié)
2.3.1 FastjsonJsonProvider
FastjsonJsonProvider擴展了AbstractJsonProvider,使用JSON.parse和JSON.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);
}
// 統(tǒng)一使用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);
}
// 統(tǒng)一使用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>
* 自動檢測系統(tǒng)中可用的JSON解析庫并設置默認配置<br>
* 優(yōu)先級為:fastjson > jackson > gson > jettison > org.json > TapestryJson
*/
public final class XJsonPathConfiguration {
/**
* 標記是否已經自動設置默認配置<br>
* 防止重復初始化
*/
private static boolean autoSet = false;
/**
* 靜態(tài)初始化塊,在類加載時自動設置默認配置
*/
static {
autoSetDefaults();
}
private XJsonPathConfiguration() {
// 私有構造方法,防止實例化
}
/**
* 自動檢測并設置JsonPath默認配置<br>
* 根據系統(tǒng)中可用的JSON解析庫,優(yōu)先級為:fastjson > jackson > gson > jettison > org.json > TapestryJson<br>
* 該方法只會執(zhí)行一次,后續(xù)調用將不再執(zhí)行該方法
*/
public static synchronized void autoSetDefaults() {
if (autoSet) {
return ;
}
autoSet = true;
Configuration.Defaults defaults = null;
// 檢測fastjson是否可用
if (isFastjsonAvailable()) {
// 使用createDefaults方法創(chuàng)建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() {
// 靜態(tài)初始化塊已經自動設置了默認配置,直接返回Configuration.defaultConfiguration()即可
return Configuration.defaultConfiguration();
}
/**
* 根據類名創(chuàng)建JSON提供者和映射提供者的默認配置
* @param jsonProviderClassName JSON提供者類名
* @param mappingProviderClassName 映射提供者類名,可為null,為空則使用JsonSmartMappingProvider
* @return Configuration.Defaults實例,如果創(chuàng)建失敗返回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,繼續(xù)檢測下一個JSON解析庫
return null;
}
}
/**
* 根據JSON提供者和映射提供者創(chuàng)建默認配置
* @param jsonProvider JSON提供者實例
* @param mappingProvider 映射提供者實例,可為null,為空則使用JsonSmartMappingProvider
* @return Configuration.Defaults實例,如果創(chuàng)建失敗返回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實現了自動檢測機制,可以根據系統(tǒng)中可用的JSON解析庫自動選擇最合適的解析器,優(yōu)先級為:
fastjson > jackson > gson > jettison > org.json > TapestryJson
3. json-path與fastjson內置JsonPath的區(qū)別
| 特性 | json-path | fastjson內置JsonPath |
|---|---|---|
| 設計目標 | 通用JSON查詢庫,支持多種解析器 | 僅fastjson內部使用 |
| 語法 | 類XPath語法,更豐富 | 類XPath語法,相對簡單 |
| 擴展性 | 支持多種JSON解析器 | 僅支持fastjson |
| 功能 | 更全面,支持復雜查詢、映射等 | 基礎查詢功能 |
| 性能 | 取決于底層解析器 | 與fastjson深度集成,性能可能更高 |
| 使用場景 | 跨平臺、需要靈活切換解析器 | 僅使用fastjson的項目 |
4. json-path支持fastjson的使用場景
4.1 現有項目已使用json-path,但希望使用fastjson提高性能
如果項目中已經大量使用了json-path,但對性能有更高要求,可以通過我們的實現無縫切換到底層使用fastjson,無需修改現有代碼。
4.2 需要在不同環(huán)境下靈活切換JSON解析器
通過XJsonPathConfiguration的自動檢測機制,可以在不同環(huán)境下自動選擇可用的最佳解析器,提高項目的適應性和兼容性。
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. 總結
通過實現FastjsonJsonProvider和FastjsonMappingProvider,我們成功地讓json-path支持了fastjson作為底層JSON解析庫。這種實現方式具有以下優(yōu)勢:
- 高性能:利用fastjson的高性能特性,提高json-path的解析速度
- 靈活性:可以根據系統(tǒng)環(huán)境自動選擇最佳解析器
- 兼容性:無需修改現有json-path代碼,無縫集成
- 易用性:自動初始化,無需手動配置
- 擴展性:可以方便地支持更多JSON解析庫
json-path支持fastjson為開發(fā)者提供了更多選擇,可以根據項目需求靈活選擇合適的JSON處理方案,在保持json-path強大功能的同時,享受fastjson的高性能優(yōu)勢。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
參考資料:
相關文章
Java工具之ja-netfilter?2022.1?配置教程
這篇文章主要介紹了Java工具之ja-netfilter?2022.1?配置教程,本防火墻基于javaagent,所以目前只有基于java的程序能夠使用,需要的朋友可以參考下2022-04-04
SpringCloud-Hystrix-Dashboard客戶端服務監(jiān)控的實現方法
這篇文章主要介紹了SpringCloud-Hystrix-Dashboard客戶端服務監(jiān)控的實現方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03
SpringBoot Redis實現接口冪等性校驗方法詳細講解
這篇文章主要介紹了SpringBoot Redis實現接口冪等性校驗方法,近期一個老項目出現了接口冪等性校驗問題,前端加了按鈕置灰,依然被人拉著接口參數一頓輸出,還是重復調用了接口,通過復制粘貼,完成了后端接口冪等性調用校驗2022-11-11
SpringBoot Swagger2 接口規(guī)范示例詳解
Swagger(在谷歌、IBM、微軟等公司的支持下)做了一個公共的文檔風格來填補上述問題,在本文中,我們將會學習怎么使用Swagger的 Swagger2注解去生成REST API文檔,感興趣的朋友一起看看吧2023-12-12

