文章链接 https://blog.csdn.net/wuyuanshun/article/details/128498372
此文章链接: 复杂json解析
为什么要写这个自定义注解,因为需求需要处理一批比较复杂的json(如果只有一个,直接手动写代码解析就好),众所周知批量且类似的工作,最好抽象出来。这也符合编程的风格,不重复造轮子,但是需要造轮子。【前面铺垫比较长,如果需求比较复杂的json可以直接划到最下面,粘贴自定义注解类 和 自定义注解解析工具类】
需要的字段也恰好是对应上的
json
{
"name": "wuyuanshun",
"sex":"男"
}
或
[
{
"name": "wuyuanshun",
"sex":"男"
},
{
"name": "liuyuanshun",
"sex":"男"
}
]
java对象
@Data
public class Bean {
private String name;
private String sex;
}
解析方法
public class JsonUtil {
public static final ObjectMapper mapper = new ObjectMapper();
public static <T> T fromJson(String json, Class<T> clazz) {
requireNonNull(json);
requireNonNull(clazz);
try {
return mapper.readValue(json, clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* json数组转java对象
* @param json
* @param valueType
* @param
* @return
* @author: wuyuanshn
*/
public static <T> List<T> jsonArrayToObjectList(String json, Class<T> valueType) {
try {
//解析
JSONArray redisJsonArray = JSONArray.parseArray(json);
List<T> redisList = new ArrayList<>(redisJsonArray.size());
//封装
for (int i = 0; i < redisJsonArray.size(); i++) {
T item = mapper.readValue(redisJsonArray.getString(i), valueType);
redisList.add(item);
}
//返回
return redisList;
} catch (Exception e) {
return null;
}
}
public static void main(String[] args) {
String json="xxx";
Bean bean = fromJson(json, Bean.class);
System.out.println(bean);
String jsonList="[xxx,xxx]";
List<Bean> beanList = jsonArrayToObjectList(jsonList, Bean.class);
System.out.println(beanList);
}
}
难度加大一些,比如,出现了层级
json
{
"name": "wuyuanshun",
"sex":"男",
"like":{
"title":"羽毛球",
"level":1,
"time":1672402865000
}
}
简单解析的话,我们可以再新建一个Like对象,如:
@Data
public class Like {
private String title;
private Integer level;
private Long time;
}
然后再bean里加上Like对象即可:
java对象
public class Bean {
private String name;
private String sex;
private Like like;
}
不过需求真的会那么简单,我就不需要写这个文章了。像闯关一下,让我们提升难度,
比如我需要的对象是要同一层级的(如下),要存在一起(比如存数据库表)。当然我们也可以建Like对象再通过代码导入到同一层级。不过我们可以让他简单一些(正题开始了):
对应解析java对象
@Date
public class Bean {
@JsonAnalysisProperty("name")
private String name;
@JsonAnalysisProperty("sex")
private String sex;
@JsonAnalysisProperty("like > title")
private String likeTitle;
@JsonAnalysisProperty("like > level")
private Integer likeLevel;
@JsonAnalysisProperty("like > time")
private Long likeTime;
}
对应解析的bean【自定义注解@JsonAnalysisProperty在文章最下面 目录六】
*测试方法【之后每个测试用测方法】
public static void main(String[] args) {
//json
String json = "{xxxxxxxxxxx}";
//自定义对象
Bean bean = new Bean();
JsonAnalysisPropertyConfig.setObjectByJsonAnalysis(bean,json);
System.out.println(bean);
//此文章链接: [复杂json解析] https://blog.csdn.net/wuyuanshun/article/details/128498372
}
}
需求取出姓名、性别、爱好名称(like > title)、语文分数。
{
"name":"wuyuanshun",
"sex":"男",
"like":{
"title":"羽毛球",
"level":1,
"time":1672402865000
},
"examination_results":[
{
"subject":"数学",
"date":"2023-01-29",
"score":98.5
},
{
"subject":"语文",
"date":"2023-01-29",
"score":98.5
},
{
"subject":"英语",
"date":"2023-01-29",
"score":98.5
}
]
}
对应解析java对象
@Date
public class Bean {
@JsonAnalysisProperty("name")
private String name;
@JsonAnalysisProperty("sex")
private String sex;
@JsonAnalysisProperty("like > title")
private String likeTitle;
@JsonAnalysisProperty("like > level")
private Integer likeLevel;
@JsonAnalysisProperty("examination_results >> \"subject\":\"语文\" > score")
private Double chineseScore;
}
>> 代表之后是数组中的内容,直到"key": "value"这种选择器结束。如果数组到选择器key:value中还有层级,原来的层级( > )需要换成 (>>),如四星难度json。【注意 >> 前后有空格】
需求是取index_display 为 "有互动人数"的数组中的value数值。
{
"purchase_crowd":{
"interact_data":[
{
"index_value":{
"index_display":"有互动人数",
"value":{
"value":44,
"unit":"number"
},
},
"show_list":[
{
"display":"首购人数占比",
"value":{
"value":0.5909090909090909
}
}
]
},
{
"index_value":{
"index_display":"无互动人数"
},
"show_list":[
{
"display":"xxx占比",
"value":{
"value":0.02
}
},
{
"display":"首购人数占比",
"value":{
"value":0.4444444444444444
}
}
]
}
]
}
}
对应解析java对象
@Date
public class Bean {
@JsonAnalysisProperty(defaultValue = "0",value = "purchase_crowd > interact_data >> index_value >> \"index_display\":\"有互动人数\" > value > value") @ApiModelProperty("有互动人数") private String purchaseCrowdInteractDataPeopleNumberInteracting;
}
json同上
如 需求是取index_display 为 "有互动人数"的数组中的 ‘收购人数占比’ value数值、
和取index_display 为 "无互动人数"的数组中的‘收购人数占比’value数值
对应解析java对象
@Date
public class Bean {
@JsonAnalysisProperty(defaultValue = "0",value = "purchase_crowd > interact_data >> * index_value >> \"index_display\":\"有互动人数\" > show_list >> \"display\":\"首购人数占比\" > value > value") @ApiModelProperty("有互动人数-首购人数占比") private String purchaseCrowdInteractDataPeopleNumberFirstPurchaseRatio;
@JsonAnalysisProperty(defaultValue = "0",value = "purchase_crowd > interact_data >> * index_value >> \"index_display\":\"无互动人数\" > show_list >> \"display\":\"首购人数占比\" > value > value") @ApiModelProperty("无互动人数-首购人数占比") private String purchaseCrowdNotInteractDataPeopleNumberFirstPurchaseRatio;
}
自定义注解的逻辑就是像指针一样根据注解中的路由去寻找字段
符号 “ * ” 代表记录指针位置层级,等找到对应的key:value时,返回之前保存的层级。【注意 * 前后有空格】
public static void main(String[] args) {
//json
String json = "{xxxxxxxxxxx}";
//自定义对象
Bean bean = new Bean();
JsonAnalysisPropertyConfig.setObjectByJsonAnalysis(bean,json);
System.out.println(bean);
//此文章链接 https://blog.csdn.net/wuyuanshun/article/details/128498372
}
}
package com.wuyuanshun.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @program: wys-service
* @description: 自定义json解析注解
* @author: wuyuanshn
* @create: 2022-12-26 17:12
**/
@Target({java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JsonAnalysisProperty {
/**
* 正常层级选择
*/
String SPLIT_DEFAULT = " > ";
/**
* 下层级是数组内元素
*/
String SPLIT_DEFAULT_ARRAY = " >> ";
/**
* 数组内 选择返回的层级 默认是最后(最深层)的节点
*/
String SELECT_ARRAY_DEFAULT_ARRAY = "* ";
/**
* 核心字段
*
* @return
*/
String value();
/**
* 类型 默认0
* 1 多层级【后续可以改为枚举类型】
*
* @return
*/
int type() default 0;
/**
* 分隔符
*
* @return
*/
String split() default SPLIT_DEFAULT;
/**
* 数组内 选择返回的层级 默认是最后(最深层)的节点
*
* @return
*/
String selectArrayOne() default SELECT_ARRAY_DEFAULT_ARRAY;
/**
* 标记为数组
*
* @return
*/
String splitArray() default SPLIT_DEFAULT_ARRAY;
/**
* 是否忽略
*
* @return
*/
boolean ignore() default false;
/**
* 默认值
*
* @return
*/
String defaultValue() default "";
}
以下代码是此篇文章的核心重点,添加了解释代码的注释便于大家理解和修改升级。
package com.wuyuanshun.annotation;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @program: wys-service
* @description: 自定义json解析注解 工具类
* 此文章链接: [复杂json解析] https://blog.csdn.net/wuyuanshun/article/details/128498372
* @author: wuyuanshn
* @create: 2022-12-26 18:38
**/
@Slf4j
public class JsonAnalysisPropertyConfig {
public static final ObjectMapper mapper = new ObjectMapper();
static Pattern GROUP_INDEX_PATTERN = Pattern.compile("\"([\\u4E00-\\u9FA5A-Za-z0-9_]+)\"[ ]*:[ ]*\"([\\u4E00-\\u9FA5A-Za-z0-9_%&',,+!@#^*《》【】\\-()。;=?$\\x22]+)\"");
/**
* 根据注解解析 对象中的所有
*
* @param t
* @param json
* @param
*/
public static <T> void setObjectByJsonAnalysis(T t, String json) {
List<Field> fieldList = getFieldList(t.getClass());
for (Field field : fieldList) {
setAnalysisProperty(field, t, json);
}
}
/**
* 解析对象
*
* @return
*/
public static List<Field> getFieldList(Class<?> clazz) {
List<Field> fieldList = new ArrayList<>();
while (clazz != null) {
fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
clazz = clazz.getSuperclass();
}
Field[] fields = new Field[fieldList.size()];
fieldList.toArray(fields);
return fieldList;
}
/**
* 根据注解解析 对象中的单个字段
*
* @param field
* @param t
* @param json
* @param
*/
public static <T> void setAnalysisProperty(Field field, T t, String json) {
try {
JsonAnalysisProperty annotation = field.getAnnotation(JsonAnalysisProperty.class);
if (annotation == null || StringUtils.isEmpty(json)) {
return;
}
//一、 验证参数
//忽略
boolean ignore = annotation.ignore();
String split = annotation.split();
String splitArray = annotation.splitArray();
String selectArrayOne = annotation.selectArrayOne();
String defaultValue = annotation.defaultValue();
if (ignore) {
return;
}
field.setAccessible(true);
//字段内容
Object fieldValue = field.get(t);
//有默认值 先这设置默认值 防止后续报错 设置不上(字段没有内容的情况下 再设置默认值)
if (!StringUtils.isEmpty(defaultValue) && StringUtils.isEmpty(fieldValue)) {
setField(field, t, defaultValue);
}
//默认-核心字段
String codeValue = annotation.value();
if (StringUtils.isEmpty(codeValue)) {
return;
}
//二、 处理数据
JsonNode jsonNode = mapper.readTree(json);
String[] codeValueList = codeValue.split(split);
for (int i = 0; i < codeValueList.length; i++) {
String codeValueItem = codeValueList[i];
if (StringUtils.isEmpty(codeValueItem)) {
continue;
}
//剔除多余空格
codeValueItem = codeValueItem.trim();
//判断是否是数组 是则处理数组 选择节点
JsonNode jsonNodeItem = analysisSplitArrayMax(jsonNode, codeValueItem, splitArray, selectArrayOne);
//是选择数组 跳过
if (jsonNodeItem != null) {
jsonNode = jsonNodeItem;
continue;
}
jsonNode = jsonNode.get(codeValueItem);
}
String text = jsonNode.asText();
//设置值
// field.set(t, text);
setField(field, t, text);
//如果为空 且有默认值 设置默认值
if (StringUtils.isEmpty(text) && !StringUtils.isEmpty(defaultValue) && StringUtils.isEmpty(fieldValue)) {
setField(field, t, defaultValue);
}
} catch (Exception e) {
String error = null;
try {
StackTraceElement[] stackTrace = e.getStackTrace();
StackTraceElement stackTraceElement = stackTrace[0];
error = e + "\r\n " + stackTraceElement;
} catch (Exception exception) {
log.error("JsonAnalysisPropertyConfig setAnalysisProperty exception error {}", e, exception);
}
log.error("JsonAnalysisPropertyConfig setAnalysisProperty field {} error {}", field, error);
}
}
/**
* 设置字段值 不同类型
*
* @param field
* @param t
* @param value
* @param
*/
public static <T> void setField(Field field, T t, String value) {
try {
Object obj = value;
Class<?> type = field.getType();
if (type.equals(String.class)) {
// field.set(t, obj);
} else if (type.equals(Long.class)) {
obj = Long.parseLong(value);
} else if (type.equals(Integer.class)) {
obj = Integer.parseInt(value);
} else if (type.equals(Boolean.class)) {
obj = Boolean.parseBoolean(value);
} else if (type.equals(BigDecimal.class)) {
obj = new BigDecimal(value);
} else if (type.equals(Double.class)) {
obj = Double.parseDouble(value);
} else if (type.equals(Float.class)) {
obj = Float.parseFloat(value);
}
// else if (type.equals(Date.class)) {
// obj = DateUtils.getDate(value);
// }
//其他类型可以在这里添加
field.set(t, obj);
} catch (Exception e) {
log.error("JsonAnalysisPropertyConfig setField 赋值字段失败 field {}; t {}; value {}", field, t, value, e);
}
}
/**
* 判断是否是数组,如果是 接着处理
*
* @param jsonNode
* @param codeValueItem
* @param splitArray
*/
public static JsonNode analysisSplitArray1(JsonNode jsonNode, String codeValueItem, String splitArray) {
//判断是否是数组
if (codeValueItem.startsWith(splitArray)) {
String key;
String value;
//查看是否需要选择json数组中的某一个
Matcher matcher = GROUP_INDEX_PATTERN.matcher(codeValueItem);
if (matcher.find()) {
key = matcher.group(1);
value = matcher.group(2);
} else {
return null;
}
Iterator<JsonNode> elements = jsonNode.elements();
//遍历找出对应的数组item
while (elements.hasNext()) {
JsonNode next = elements.next();
String getValue = next.get(key).asText();
if (!StringUtils.isEmpty(getValue) && getValue.equals(value)) {
return next;
}
}
}
return null;
}
/**
* 判断是否是数组,如果是 接着处理
*
* @param jsonNode
* @param codeValueItem
* @param splitArray
*/
public static JsonNode analysisSplitArray2(JsonNode jsonNode, String codeValueItem, String splitArray) {
//判断是否是数组
if (codeValueItem.contains(splitArray)) {
String[] keyList = null;
keyList = codeValueItem.split(splitArray);
//一、平级选择
String codeNext = keyList[0];
jsonNode = jsonNode.get(codeNext);
//二、数组选择
Iterator<JsonNode> elements = jsonNode.elements();
String key;
String value;
//查看是否需要选择json数组中的某一个
Matcher matcher = GROUP_INDEX_PATTERN.matcher(codeValueItem);
if (matcher.find()) {
key = matcher.group(1);
value = matcher.group(2);
} else {
//匹配不到筛选key value 但是包含数组 按第一哥个返回(认为数组中只有一个JsonNode 或者取第一个【顺序保证的前提下】)
if (elements.hasNext()) {
return elements.next();
}
//取不到数据 认为不是数组
return null;
}
//遍历找出对应的数组item
while (elements.hasNext()) {
JsonNode next = elements.next();
//判断是否需要深层选择
if (keyList.length > 2) {
for (int i = 0; i < keyList.length; i++) {
//跳过最后一个 认为最后一个是key value
//跳过第一个 第一个事平级选择
if (i == 0 || i == keyList.length - 1) {
continue;
}
next = next.get(keyList[i]);
}
}
String getValue = next.get(key).asText();
if (!StringUtils.isEmpty(getValue) && getValue.equals(value)) {
return next;
}
}
}
return null;
}
/**
* 判断是否是数组,如果是 接着处理
*
* @param jsonNode
* @param codeValueItem
* @param splitArray
* @param selectArrayOne
*/
public static JsonNode analysisSplitArrayMax(JsonNode jsonNode, String codeValueItem, String splitArray, String selectArrayOne) {
//判断是否是数组
if (codeValueItem.contains(splitArray)) {
String[] keyList = null;
keyList = codeValueItem.split(splitArray);
//一、层级选择
String codeNext = keyList[0];
jsonNode = jsonNode.get(codeNext);
//二、数组选择
Iterator<JsonNode> elements = jsonNode.elements();
String key;
String value;
//查看是否需要选择json数组中的某一个
Matcher matcher = GROUP_INDEX_PATTERN.matcher(codeValueItem);
if (matcher.find()) {
key = matcher.group(1);
value = matcher.group(2);
} else {
//匹配不到筛选key value 但是包含数组 按第一个返回(认为数组中只有一个JsonNode 或者取第一个【顺序保证的前提下】)
if (elements.hasNext()) {
return elements.next();
}
//取不到数据 认为不是数组
return null;
}
//遍历找出对应的数组item
while (elements.hasNext()) {
JsonNode next = elements.next();
JsonNode returnNext = null;
//判断是否需要深层选择
if (keyList.length > 2) {
for (int i = 0; i < keyList.length; i++) {
//跳过最后一个 认为最后一个是key value
//跳过第一个 第一个事平级选择
if (i == 0 || i == keyList.length - 1) {
continue;
}
String keyItem = keyList[i];
if (keyItem.startsWith(selectArrayOne)) {
keyItem = keyItem.replace(selectArrayOne, "");
returnNext = next;
}
next = next.get(keyItem);
}
}
String getValue = next.get(key).asText();
if (!StringUtils.isEmpty(getValue) && getValue.equals(value)) {
//是否选择返回层级
if (returnNext != null) {
return returnNext;
}
return next;
}
}
}
return null;
}
public static void main(String[] args) {
String a = "aasddd@@\"key_\" :\"value值(asdd,。a)\"";
Matcher matcher = GROUP_INDEX_PATTERN.matcher(a);
if (matcher.find()) {
String key = matcher.group(1);
String value = matcher.group(2);
System.out.println("key = " + key);
System.out.println("value = " + value);
/* key = key_
value = value值(asdd,。a)*/
}
String json = "{\n" +
"\"name\": \"wuyuanshun\",\n" +
"\"sex\":\"男\",\n" +
"\"like\":{\n" +
"\t\"title\":\"羽毛球\",\n" +
"\t\"level\":1,\n" +
"\t\"time\":1672402865000\n" +
"},\n" +
" \"examination_results\":[\n" +
" {\n" +
"\t\"subject\":\"语文\",\n" +
"\t\"date\":\"2023-01-29\",\n" +
"\t\"score\":98.5\n" +
" }, {\n" +
"\t\"subject\":\"数学\",\n" +
"\t\"date\":\"2023-01-29\",\n" +
"\t\"score\":98.5\n" +
" }, {\n" +
"\t\"subject\":\"英语\",\n" +
"\t\"date\":\"2023-01-29\",\n" +
"\t\"score\":98.5\n" +
" }\n" +
"]\n" +
"}";
Bean bean = new Bean();
setObjectByJsonAnalysis(bean,json);
System.out.println(bean);
}
}
@JsonAnalysisProperty(defaultValue = "0",value = "purchase_crowd > interact_data >> * index_value >> \"index_display\":\"有互动人数\" > show_list >> \"display\":\"首购人数占比\" > value > value")
@ApiModelProperty("成交人群分析-有互动人数-首购人数占比")
private String purchaseCrowdInteractDataPeopleNumberFirstPurchaseRatio;
//替换为
@JsonAnalysisProperty(split = " 》 ", splitArray = " 》》 ", selectArrayOne= "** ", defaultValue = "0",value = "purchase_crowd 》 interact_data 》》 ** index_value 》》 \"index_display\":\"有互动人数\" > show_list 》》 \"display\":\"首购人数占比\" 》 value 》 value")
@ApiModelProperty("成交人群分析-有互动人数-首购人数占比")
private String purchaseCrowdInteractDataPeopleNumberFirstPurchaseRatio;
{
"purchase_crowd":{
"interact_data":[
{
"index_value":{
"index_display":"有互动人数",
"index_name":"",
"value":{
"value":44,
"unit":"number"
},
"change_value":{
"value":0.4943820224719101,
"unit":"ratio"
}
},
"show_list":[
{
"display":"粉丝占比",
"name":"",
"value":{
"value":0.8636363636363636,
"unit":"ratio"
}
},
{
"display":"首购人数占比",
"name":"",
"value":{
"value":0.5909090909090909,
"unit":"ratio"
}
}
]
},
{
"index_value":{
"index_display":"无互动人数",
"index_name":"",
"value":{
"value":45,
"unit":"number"
},
"change_value":{
"value":0.5056179775280899,
"unit":"ratio"
},
},
"show_list":[
{
"display":"粉丝占比",
"name":"",
"value":{
"value":0.6888888888888889,
"unit":"ratio"
}
},
{
"display":"首购人数占比",
"name":"",
"value":{
"value":0.4444444444444444,
"unit":"ratio"
}
}
]
}
]
}
}
对应解析java对象
@JsonAnalysisProperty(defaultValue = "0", value = "purchase_crowd > interact_data >> index_value >> \"index_display\":\"有互动人数\" > value > value")
@ApiModelProperty("成交人群分析-有互动人数")
private String purchaseCrowdInteractDataPeopleNumberInteracting;
@JsonAnalysisProperty(defaultValue = "0", value = "purchase_crowd > interact_data >> index_value >> \"index_display\":\"有互动人数\" > change_value > value")
@ApiModelProperty("成交人群分析-有互动人数占比")
private String purchaseCrowdInteractDataPeopleNumberRatio;
@JsonAnalysisProperty(defaultValue = "0", value = "purchase_crowd > interact_data >> * index_value >> \"index_display\":\"有互动人数\" > show_list >> \"display\":\"粉丝占比\" > value > value")
@ApiModelProperty("成交人群分析-有互动人数-粉丝占比")
private String purchaseCrowdInteractDataPeopleNumberFansDataRatio;
@JsonAnalysisProperty(defaultValue = "0", value = "purchase_crowd > interact_data >> * index_value >> \"index_display\":\"有互动人数\" > show_list >> \"display\":\"首购人数占比\" > value > value")
@ApiModelProperty("成交人群分析-有互动人数-首购人数占比")
private String purchaseCrowdInteractDataPeopleNumberFirstPurchaseRatio;
@JsonAnalysisProperty(defaultValue = "0", value = "purchase_crowd > interact_data >> index_value >> \"index_display\":\"无互动人数\" > value > value")
@ApiModelProperty("成交人群分析-无互动人数")
private String purchaseCrowdNotInteractDataPeopleNumber;
@JsonAnalysisProperty(defaultValue = "0", value = "purchase_crowd > interact_data >> index_value >> \"index_display\":\"无互动人数\" > change_value > value")
@ApiModelProperty("成交人群分析-无互动人数占比")
private String purchaseCrowdNotInteractDataPeopleNumberRatio;
@JsonAnalysisProperty(defaultValue = "0", value = "purchase_crowd > interact_data >> * index_value >> \"index_display\":\"无互动人数\" > show_list >> \"display\":\"粉丝占比\" > value > value")
@ApiModelProperty("成交人群分析-无互动人数-粉丝占比")
private String purchaseCrowdNotInteractDataPeopleNumberFansDataRatio;
@JsonAnalysisProperty(defaultValue = "0", value = "purchase_crowd > interact_data >> * index_value >> \"index_display\":\"无互动人数\" > show_list >> \"display\":\"首购人数占比\" > value > value")
@ApiModelProperty("成交人群分析-无互动人数-首购人数占比")
private String purchaseCrowdNotInteractDataPeopleNumberFirstPurchaseRatio;
转发请发送私信告知,并标注本文章链接(https://blog.csdn.net/wuyuanshun/article/details/128498372)感谢。
链接: java 自定义json解析注解 复杂json解析 工具类