修改关键字定位方式
parent
7e6fa007e0
commit
17ba40eee8
|
@ -0,0 +1,214 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.bean.copier.CopyOptions;
|
||||
import aiyh.utils.tool.cn.hutool.core.convert.Convert;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Filter;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.mutable.MutablePair;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.CaseInsensitiveLinkedMap;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.CaseInsensitiveTreeMap;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 内部JSON工具类,仅用于JSON内部使用
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public final class InternalJSONUtil {
|
||||
|
||||
private InternalJSONUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果对象是Number 且是 NaN or infinite,将抛出异常
|
||||
*
|
||||
* @param obj 被检查的对象
|
||||
* @return 检测后的值
|
||||
* @throws JSONException If o is a non-finite number.
|
||||
*/
|
||||
static Object testValidity(Object obj) throws JSONException {
|
||||
if (!ObjectUtil.isValidIfNumber(obj)) {
|
||||
throw new JSONException("JSON does not allow non-finite numbers.");
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 值转为String,用于JSON中。规则为:
|
||||
* <ul>
|
||||
* <li>对象如果实现了{@link JSONString}接口,调用{@link JSONString#toJSONString()}方法</li>
|
||||
* <li>对象如果实现了{@link JSONString}接口,调用{@link JSONString#toJSONString()}方法</li>
|
||||
* <li>对象如果是数组或Collection,包装为{@link JSONArray}</li>
|
||||
* <li>对象如果是Map,包装为{@link JSONObject}</li>
|
||||
* <li>对象如果是数字,使用{@link NumberUtil#toStr(Number)}转换为字符串</li>
|
||||
* <li>其他情况调用toString并使用双引号包装</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param value 需要转为字符串的对象
|
||||
* @return 字符串
|
||||
* @throws JSONException If the value is or contains an invalid number.
|
||||
*/
|
||||
static String valueToString(Object value) throws JSONException {
|
||||
if (value == null || value instanceof JSONNull) {
|
||||
return JSONNull.NULL.toString();
|
||||
}
|
||||
if (value instanceof JSONString) {
|
||||
try {
|
||||
return ((JSONString) value).toJSONString();
|
||||
} catch (Exception e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
} else if (value instanceof Number) {
|
||||
return NumberUtil.toStr((Number) value);
|
||||
} else if (value instanceof Boolean || value instanceof JSONObject || value instanceof JSONArray) {
|
||||
return value.toString();
|
||||
} else if (value instanceof Map) {
|
||||
Map<?, ?> map = (Map<?, ?>) value;
|
||||
return new JSONObject(map).toString();
|
||||
} else if (value instanceof Collection) {
|
||||
Collection<?> coll = (Collection<?>) value;
|
||||
return new JSONArray(coll).toString();
|
||||
} else if (ArrayUtil.isArray(value)) {
|
||||
return new JSONArray(value).toString();
|
||||
} else {
|
||||
return JSONUtil.quote(value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试转换字符串为number, boolean, or null,无法转换返回String
|
||||
*
|
||||
* @param string A String.
|
||||
* @return A simple JSON value.
|
||||
*/
|
||||
public static Object stringToValue(String string) {
|
||||
// null处理
|
||||
if (StrUtil.isEmpty(string) || StrUtil.NULL.equalsIgnoreCase(string)) {
|
||||
return JSONNull.NULL;
|
||||
}
|
||||
|
||||
// boolean处理
|
||||
if ("true".equalsIgnoreCase(string)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
if ("false".equalsIgnoreCase(string)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
// Number处理
|
||||
char b = string.charAt(0);
|
||||
if ((b >= '0' && b <= '9') || b == '-') {
|
||||
try {
|
||||
if (StrUtil.containsAnyIgnoreCase(string, ".", "e")) {
|
||||
// pr#192@Gitee,Double会出现小数精度丢失问题,此处使用BigDecimal
|
||||
return new BigDecimal(string);
|
||||
} else {
|
||||
final long myLong = Long.parseLong(string);
|
||||
if (string.equals(Long.toString(myLong))) {
|
||||
if (myLong == (int) myLong) {
|
||||
return (int) myLong;
|
||||
} else {
|
||||
return myLong;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
// 其它情况返回原String值下
|
||||
return string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Property的键转化为JSON形式<br>
|
||||
* 用于识别类似于:com.luxiaolei.package.hutool这类用点隔开的键<br>
|
||||
* 注意:是否允许重复键,取决于JSONObject配置
|
||||
*
|
||||
* @param jsonObject JSONObject
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return JSONObject
|
||||
*/
|
||||
static JSONObject propertyPut(JSONObject jsonObject, Object key, Object value, Filter<MutablePair<String, Object>> filter) {
|
||||
final String[] path = StrUtil.splitToArray(Convert.toStr(key), CharUtil.DOT);
|
||||
final int last = path.length - 1;
|
||||
JSONObject target = jsonObject;
|
||||
for (int i = 0; i < last; i += 1) {
|
||||
final String segment = path[i];
|
||||
JSONObject nextTarget = target.getJSONObject(segment);
|
||||
if (nextTarget == null) {
|
||||
nextTarget = new JSONObject(target.getConfig());
|
||||
target.set(segment, nextTarget, filter, target.getConfig().isCheckDuplicate());
|
||||
}
|
||||
target = nextTarget;
|
||||
}
|
||||
target.set(path[last], value, filter, target.getConfig().isCheckDuplicate());
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认情况下是否忽略null值的策略选择,以下对象不忽略null值,其它对象忽略:
|
||||
*
|
||||
* <pre>
|
||||
* 1. CharSequence
|
||||
* 2. JSONTokener
|
||||
* 3. Map
|
||||
* </pre>
|
||||
*
|
||||
* @param obj 需要检查的对象
|
||||
* @return 是否忽略null值
|
||||
* @since 4.3.1
|
||||
*/
|
||||
static boolean defaultIgnoreNullValue(Object obj) {
|
||||
return (!(obj instanceof CharSequence))//
|
||||
&& (!(obj instanceof JSONTokener))//
|
||||
&& (!(obj instanceof Map));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将{@link JSONConfig}参数转换为Bean拷贝所用的{@link CopyOptions}
|
||||
*
|
||||
* @param config {@link JSONConfig}
|
||||
* @return {@link CopyOptions}
|
||||
* @since 5.8.0
|
||||
*/
|
||||
static CopyOptions toCopyOptions(JSONConfig config) {
|
||||
return CopyOptions.create()
|
||||
.setIgnoreCase(config.isIgnoreCase())
|
||||
.setIgnoreError(config.isIgnoreError())
|
||||
.setIgnoreNullValue(config.isIgnoreNullValue())
|
||||
.setTransientSupport(config.isTransientSupport());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置创建对应的原始Map
|
||||
*
|
||||
* @param capacity 初始大小
|
||||
* @param config JSON配置项,{@code null}则使用默认配置
|
||||
* @return Map
|
||||
*/
|
||||
static Map<String, Object> createRawMap(int capacity, JSONConfig config) {
|
||||
final Map<String, Object> rawHashMap;
|
||||
if (null == config) {
|
||||
config = JSONConfig.create();
|
||||
}
|
||||
final Comparator<String> keyComparator = config.getKeyComparator();
|
||||
if (config.isIgnoreCase()) {
|
||||
if (null != keyComparator) {
|
||||
rawHashMap = new CaseInsensitiveTreeMap<>(keyComparator);
|
||||
} else {
|
||||
rawHashMap = new CaseInsensitiveLinkedMap<>(capacity);
|
||||
}
|
||||
} else {
|
||||
if (null != keyComparator) {
|
||||
rawHashMap = new TreeMap<>(keyComparator);
|
||||
} else {
|
||||
rawHashMap = new LinkedHashMap<>(capacity);
|
||||
}
|
||||
}
|
||||
return rawHashMap;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.bean.BeanPath;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.TypeReference;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* JSON接口
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public interface JSON extends Cloneable, Serializable {
|
||||
|
||||
/**
|
||||
* 获取JSON配置
|
||||
*
|
||||
* @return {@link JSONConfig}
|
||||
* @since 5.8.6
|
||||
*/
|
||||
JSONConfig getConfig();
|
||||
|
||||
/**
|
||||
* 通过表达式获取JSON中嵌套的对象<br>
|
||||
* <ol>
|
||||
* <li>.表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值</li>
|
||||
* <li>[]表达式,可以获取集合等对象中对应index的值</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* 表达式栗子:
|
||||
*
|
||||
* <pre>
|
||||
* persion
|
||||
* persion.name
|
||||
* persons[3]
|
||||
* person.friends[5].name
|
||||
* </pre>
|
||||
*
|
||||
* @param expression 表达式
|
||||
* @return 对象
|
||||
* @see BeanPath#get(Object)
|
||||
* @since 4.0.6
|
||||
*/
|
||||
Object getByPath(String expression);
|
||||
|
||||
/**
|
||||
* 设置表达式指定位置(或filed对应)的值<br>
|
||||
* 若表达式指向一个JSONArray则设置其坐标对应位置的值,若指向JSONObject则put对应key的值<br>
|
||||
* 注意:如果为JSONArray,设置值下标小于其长度,将替换原有值,否则追加新值<br>
|
||||
* <ol>
|
||||
* <li>.表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值</li>
|
||||
* <li>[]表达式,可以获取集合等对象中对应index的值</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* 表达式栗子:
|
||||
*
|
||||
* <pre>
|
||||
* persion
|
||||
* persion.name
|
||||
* persons[3]
|
||||
* person.friends[5].name
|
||||
* </pre>
|
||||
*
|
||||
* @param expression 表达式
|
||||
* @param value 值
|
||||
*/
|
||||
void putByPath(String expression, Object value);
|
||||
|
||||
/**
|
||||
* 通过表达式获取JSON中嵌套的对象<br>
|
||||
* <ol>
|
||||
* <li>.表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值</li>
|
||||
* <li>[]表达式,可以获取集合等对象中对应index的值</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* 表达式栗子:
|
||||
*
|
||||
* <pre>
|
||||
* persion
|
||||
* persion.name
|
||||
* persons[3]
|
||||
* person.friends[5].name
|
||||
* </pre>
|
||||
* <p>
|
||||
* 获取表达式对应值后转换为对应类型的值
|
||||
*
|
||||
* @param <T> 返回值类型
|
||||
* @param expression 表达式
|
||||
* @param resultType 返回值类型
|
||||
* @return 对象
|
||||
* @see BeanPath#get(Object)
|
||||
* @since 4.0.6
|
||||
*/
|
||||
<T> T getByPath(String expression, Class<T> resultType);
|
||||
|
||||
/**
|
||||
* 格式化打印JSON,缩进为4个空格
|
||||
*
|
||||
* @return 格式化后的JSON字符串
|
||||
* @throws JSONException 包含非法数抛出此异常
|
||||
* @since 3.0.9
|
||||
*/
|
||||
default String toStringPretty() throws JSONException {
|
||||
return this.toJSONString(4);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化输出JSON字符串
|
||||
*
|
||||
* @param indentFactor 每层缩进空格数
|
||||
* @return JSON字符串
|
||||
* @throws JSONException 包含非法数抛出此异常
|
||||
*/
|
||||
default String toJSONString(int indentFactor) throws JSONException {
|
||||
final StringWriter sw = new StringWriter();
|
||||
synchronized (sw.getBuffer()) {
|
||||
return this.write(sw, indentFactor, 0).toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将JSON内容写入Writer,无缩进<br>
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @param writer Writer
|
||||
* @return Writer
|
||||
* @throws JSONException JSON相关异常
|
||||
*/
|
||||
default Writer write(Writer writer) throws JSONException {
|
||||
return this.write(writer, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将JSON内容写入Writer<br>
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @param writer writer
|
||||
* @param indentFactor 缩进因子,定义每一级别增加的缩进量
|
||||
* @param indent 本级别缩进量
|
||||
* @return Writer
|
||||
* @throws JSONException JSON相关异常
|
||||
*/
|
||||
Writer write(Writer writer, int indentFactor, int indent) throws JSONException;
|
||||
|
||||
/**
|
||||
* 转为实体类对象,转换异常将被抛出
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param clazz 实体类
|
||||
* @return 实体类对象
|
||||
*/
|
||||
default <T> T toBean(Class<T> clazz) {
|
||||
return toBean((Type) clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为实体类对象,转换异常将被抛出
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param reference {@link TypeReference}类型参考子类,可以获取其泛型参数中的Type类型
|
||||
* @return 实体类对象
|
||||
* @since 4.2.2
|
||||
*/
|
||||
default <T> T toBean(TypeReference<T> reference) {
|
||||
return toBean(reference.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为实体类对象
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param type {@link Type}
|
||||
* @return 实体类对象
|
||||
* @since 3.0.8
|
||||
*/
|
||||
default <T> T toBean(Type type) {
|
||||
return toBean(type, getConfig().isIgnoreError());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为实体类对象
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param type {@link Type}
|
||||
* @param ignoreError 是否忽略转换错误
|
||||
* @return 实体类对象
|
||||
* @since 4.3.2
|
||||
*/
|
||||
default <T> T toBean(Type type, boolean ignoreError) {
|
||||
return JSONConverter.jsonConvert(type, this, ignoreError);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,591 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.bean.BeanPath;
|
||||
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Filter;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.mutable.Mutable;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.mutable.MutableObj;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.mutable.MutablePair;
|
||||
import aiyh.utils.tool.cn.hutool.core.text.StrJoiner;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
import aiyh.utils.tool.cn.hutool.json.serialize.JSONWriter;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* JSON数组<br>
|
||||
* JSON数组是表示中括号括住的数据表现形式<br>
|
||||
* 对应的JSON字符串格格式例如:
|
||||
*
|
||||
* <pre>
|
||||
* ["a", "b", "c", 12]
|
||||
* </pre>
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
public class JSONArray implements JSON, JSONGetter<Integer>, List<Object>, RandomAccess {
|
||||
private static final long serialVersionUID = 2664900568717612292L;
|
||||
|
||||
/**
|
||||
* 默认初始大小
|
||||
*/
|
||||
public static final int DEFAULT_CAPACITY = 10;
|
||||
|
||||
/**
|
||||
* 持有原始数据的List
|
||||
*/
|
||||
private List<Object> rawList;
|
||||
/**
|
||||
* 配置项
|
||||
*/
|
||||
private final JSONConfig config;
|
||||
|
||||
// region Constructors
|
||||
|
||||
/**
|
||||
* 构造<br>
|
||||
* 默认使用{@link ArrayList} 实现
|
||||
*/
|
||||
public JSONArray() {
|
||||
this(DEFAULT_CAPACITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造<br>
|
||||
* 默认使用{@link ArrayList} 实现
|
||||
*
|
||||
* @param initialCapacity 初始大小
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public JSONArray(int initialCapacity) {
|
||||
this(initialCapacity, JSONConfig.create());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造<br>
|
||||
* 默认使用{@link ArrayList} 实现
|
||||
*
|
||||
* @param config JSON配置项
|
||||
* @since 4.6.5
|
||||
*/
|
||||
public JSONArray(JSONConfig config) {
|
||||
this(DEFAULT_CAPACITY, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造<br>
|
||||
* 默认使用{@link ArrayList} 实现
|
||||
*
|
||||
* @param initialCapacity 初始大小
|
||||
* @param config JSON配置项
|
||||
* @since 4.1.19
|
||||
*/
|
||||
public JSONArray(int initialCapacity, JSONConfig config) {
|
||||
this.rawList = new ArrayList<>(initialCapacity);
|
||||
this.config = ObjectUtil.defaultIfNull(config, JSONConfig::create);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从对象构造,忽略{@code null}的值<br>
|
||||
* 支持以下类型的参数:
|
||||
*
|
||||
* <pre>
|
||||
* 1. 数组
|
||||
* 2. {@link Iterable}对象
|
||||
* 3. JSON数组字符串
|
||||
* </pre>
|
||||
*
|
||||
* @param object 数组或集合或JSON数组字符串
|
||||
* @throws JSONException 非数组或集合
|
||||
*/
|
||||
public JSONArray(Object object) throws JSONException {
|
||||
this(object, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从对象构造<br>
|
||||
* 支持以下类型的参数:
|
||||
*
|
||||
* <pre>
|
||||
* 1. 数组
|
||||
* 2. {@link Iterable}对象
|
||||
* 3. JSON数组字符串
|
||||
* </pre>
|
||||
*
|
||||
* @param object 数组或集合或JSON数组字符串
|
||||
* @param ignoreNullValue 是否忽略空值
|
||||
* @throws JSONException 非数组或集合
|
||||
*/
|
||||
public JSONArray(Object object, boolean ignoreNullValue) throws JSONException {
|
||||
this(object, JSONConfig.create().setIgnoreNullValue(ignoreNullValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从对象构造<br>
|
||||
* 支持以下类型的参数:
|
||||
*
|
||||
* <pre>
|
||||
* 1. 数组
|
||||
* 2. {@link Iterable}对象
|
||||
* 3. JSON数组字符串
|
||||
* </pre>
|
||||
*
|
||||
* @param object 数组或集合或JSON数组字符串
|
||||
* @param jsonConfig JSON选项
|
||||
* @throws JSONException 非数组或集合
|
||||
* @since 4.6.5
|
||||
*/
|
||||
public JSONArray(Object object, JSONConfig jsonConfig) throws JSONException {
|
||||
this(object, jsonConfig, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从对象构造<br>
|
||||
* 支持以下类型的参数:
|
||||
*
|
||||
* <pre>
|
||||
* 1. 数组
|
||||
* 2. {@link Iterable}对象
|
||||
* 3. JSON数组字符串
|
||||
* </pre>
|
||||
*
|
||||
* @param object 数组或集合或JSON数组字符串
|
||||
* @param jsonConfig JSON选项
|
||||
* @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤
|
||||
* @throws JSONException 非数组或集合
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public JSONArray(Object object, JSONConfig jsonConfig, Filter<Mutable<Object>> filter) throws JSONException {
|
||||
this(DEFAULT_CAPACITY, jsonConfig);
|
||||
ObjectMapper.of(object).map(this, filter);
|
||||
}
|
||||
// endregion
|
||||
|
||||
@Override
|
||||
public JSONConfig getConfig() {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置转为字符串时的日期格式,默认为时间戳(null值)
|
||||
*
|
||||
* @param format 格式,null表示使用时间戳
|
||||
* @return this
|
||||
* @since 4.1.19
|
||||
*/
|
||||
public JSONArray setDateFormat(String format) {
|
||||
this.config.setDateFormat(format);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSONArray转为以{@code separator}为分界符的字符串
|
||||
*
|
||||
* @param separator 分界符
|
||||
* @return a string.
|
||||
* @throws JSONException If the array contains an invalid number.
|
||||
*/
|
||||
public String join(String separator) throws JSONException {
|
||||
return StrJoiner.of(separator)
|
||||
.append(this, InternalJSONUtil::valueToString).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(int index) {
|
||||
return this.rawList.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObj(Integer index, Object defaultValue) {
|
||||
return (index < 0 || index >= this.size()) ? defaultValue : this.rawList.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getByPath(String expression) {
|
||||
return BeanPath.create(expression).get(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getByPath(String expression, Class<T> resultType) {
|
||||
return JSONConverter.jsonConvert(resultType, getByPath(expression), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putByPath(String expression, Object value) {
|
||||
BeanPath.create(expression).set(this, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an object value. This increases the array's length by one. <br>
|
||||
* 加入元素,数组长度+1,等同于 {@link JSONArray#add(Object)}
|
||||
*
|
||||
* @param value 值,可以是: Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the JSONNull.NULL。
|
||||
* @return this.
|
||||
* @see #set(Object)
|
||||
*/
|
||||
public JSONArray put(Object value) {
|
||||
return set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an object value. This increases the array's length by one. <br>
|
||||
* 加入元素,数组长度+1,等同于 {@link JSONArray#add(Object)}
|
||||
*
|
||||
* @param value 值,可以是: Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the JSONNull.NULL。
|
||||
* @return this.
|
||||
* @since 5.2.5
|
||||
*/
|
||||
public JSONArray set(Object value) {
|
||||
this.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入或者替换JSONArray中指定Index的值,如果index大于JSONArray的长度,将在指定index设置值,之前的位置填充JSONNull.Null
|
||||
*
|
||||
* @param index 位置
|
||||
* @param value 值对象. 可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL.
|
||||
* @return this.
|
||||
* @throws JSONException index < 0 或者非有限的数字
|
||||
* @see #set(int, Object)
|
||||
*/
|
||||
public JSONArray put(int index, Object value) throws JSONException {
|
||||
this.set(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据给定名列表,与其位置对应的值组成JSONObject
|
||||
*
|
||||
* @param names 名列表,位置与JSONArray中的值位置对应
|
||||
* @return A JSONObject,无名或值返回null
|
||||
* @throws JSONException 如果任何一个名为null
|
||||
*/
|
||||
public JSONObject toJSONObject(JSONArray names) throws JSONException {
|
||||
if (names == null || names.size() == 0 || this.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
final JSONObject jo = new JSONObject(this.config);
|
||||
for (int i = 0; i < names.size(); i += 1) {
|
||||
jo.set(names.getStr(i), this.getObj(i));
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((rawList == null) ? 0 : rawList.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final JSONArray other = (JSONArray) obj;
|
||||
if (rawList == null) {
|
||||
return other.rawList == null;
|
||||
} else {
|
||||
return rawList.equals(other.rawList);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Object> iterator() {
|
||||
return rawList.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当此JSON列表的每个元素都是一个JSONObject时,可以调用此方法返回一个Iterable,便于使用foreach语法遍历
|
||||
*
|
||||
* @return Iterable
|
||||
* @since 4.0.12
|
||||
*/
|
||||
public Iterable<JSONObject> jsonIter() {
|
||||
return new JSONObjectIter(iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return rawList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return rawList.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return rawList.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return rawList.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return (T[]) JSONConverter.toArray(this, a.getClass().getComponentType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Object e) {
|
||||
return addRaw(JSONUtil.wrap(e, this.config), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object remove(int index) {
|
||||
return index >= 0 && index < this.size() ? this.rawList.remove(index) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return rawList.remove(o);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
return rawList.containsAll(c);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public boolean addAll(Collection<?> c) {
|
||||
if (CollUtil.isEmpty(c)) {
|
||||
return false;
|
||||
}
|
||||
for (Object obj : c) {
|
||||
this.add(obj);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<?> c) {
|
||||
if (CollUtil.isEmpty(c)) {
|
||||
return false;
|
||||
}
|
||||
final ArrayList<Object> list = new ArrayList<>(c.size());
|
||||
for (Object object : c) {
|
||||
list.add(JSONUtil.wrap(object, this.config));
|
||||
}
|
||||
return rawList.addAll(index, list);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
return this.rawList.removeAll(c);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
return this.rawList.retainAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.rawList.clear();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入或者替换JSONArray中指定Index的值,如果index大于JSONArray的长度,将在指定index设置值,之前的位置填充JSONNull.Null
|
||||
*
|
||||
* @param index 位置
|
||||
* @param element 值对象. 可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL.
|
||||
* @return 替换的值,即之前的值
|
||||
*/
|
||||
@Override
|
||||
public Object set(int index, Object element) {
|
||||
return set(index, element, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入或者替换JSONArray中指定Index的值,如果index大于JSONArray的长度,将在指定index设置值,之前的位置填充JSONNull.Null
|
||||
*
|
||||
* @param index 位置
|
||||
* @param element 值对象. 可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL.
|
||||
* @param filter 过滤器,可以修改值,key(index)无法修改
|
||||
* @return 替换的值,即之前的值
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public Object set(int index, Object element, Filter<MutablePair<Integer, Object>> filter) {
|
||||
// 添加前置过滤,通过MutablePair实现过滤、修改键值对等
|
||||
if (null != filter) {
|
||||
final MutablePair<Integer, Object> pair = new MutablePair<>(index, element);
|
||||
if (filter.accept(pair)) {
|
||||
// 使用修改后的值
|
||||
element = pair.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
if (index >= size()) {
|
||||
add(index, element);
|
||||
}
|
||||
return this.rawList.set(index, JSONUtil.wrap(element, this.config));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, Object element) {
|
||||
if (index < 0) {
|
||||
throw new JSONException("JSONArray[{}] not found.", index);
|
||||
}
|
||||
if (index < this.size()) {
|
||||
InternalJSONUtil.testValidity(element);
|
||||
this.rawList.add(index, JSONUtil.wrap(element, this.config));
|
||||
} else {
|
||||
while (index != this.size()) {
|
||||
this.add(JSONNull.NULL);
|
||||
}
|
||||
this.set(element);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
return this.rawList.indexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
return this.rawList.lastIndexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<Object> listIterator() {
|
||||
return this.rawList.listIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<Object> listIterator(int index) {
|
||||
return this.rawList.listIterator(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> subList(int fromIndex, int toIndex) {
|
||||
return this.rawList.subList(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为Bean数组
|
||||
*
|
||||
* @param arrayClass 数组元素类型
|
||||
* @return 实体类对象
|
||||
*/
|
||||
public Object toArray(Class<?> arrayClass) {
|
||||
return JSONConverter.toArray(this, arrayClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为{@link ArrayList}
|
||||
*
|
||||
* @param <T> 元素类型
|
||||
* @param elementType 元素类型
|
||||
* @return {@link ArrayList}
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public <T> List<T> toList(Class<T> elementType) {
|
||||
return JSONConverter.toList(this, elementType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为JSON字符串,无缩进
|
||||
*
|
||||
* @return JSONArray字符串
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.toJSONString(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回JSON字符串<br>
|
||||
* 支持过滤器,即选择哪些字段或值不写出
|
||||
*
|
||||
* @param indentFactor 每层缩进空格数
|
||||
* @param filter 过滤器,可以修改值,key(index)无法修改
|
||||
* @return JSON字符串
|
||||
* @since 5.7.15
|
||||
*/
|
||||
public String toJSONString(int indentFactor, Filter<MutablePair<Object, Object>> filter) {
|
||||
final StringWriter sw = new StringWriter();
|
||||
synchronized (sw.getBuffer()) {
|
||||
return this.write(sw, indentFactor, 0, filter).toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Writer write(Writer writer, int indentFactor, int indent) throws JSONException {
|
||||
return write(writer, indentFactor, indent, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将JSON内容写入Writer<br>
|
||||
* 支持过滤器,即选择哪些字段或值不写出
|
||||
*
|
||||
* @param writer writer
|
||||
* @param indentFactor 缩进因子,定义每一级别增加的缩进量
|
||||
* @param indent 本级别缩进量
|
||||
* @param filter 过滤器,可以修改值,key(index)无法修改
|
||||
* @return Writer
|
||||
* @throws JSONException JSON相关异常
|
||||
* @since 5.7.15
|
||||
*/
|
||||
public Writer write(Writer writer, int indentFactor, int indent, Filter<MutablePair<Object, Object>> filter) throws JSONException {
|
||||
final JSONWriter jsonWriter = JSONWriter.of(writer, indentFactor, indent, config).beginArray();
|
||||
|
||||
CollUtil.forEach(this, (value, index) -> jsonWriter.writeField(new MutablePair<>(index, value), filter));
|
||||
jsonWriter.end();
|
||||
// 此处不关闭Writer,考虑writer后续还需要填内容
|
||||
return writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
final JSONArray clone = (JSONArray) super.clone();
|
||||
clone.rawList = ObjectUtil.clone(this.rawList);
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* 原始添加,添加的对象不做任何处理
|
||||
*
|
||||
* @param obj 添加的对象
|
||||
* @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤
|
||||
* @return 是否加入成功
|
||||
* @since 5.8.0
|
||||
*/
|
||||
protected boolean addRaw(Object obj, Filter<Mutable<Object>> filter) {
|
||||
// 添加前置过滤,通过MutablePair实现过滤、修改键值对等
|
||||
if (null != filter) {
|
||||
final Mutable<Object> mutable = new MutableObj<>(obj);
|
||||
if (filter.accept(mutable)) {
|
||||
// 使用修改后的值
|
||||
obj = mutable.get();
|
||||
} else {
|
||||
// 键值对被过滤
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return this.rawList.add(obj);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
/**
|
||||
* 实现此接口的类可以通过实现{@code parse(value)}方法来将JSON中的值解析为此对象的值
|
||||
*
|
||||
* @author Looly
|
||||
* @since 5.7.8
|
||||
*/
|
||||
public interface JSONBeanParser<T> {
|
||||
|
||||
/**
|
||||
* value转Bean<br>
|
||||
* 通过实现此接口,将JSON中的值填充到当前对象的字段值中,即对象自行实现JSON反序列化逻辑
|
||||
*
|
||||
* @param value 被解析的对象类型,可能为JSON或者普通String、Number等
|
||||
*/
|
||||
void parse(T value);
|
||||
}
|
|
@ -0,0 +1,265 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.comparator.CompareUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* JSON配置项
|
||||
*
|
||||
* @author looly
|
||||
* @since 4.1.19
|
||||
*/
|
||||
public class JSONConfig implements Serializable {
|
||||
private static final long serialVersionUID = 119730355204738278L;
|
||||
|
||||
/**
|
||||
* 键排序规则,{@code null}表示不排序,不排序情况下,按照加入顺序排序
|
||||
*/
|
||||
private Comparator<String> keyComparator;
|
||||
/**
|
||||
* 是否忽略转换过程中的异常
|
||||
*/
|
||||
private boolean ignoreError;
|
||||
/**
|
||||
* 是否忽略键的大小写
|
||||
*/
|
||||
private boolean ignoreCase;
|
||||
/**
|
||||
* 日期格式,null表示默认的时间戳
|
||||
*/
|
||||
private String dateFormat;
|
||||
/**
|
||||
* 是否忽略null值
|
||||
*/
|
||||
private boolean ignoreNullValue = true;
|
||||
/**
|
||||
* 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
|
||||
*/
|
||||
private boolean transientSupport = true;
|
||||
|
||||
/**
|
||||
* 是否去除末尾多余0,例如如果为true,5.0返回5
|
||||
*/
|
||||
private boolean stripTrailingZeros = true;
|
||||
|
||||
/**
|
||||
* 是否检查重复key
|
||||
*/
|
||||
private boolean checkDuplicate;
|
||||
|
||||
/**
|
||||
* 创建默认的配置项
|
||||
*
|
||||
* @return JSONConfig
|
||||
*/
|
||||
public static JSONConfig create() {
|
||||
return new JSONConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有序,顺序按照加入顺序排序,只针对JSONObject有效
|
||||
*
|
||||
* @return 是否有序
|
||||
* @deprecated 始终返回 {@code true}
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isOrder() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否有序,顺序按照加入顺序排序,只针对JSONObject有效
|
||||
*
|
||||
* @param order 是否有序
|
||||
* @return this
|
||||
* @deprecated 始终有序,无需设置
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Deprecated
|
||||
public JSONConfig setOrder(boolean order) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取键排序规则<br>
|
||||
* 键排序规则,{@code null}表示不排序,不排序情况下,按照加入顺序排序
|
||||
*
|
||||
* @return 键排序规则
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public Comparator<String> getKeyComparator() {
|
||||
return this.keyComparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置自然排序,即按照字母顺序排序
|
||||
*
|
||||
* @return this
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public JSONConfig setNatureKeyComparator() {
|
||||
return setKeyComparator(CompareUtil.naturalComparator());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置键排序规则<br>
|
||||
* 键排序规则,{@code null}表示不排序,不排序情况下,按照加入顺序排序
|
||||
*
|
||||
* @param keyComparator 键排序规则
|
||||
* @return this
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public JSONConfig setKeyComparator(Comparator<String> keyComparator) {
|
||||
this.keyComparator = keyComparator;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否忽略转换过程中的异常
|
||||
*
|
||||
* @return 是否忽略转换过程中的异常
|
||||
*/
|
||||
public boolean isIgnoreError() {
|
||||
return ignoreError;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否忽略转换过程中的异常
|
||||
*
|
||||
* @param ignoreError 是否忽略转换过程中的异常
|
||||
* @return this
|
||||
*/
|
||||
public JSONConfig setIgnoreError(boolean ignoreError) {
|
||||
this.ignoreError = ignoreError;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否忽略键的大小写
|
||||
*
|
||||
* @return 是否忽略键的大小写
|
||||
*/
|
||||
public boolean isIgnoreCase() {
|
||||
return ignoreCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否忽略键的大小写
|
||||
*
|
||||
* @param ignoreCase 是否忽略键的大小写
|
||||
* @return this
|
||||
*/
|
||||
public JSONConfig setIgnoreCase(boolean ignoreCase) {
|
||||
this.ignoreCase = ignoreCase;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期格式,null表示默认的时间戳
|
||||
*
|
||||
* @return 日期格式,null表示默认的时间戳
|
||||
*/
|
||||
public String getDateFormat() {
|
||||
return dateFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置日期格式,null表示默认的时间戳<br>
|
||||
* 此方法设置的日期格式仅对转换为JSON字符串有效,对解析JSON为bean无效。
|
||||
*
|
||||
* @param dateFormat 日期格式,null表示默认的时间戳
|
||||
* @return this
|
||||
*/
|
||||
public JSONConfig setDateFormat(String dateFormat) {
|
||||
this.dateFormat = dateFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否忽略null值
|
||||
*
|
||||
* @return 是否忽略null值
|
||||
*/
|
||||
public boolean isIgnoreNullValue() {
|
||||
return this.ignoreNullValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否忽略null值
|
||||
*
|
||||
* @param ignoreNullValue 是否忽略null值
|
||||
* @return this
|
||||
*/
|
||||
public JSONConfig setIgnoreNullValue(boolean ignoreNullValue) {
|
||||
this.ignoreNullValue = ignoreNullValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
|
||||
*
|
||||
* @return 是否支持
|
||||
* @since 5.4.2
|
||||
*/
|
||||
public boolean isTransientSupport() {
|
||||
return this.transientSupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
|
||||
*
|
||||
* @param transientSupport 是否支持
|
||||
* @return this
|
||||
* @since 5.4.2
|
||||
*/
|
||||
public JSONConfig setTransientSupport(boolean transientSupport) {
|
||||
this.transientSupport = transientSupport;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否去除末尾多余0,例如如果为true,5.0返回5
|
||||
*
|
||||
* @return 是否去除末尾多余0,例如如果为true,5.0返回5
|
||||
* @since 5.6.2
|
||||
*/
|
||||
public boolean isStripTrailingZeros() {
|
||||
return stripTrailingZeros;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否去除末尾多余0,例如如果为true,5.0返回5
|
||||
*
|
||||
* @param stripTrailingZeros 是否去除末尾多余0,例如如果为true,5.0返回5
|
||||
* @return this
|
||||
* @since 5.6.2
|
||||
*/
|
||||
public JSONConfig setStripTrailingZeros(boolean stripTrailingZeros) {
|
||||
this.stripTrailingZeros = stripTrailingZeros;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否检查多个相同的key
|
||||
*
|
||||
* @return 是否检查多个相同的key
|
||||
* @since 5.8.5
|
||||
*/
|
||||
public boolean isCheckDuplicate() {
|
||||
return checkDuplicate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否检查多个相同的key
|
||||
*
|
||||
* @param checkDuplicate 是否检查多个相同的key
|
||||
* @return this
|
||||
* @since 5.8.5
|
||||
*/
|
||||
public JSONConfig setCheckDuplicate(boolean checkDuplicate) {
|
||||
this.checkDuplicate = checkDuplicate;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.bean.BeanUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.codec.Base64;
|
||||
import aiyh.utils.tool.cn.hutool.core.convert.Convert;
|
||||
import aiyh.utils.tool.cn.hutool.core.convert.ConvertException;
|
||||
import aiyh.utils.tool.cn.hutool.core.convert.Converter;
|
||||
import aiyh.utils.tool.cn.hutool.core.convert.ConverterRegistry;
|
||||
import aiyh.utils.tool.cn.hutool.core.convert.impl.ArrayConverter;
|
||||
import aiyh.utils.tool.cn.hutool.core.convert.impl.BeanConverter;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.TypeUtil;
|
||||
import aiyh.utils.tool.cn.hutool.json.serialize.GlobalSerializeMapping;
|
||||
import aiyh.utils.tool.cn.hutool.json.serialize.JSONDeserializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* JSON转换器
|
||||
*
|
||||
* @author looly
|
||||
* @since 4.2.2
|
||||
*/
|
||||
public class JSONConverter implements Converter<JSON> {
|
||||
|
||||
static {
|
||||
// 注册到转换中心
|
||||
final ConverterRegistry registry = ConverterRegistry.getInstance();
|
||||
registry.putCustom(JSON.class, JSONConverter.class);
|
||||
registry.putCustom(JSONObject.class, JSONConverter.class);
|
||||
registry.putCustom(JSONArray.class, JSONConverter.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSONArray转数组
|
||||
*
|
||||
* @param jsonArray JSONArray
|
||||
* @param arrayClass 数组元素类型
|
||||
* @return 数组对象
|
||||
*/
|
||||
protected static Object toArray(JSONArray jsonArray, Class<?> arrayClass) {
|
||||
return new ArrayConverter(arrayClass).convert(jsonArray, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将JSONArray转换为指定类型的对量列表
|
||||
*
|
||||
* @param <T> 元素类型
|
||||
* @param jsonArray JSONArray
|
||||
* @param elementType 对象元素类型
|
||||
* @return 对象列表
|
||||
*/
|
||||
protected static <T> List<T> toList(JSONArray jsonArray, Class<T> elementType) {
|
||||
return Convert.toList(elementType, jsonArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON递归转换<br>
|
||||
* 首先尝试JDK类型转换,如果失败尝试JSON转Bean<br>
|
||||
* 如果遇到{@link JSONBeanParser},则调用其{@link JSONBeanParser#parse(Object)}方法转换。
|
||||
*
|
||||
* @param <T> 转换后的对象类型
|
||||
* @param targetType 目标类型
|
||||
* @param value 值
|
||||
* @param ignoreError 是否忽略转换错误
|
||||
* @return 目标类型的值
|
||||
* @throws ConvertException 转换失败
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static <T> T jsonConvert(Type targetType, Object value, boolean ignoreError) throws ConvertException {
|
||||
if (JSONUtil.isNull(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// since 5.7.8,增加自定义Bean反序列化接口
|
||||
if (targetType instanceof Class) {
|
||||
final Class<?> clazz = (Class<?>) targetType;
|
||||
if (JSONBeanParser.class.isAssignableFrom(clazz)) {
|
||||
@SuppressWarnings("rawtypes") final JSONBeanParser target = (JSONBeanParser) ReflectUtil.newInstanceIfPossible(clazz);
|
||||
if (null == target) {
|
||||
throw new ConvertException("Can not instance [{}]", targetType);
|
||||
}
|
||||
target.parse(value);
|
||||
return (T) target;
|
||||
} else if (targetType == byte[].class && value instanceof CharSequence) {
|
||||
// issue#I59LW4
|
||||
return (T) Base64.decode((CharSequence) value);
|
||||
}
|
||||
}
|
||||
|
||||
return jsonToBean(targetType, value, ignoreError);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON递归转换<br>
|
||||
* 首先尝试JDK类型转换,如果失败尝试JSON转Bean
|
||||
*
|
||||
* @param <T> 转换后的对象类型
|
||||
* @param targetType 目标类型
|
||||
* @param value 值,JSON格式
|
||||
* @param ignoreError 是否忽略转换错误
|
||||
* @return 目标类型的值
|
||||
* @throws ConvertException 转换失败
|
||||
* @since 5.7.10
|
||||
*/
|
||||
protected static <T> T jsonToBean(Type targetType, Object value, boolean ignoreError) throws ConvertException {
|
||||
if (JSONUtil.isNull(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value instanceof JSON) {
|
||||
final JSONDeserializer<?> deserializer = GlobalSerializeMapping.getDeserializer(targetType);
|
||||
if (null != deserializer) {
|
||||
// noinspection unchecked
|
||||
return (T) deserializer.deserialize((JSON) value);
|
||||
}
|
||||
|
||||
// issue#2212@Github
|
||||
// 在JSONObject转Bean时,读取JSONObject本身的配置文件
|
||||
if (value instanceof JSONGetter
|
||||
&& targetType instanceof Class && BeanUtil.hasSetter((Class<?>) targetType)) {
|
||||
final JSONConfig config = ((JSONGetter<?>) value).getConfig();
|
||||
final Converter<T> converter = new BeanConverter<>(targetType,
|
||||
InternalJSONUtil.toCopyOptions(config).setIgnoreError(ignoreError));
|
||||
return converter.convertWithCheck(value, null, ignoreError);
|
||||
}
|
||||
}
|
||||
|
||||
final T targetValue = Convert.convertWithCheck(targetType, value, null, ignoreError);
|
||||
|
||||
if (null == targetValue && !ignoreError) {
|
||||
if (StrUtil.isBlankIfStr(value)) {
|
||||
// 对于传入空字符串的情况,如果转换的目标对象是非字符串或非原始类型,转换器会返回false。
|
||||
// 此处特殊处理,认为返回null属于正常情况
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new ConvertException("Can not convert {} to type {}", value, ObjectUtil.defaultIfNull(TypeUtil.getClass(targetType), targetType));
|
||||
}
|
||||
|
||||
return targetValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSON convert(Object value, JSON defaultValue) throws IllegalArgumentException {
|
||||
return JSONUtil.parse(value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* JSON异常
|
||||
*
|
||||
* @author looly
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public class JSONException extends RuntimeException {
|
||||
private static final long serialVersionUID = 0;
|
||||
|
||||
public JSONException(Throwable e) {
|
||||
super(ExceptionUtil.getMessage(e), e);
|
||||
}
|
||||
|
||||
public JSONException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JSONException(String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params));
|
||||
}
|
||||
|
||||
public JSONException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public JSONException(String message, Throwable throwable, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, throwable, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
public JSONException(Throwable throwable, String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params), throwable);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,237 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.convert.Convert;
|
||||
import aiyh.utils.tool.cn.hutool.core.convert.ConvertException;
|
||||
import aiyh.utils.tool.cn.hutool.core.date.DateUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.getter.OptNullBasicTypeFromObjectGetter;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 用于JSON的Getter类,提供各种类型的Getter方法
|
||||
*
|
||||
* @param <K> Key类型
|
||||
* @author Looly
|
||||
*/
|
||||
public interface JSONGetter<K> extends OptNullBasicTypeFromObjectGetter<K> {
|
||||
|
||||
/**
|
||||
* 获取JSON配置
|
||||
*
|
||||
* @return {@link JSONConfig}
|
||||
* @since 5.3.0
|
||||
*/
|
||||
JSONConfig getConfig();
|
||||
|
||||
/**
|
||||
* key对应值是否为{@code null}或无此key
|
||||
*
|
||||
* @param key 键
|
||||
* @return true 无此key或值为{@code null}或{@link JSONNull#NULL}返回{@code false},其它返回{@code true}
|
||||
*/
|
||||
default boolean isNull(K key) {
|
||||
return JSONUtil.isNull(this.getObj(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串类型值,并转义不可见字符,如'\n'换行符会被转义为字符串"\n"
|
||||
*
|
||||
* @param key 键
|
||||
* @return 字符串类型值
|
||||
* @since 4.2.2
|
||||
*/
|
||||
default String getStrEscaped(K key) {
|
||||
return getStrEscaped(key, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串类型值,并转义不可见字符,如'\n'换行符会被转义为字符串"\n"
|
||||
*
|
||||
* @param key 键
|
||||
* @param defaultValue 默认值
|
||||
* @return 字符串类型值
|
||||
* @since 4.2.2
|
||||
*/
|
||||
default String getStrEscaped(K key, String defaultValue) {
|
||||
return JSONUtil.escape(getStr(key, defaultValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得JSONArray对象<br>
|
||||
* 如果值为其它类型对象,尝试转换为{@link JSONArray}返回,否则抛出异常
|
||||
*
|
||||
* @param key KEY
|
||||
* @return JSONArray对象,如果值为{@code null},返回{@code null},非JSONArray类型,尝试转换,转换失败抛出异常
|
||||
*/
|
||||
default JSONArray getJSONArray(K key) {
|
||||
final Object object = this.getObj(key);
|
||||
if (JSONUtil.isNull(object)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (object instanceof JSON) {
|
||||
return (JSONArray) object;
|
||||
}
|
||||
return new JSONArray(object, getConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得JSONObject对象<br>
|
||||
* 如果值为其它类型对象,尝试转换为{@link JSONObject}返回,否则抛出异常
|
||||
*
|
||||
* @param key KEY
|
||||
* @return JSONObject对象,如果值为{@code null},返回{@code null},非JSONObject类型,尝试转换,转换失败抛出异常
|
||||
*/
|
||||
default JSONObject getJSONObject(K key) {
|
||||
final Object object = this.getObj(key);
|
||||
if (JSONUtil.isNull(object)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (object instanceof JSON) {
|
||||
return (JSONObject) object;
|
||||
}
|
||||
return new JSONObject(object, getConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从JSON中直接获取Bean对象<br>
|
||||
* 先获取JSONObject对象,然后转为Bean对象
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param key KEY
|
||||
* @param beanType Bean类型
|
||||
* @return Bean对象,如果值为null或者非JSONObject类型,返回null
|
||||
* @since 3.1.1
|
||||
*/
|
||||
default <T> T getBean(K key, Class<T> beanType) {
|
||||
final JSONObject obj = getJSONObject(key);
|
||||
return (null == obj) ? null : obj.toBean(beanType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从JSON中直接获取Bean的List列表<br>
|
||||
* 先获取JSONArray对象,然后转为Bean的List
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param key KEY
|
||||
* @param beanType Bean类型
|
||||
* @return Bean的List,如果值为null或者非JSONObject类型,返回null
|
||||
* @since 5.7.20
|
||||
*/
|
||||
default <T> List<T> getBeanList(K key, Class<T> beanType) {
|
||||
final JSONArray jsonArray = getJSONArray(key);
|
||||
return (null == jsonArray) ? null : jsonArray.toList(beanType);
|
||||
}
|
||||
|
||||
@Override
|
||||
default Date getDate(K key, Date defaultValue) {
|
||||
// 默认转换
|
||||
final Object obj = getObj(key);
|
||||
if (JSONUtil.isNull(obj)) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (obj instanceof Date) {
|
||||
return (Date) obj;
|
||||
}
|
||||
|
||||
final Optional<String> formatOps = Optional.ofNullable(getConfig()).map(JSONConfig::getDateFormat);
|
||||
if (formatOps.isPresent()) {
|
||||
final String format = formatOps.get();
|
||||
if (StrUtil.isNotBlank(format)) {
|
||||
// 用户指定了日期格式,获取日期属性时使用对应格式
|
||||
final String str = Convert.toStr(obj);
|
||||
if (null == str) {
|
||||
return defaultValue;
|
||||
}
|
||||
return DateUtil.parse(str, format);
|
||||
}
|
||||
}
|
||||
|
||||
return Convert.toDate(obj, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link LocalDateTime}类型值
|
||||
*
|
||||
* @param key 键
|
||||
* @param defaultValue 默认值
|
||||
* @return {@link LocalDateTime}
|
||||
* @since 5.7.7
|
||||
*/
|
||||
default LocalDateTime getLocalDateTime(K key, LocalDateTime defaultValue) {
|
||||
// 默认转换
|
||||
final Object obj = getObj(key);
|
||||
if (JSONUtil.isNull(obj)) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (obj instanceof LocalDateTime) {
|
||||
return (LocalDateTime) obj;
|
||||
}
|
||||
|
||||
final Optional<String> formatOps = Optional.ofNullable(getConfig()).map(JSONConfig::getDateFormat);
|
||||
if (formatOps.isPresent()) {
|
||||
final String format = formatOps.get();
|
||||
if (StrUtil.isNotBlank(format)) {
|
||||
// 用户指定了日期格式,获取日期属性时使用对应格式
|
||||
final String str = Convert.toStr(obj);
|
||||
if (null == str) {
|
||||
return defaultValue;
|
||||
}
|
||||
return LocalDateTimeUtil.parse(str, format);
|
||||
}
|
||||
}
|
||||
|
||||
return Convert.toLocalDateTime(obj, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取byte[]数据
|
||||
*
|
||||
* @param key 键
|
||||
* @return 值
|
||||
* @since 5.8.2
|
||||
*/
|
||||
default byte[] getBytes(K key) {
|
||||
return get(key, byte[].class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定类型的对象<br>
|
||||
* 转换失败或抛出异常
|
||||
*
|
||||
* @param <T> 获取的对象类型
|
||||
* @param key 键
|
||||
* @param type 获取对象类型
|
||||
* @return 对象
|
||||
* @throws ConvertException 转换异常
|
||||
* @since 3.0.8
|
||||
*/
|
||||
default <T> T get(K key, Class<T> type) throws ConvertException {
|
||||
return get(key, type, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定类型的对象
|
||||
*
|
||||
* @param <T> 获取的对象类型
|
||||
* @param key 键
|
||||
* @param type 获取对象类型
|
||||
* @param ignoreError 是否跳过转换失败的对象或值
|
||||
* @return 对象
|
||||
* @throws ConvertException 转换异常
|
||||
* @since 3.0.8
|
||||
*/
|
||||
default <T> T get(K key, Class<T> type, boolean ignoreError) throws ConvertException {
|
||||
final Object value = this.getObj(key);
|
||||
if (JSONUtil.isNull(value)) {
|
||||
return null;
|
||||
}
|
||||
return JSONConverter.jsonConvert(type, value, ignoreError);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 用于定义{@code null},与Javascript中null相对应<br>
|
||||
* Java中的{@code null}值在js中表示为undefined。
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class JSONNull implements Serializable {
|
||||
private static final long serialVersionUID = 2633815155870764938L;
|
||||
|
||||
/**
|
||||
* {@code NULL} 对象用于减少歧义来表示Java 中的{@code null} <br>
|
||||
* {@code NULL.equals(null)} 返回 {@code true}. <br>
|
||||
* {@code NULL.toString()} 返回 {@code "null"}.
|
||||
*/
|
||||
public static final JSONNull NULL = new JSONNull();
|
||||
|
||||
/**
|
||||
* A Null object is equal to the null value and to itself.
|
||||
* 对象与其本身和{@code null}值相等
|
||||
*
|
||||
* @param object An object to test for nullness.
|
||||
* @return true if the object parameter is the JSONObject.NULL object or null.
|
||||
*/
|
||||
@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
return object == null || (object == this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "null" string value.
|
||||
* 获得“null”字符串
|
||||
*
|
||||
* @return The string "null".
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return StrUtil.NULL;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,577 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.bean.BeanPath;
|
||||
import aiyh.utils.tool.cn.hutool.core.collection.CollectionUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Filter;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.mutable.MutablePair;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.CaseInsensitiveMap;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.MapUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.MapWrapper;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||
import aiyh.utils.tool.cn.hutool.json.serialize.JSONWriter;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JSON对象<br>
|
||||
* 例:<br>
|
||||
*
|
||||
* <pre>
|
||||
* json = new JSONObject().put("JSON", "Hello, World!").toString();
|
||||
* </pre>
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
public class JSONObject extends MapWrapper<String, Object> implements JSON, JSONGetter<String> {
|
||||
private static final long serialVersionUID = -330220388580734346L;
|
||||
|
||||
/**
|
||||
* 默认初始大小
|
||||
*/
|
||||
public static final int DEFAULT_CAPACITY = MapUtil.DEFAULT_INITIAL_CAPACITY;
|
||||
|
||||
/**
|
||||
* 配置项
|
||||
*/
|
||||
private JSONConfig config;
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------- Constructor start
|
||||
|
||||
/**
|
||||
* 构造,初始容量为 {@link #DEFAULT_CAPACITY},KEY无序
|
||||
*/
|
||||
public JSONObject() {
|
||||
this(DEFAULT_CAPACITY, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,初始容量为 {@link #DEFAULT_CAPACITY}
|
||||
*
|
||||
* @param isOrder 是否有序
|
||||
* @since 3.0.9
|
||||
*/
|
||||
public JSONObject(boolean isOrder) {
|
||||
this(DEFAULT_CAPACITY, isOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param capacity 初始大小
|
||||
* @param isOrder 是否有序
|
||||
* @since 3.0.9
|
||||
*/
|
||||
public JSONObject(int capacity, boolean isOrder) {
|
||||
this(capacity, false, isOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param capacity 初始大小
|
||||
* @param isIgnoreCase 是否忽略KEY大小写
|
||||
* @param isOrder 是否有序
|
||||
* @since 3.3.1
|
||||
* @deprecated isOrder无效
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Deprecated
|
||||
public JSONObject(int capacity, boolean isIgnoreCase, boolean isOrder) {
|
||||
this(capacity, JSONConfig.create().setIgnoreCase(isIgnoreCase));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param config JSON配置项
|
||||
* @since 4.6.5
|
||||
*/
|
||||
public JSONObject(JSONConfig config) {
|
||||
this(DEFAULT_CAPACITY, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param capacity 初始大小
|
||||
* @param config JSON配置项,{@code null}则使用默认配置
|
||||
* @since 4.1.19
|
||||
*/
|
||||
public JSONObject(int capacity, JSONConfig config) {
|
||||
super(InternalJSONUtil.createRawMap(capacity, ObjectUtil.defaultIfNull(config, JSONConfig.create())));
|
||||
this.config = ObjectUtil.defaultIfNull(config, JSONConfig.create());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建JSONObject,JavaBean默认忽略null值,其它对象不忽略,规则如下:
|
||||
* <ol>
|
||||
* <li>value为Map,将键值对加入JSON对象</li>
|
||||
* <li>value为JSON字符串(CharSequence),使用JSONTokener解析</li>
|
||||
* <li>value为JSONTokener,直接解析</li>
|
||||
* <li>value为普通JavaBean,如果为普通的JavaBean,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象。
|
||||
* 例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param source JavaBean或者Map对象或者String
|
||||
*/
|
||||
public JSONObject(Object source) {
|
||||
this(source, InternalJSONUtil.defaultIgnoreNullValue(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建JSONObject,规则如下:
|
||||
* <ol>
|
||||
* <li>value为Map,将键值对加入JSON对象</li>
|
||||
* <li>value为JSON字符串(CharSequence),使用JSONTokener解析</li>
|
||||
* <li>value为JSONTokener,直接解析</li>
|
||||
* <li>value为普通JavaBean,如果为普通的JavaBean,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象。例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param source JavaBean或者Map对象或者String
|
||||
* @param ignoreNullValue 是否忽略空值
|
||||
* @since 3.0.9
|
||||
*/
|
||||
public JSONObject(Object source, boolean ignoreNullValue) {
|
||||
this(source, JSONConfig.create().setIgnoreNullValue(ignoreNullValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建JSONObject,规则如下:
|
||||
* <ol>
|
||||
* <li>value为Map,将键值对加入JSON对象</li>
|
||||
* <li>value为JSON字符串(CharSequence),使用JSONTokener解析</li>
|
||||
* <li>value为JSONTokener,直接解析</li>
|
||||
* <li>value为普通JavaBean,如果为普通的JavaBean,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象。例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param source JavaBean或者Map对象或者String
|
||||
* @param ignoreNullValue 是否忽略空值,如果source为JSON字符串,不忽略空值
|
||||
* @param isOrder 是否有序
|
||||
* @since 4.2.2
|
||||
* @deprecated isOrder参数不再需要,JSONObject默认有序!
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Deprecated
|
||||
public JSONObject(Object source, boolean ignoreNullValue, boolean isOrder) {
|
||||
this(source, JSONConfig.create()//
|
||||
.setIgnoreCase((source instanceof CaseInsensitiveMap))//
|
||||
.setIgnoreNullValue(ignoreNullValue)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建JSONObject,规则如下:
|
||||
* <ol>
|
||||
* <li>value为Map,将键值对加入JSON对象</li>
|
||||
* <li>value为JSON字符串(CharSequence),使用JSONTokener解析</li>
|
||||
* <li>value为JSONTokener,直接解析</li>
|
||||
* <li>value为普通JavaBean,如果为普通的JavaBean,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象。例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* 如果给定值为Map,将键值对加入JSON对象;<br>
|
||||
* 如果为普通的JavaBean,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象<br>
|
||||
* 例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"
|
||||
*
|
||||
* @param source JavaBean或者Map对象或者String
|
||||
* @param config JSON配置文件,{@code null}则使用默认配置
|
||||
* @since 4.2.2
|
||||
*/
|
||||
public JSONObject(Object source, JSONConfig config) {
|
||||
this(source, config, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建JSONObject,规则如下:
|
||||
* <ol>
|
||||
* <li>value为Map,将键值对加入JSON对象</li>
|
||||
* <li>value为JSON字符串(CharSequence),使用JSONTokener解析</li>
|
||||
* <li>value为JSONTokener,直接解析</li>
|
||||
* <li>value为普通JavaBean,如果为普通的JavaBean,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象。例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* 如果给定值为Map,将键值对加入JSON对象;<br>
|
||||
* 如果为普通的JavaBean,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象<br>
|
||||
* 例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"
|
||||
*
|
||||
* @param source JavaBean或者Map对象或者String
|
||||
* @param config JSON配置文件,{@code null}则使用默认配置
|
||||
* @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public JSONObject(Object source, JSONConfig config, Filter<MutablePair<String, Object>> filter) {
|
||||
this(DEFAULT_CAPACITY, config);
|
||||
ObjectMapper.of(source).map(this, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建指定name列表对应的键值对为新的JSONObject,情况如下:
|
||||
*
|
||||
* <pre>
|
||||
* 1. 若obj为Map,则获取name列表对应键值对
|
||||
* 2. 若obj为普通Bean,使用反射方式获取字段名和字段值
|
||||
* </pre>
|
||||
* <p>
|
||||
* KEY或VALUE任意一个为null则不加入,字段不存在也不加入<br>
|
||||
* 若names列表为空,则字段全部加入
|
||||
*
|
||||
* @param source 包含需要字段的Bean对象或者Map对象
|
||||
* @param names 需要构建JSONObject的字段名列表
|
||||
*/
|
||||
public JSONObject(Object source, String... names) {
|
||||
this();
|
||||
if (ArrayUtil.isEmpty(names)) {
|
||||
ObjectMapper.of(source).map(this, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (source instanceof Map) {
|
||||
Object value;
|
||||
for (String name : names) {
|
||||
value = ((Map<?, ?>) source).get(name);
|
||||
this.set(name, value, null, getConfig().isCheckDuplicate());
|
||||
}
|
||||
} else {
|
||||
for (String name : names) {
|
||||
try {
|
||||
this.putOpt(name, ReflectUtil.getFieldValue(source, name));
|
||||
} catch (Exception ignore) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从JSON字符串解析为JSON对象,对于排序单独配置参数
|
||||
*
|
||||
* @param source 以大括号 {} 包围的字符串,其中KEY和VALUE使用 : 分隔,每个键值对使用逗号分隔
|
||||
* @param isOrder 是否有序
|
||||
* @throws JSONException JSON字符串语法错误
|
||||
* @since 4.2.2
|
||||
* @deprecated isOrder无效
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Deprecated
|
||||
public JSONObject(CharSequence source, boolean isOrder) throws JSONException {
|
||||
this(source, JSONConfig.create());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------- Constructor end
|
||||
|
||||
@Override
|
||||
public JSONConfig getConfig() {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置转为字符串时的日期格式,默认为时间戳(null值)<br>
|
||||
* 此方法设置的日期格式仅对转换为JSON字符串有效,对解析JSON为bean无效。
|
||||
*
|
||||
* @param format 格式,null表示使用时间戳
|
||||
* @return this
|
||||
* @since 4.1.19
|
||||
*/
|
||||
public JSONObject setDateFormat(String format) {
|
||||
this.config.setDateFormat(format);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定KEY列表的值组成新的JSONArray
|
||||
*
|
||||
* @param names KEY列表
|
||||
* @return A JSONArray of values.
|
||||
* @throws JSONException If any of the values are non-finite numbers.
|
||||
*/
|
||||
public JSONArray toJSONArray(Collection<String> names) throws JSONException {
|
||||
if (CollectionUtil.isEmpty(names)) {
|
||||
return null;
|
||||
}
|
||||
final JSONArray ja = new JSONArray(this.config);
|
||||
Object value;
|
||||
for (String name : names) {
|
||||
value = this.get(name);
|
||||
if (null != value) {
|
||||
ja.set(value);
|
||||
}
|
||||
}
|
||||
return ja;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObj(String key, Object defaultValue) {
|
||||
return this.getOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getByPath(String expression) {
|
||||
return BeanPath.create(expression).get(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getByPath(String expression, Class<T> resultType) {
|
||||
return JSONConverter.jsonConvert(resultType, getByPath(expression), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putByPath(String expression, Object value) {
|
||||
BeanPath.create(expression).set(this, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT 键值对到JSONObject中,在忽略null模式下,如果值为{@code null},将此键移除
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值对象. 可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL.
|
||||
* @return this.
|
||||
* @throws JSONException 值是无穷数字抛出此异常
|
||||
* @deprecated 此方法存在歧义,原Map接口返回的是之前的值,重写后返回this了,未来版本此方法会修改,请使用{@link #set(String, Object)}
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public JSONObject put(String key, Object value) throws JSONException {
|
||||
return set(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置键值对到JSONObject中,在忽略null模式下,如果值为{@code null},将此键移除
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值对象. 可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL.
|
||||
* @return this.
|
||||
* @throws JSONException 值是无穷数字抛出此异常
|
||||
*/
|
||||
public JSONObject set(String key, Object value) throws JSONException {
|
||||
return set(key, value, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置键值对到JSONObject中,在忽略null模式下,如果值为{@code null},将此键移除
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值对象. 可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL.
|
||||
* @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤
|
||||
* @param checkDuplicate 是否检查重复键,如果为{@code true},则出现重复键时抛出{@link JSONException}异常
|
||||
* @return this.
|
||||
* @throws JSONException 值是无穷数字抛出此异常
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public JSONObject set(String key, Object value, Filter<MutablePair<String, Object>> filter, boolean checkDuplicate) throws JSONException {
|
||||
if (null == key) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// 添加前置过滤,通过MutablePair实现过滤、修改键值对等
|
||||
if (null != filter) {
|
||||
final MutablePair<String, Object> pair = new MutablePair<>(key, value);
|
||||
if (filter.accept(pair)) {
|
||||
// 使用修改后的键值对
|
||||
key = pair.getKey();
|
||||
value = pair.getValue();
|
||||
} else {
|
||||
// 键值对被过滤
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
final boolean ignoreNullValue = this.config.isIgnoreNullValue();
|
||||
if (ObjectUtil.isNull(value) && ignoreNullValue) {
|
||||
// 忽略值模式下如果值为空清除key
|
||||
this.remove(key);
|
||||
} else {
|
||||
if (checkDuplicate && containsKey(key)) {
|
||||
throw new JSONException("Duplicate key \"{}\"", key);
|
||||
}
|
||||
|
||||
super.put(key, JSONUtil.wrap(InternalJSONUtil.testValidity(value), this.config));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 一次性Put 键值对,如果key已经存在抛出异常,如果键值中有null值,忽略
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值对象,可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL.
|
||||
* @return this.
|
||||
* @throws JSONException 值是无穷数字、键重复抛出异常
|
||||
*/
|
||||
public JSONObject putOnce(String key, Object value) throws JSONException {
|
||||
return setOnce(key, value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 一次性Put 键值对,如果key已经存在抛出异常,如果键值中有null值,忽略
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值对象,可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL.
|
||||
* @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤
|
||||
* @return this
|
||||
* @throws JSONException 值是无穷数字、键重复抛出异常
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public JSONObject setOnce(String key, Object value, Filter<MutablePair<String, Object>> filter) throws JSONException {
|
||||
return set(key, value, filter, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在键和值都为非空的情况下put到JSONObject中
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值对象,可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL.
|
||||
* @return this.
|
||||
* @throws JSONException 值是无穷数字
|
||||
*/
|
||||
public JSONObject putOpt(String key, Object value) throws JSONException {
|
||||
if (key != null && value != null) {
|
||||
this.set(key, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ?> m) {
|
||||
for (Entry<? extends String, ?> entry : m.entrySet()) {
|
||||
this.set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 积累值。类似于set,当key对应value已经存在时,与value组成新的JSONArray. <br>
|
||||
* 如果只有一个值,此值就是value,如果多个值,则是添加到新的JSONArray中
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 被积累的值
|
||||
* @return this.
|
||||
* @throws JSONException 如果给定键为{@code null}或者键对应的值存在且为非JSONArray
|
||||
*/
|
||||
public JSONObject accumulate(String key, Object value) throws JSONException {
|
||||
InternalJSONUtil.testValidity(value);
|
||||
Object object = this.getObj(key);
|
||||
if (object == null) {
|
||||
this.set(key, value);
|
||||
} else if (object instanceof JSONArray) {
|
||||
((JSONArray) object).set(value);
|
||||
} else {
|
||||
this.set(key, JSONUtil.createArray(this.config).set(object).set(value));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加值,如果key无对应值,就添加一个JSONArray,其元素只有value,如果值已经是一个JSONArray,则添加到值JSONArray中。
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return this.
|
||||
* @throws JSONException 如果给定键为{@code null}或者键对应的值存在且为非JSONArray
|
||||
*/
|
||||
public JSONObject append(String key, Object value) throws JSONException {
|
||||
InternalJSONUtil.testValidity(value);
|
||||
Object object = this.getObj(key);
|
||||
if (object == null) {
|
||||
this.set(key, new JSONArray(this.config).set(value));
|
||||
} else if (object instanceof JSONArray) {
|
||||
this.set(key, ((JSONArray) object).set(value));
|
||||
} else {
|
||||
throw new JSONException("JSONObject [" + key + "] is not a JSONArray.");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对值加一,如果值不存在,赋值1,如果为数字类型,做加一操作
|
||||
*
|
||||
* @param key A key string.
|
||||
* @return this.
|
||||
* @throws JSONException 如果存在值非Integer, Long, Double, 或 Float.
|
||||
*/
|
||||
public JSONObject increment(String key) throws JSONException {
|
||||
Object value = this.getObj(key);
|
||||
if (value == null) {
|
||||
this.set(key, 1);
|
||||
} else if (value instanceof BigInteger) {
|
||||
this.set(key, ((BigInteger) value).add(BigInteger.ONE));
|
||||
} else if (value instanceof BigDecimal) {
|
||||
this.set(key, ((BigDecimal) value).add(BigDecimal.ONE));
|
||||
} else if (value instanceof Integer) {
|
||||
this.set(key, (Integer) value + 1);
|
||||
} else if (value instanceof Long) {
|
||||
this.set(key, (Long) value + 1);
|
||||
} else if (value instanceof Double) {
|
||||
this.set(key, (Double) value + 1);
|
||||
} else if (value instanceof Float) {
|
||||
this.set(key, (Float) value + 1);
|
||||
} else {
|
||||
throw new JSONException("Unable to increment [" + JSONUtil.quote(key) + "].");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回JSON字符串<br>
|
||||
* 如果解析错误,返回{@code null}
|
||||
*
|
||||
* @return JSON字符串
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.toJSONString(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回JSON字符串<br>
|
||||
* 支持过滤器,即选择哪些字段或值不写出
|
||||
*
|
||||
* @param indentFactor 每层缩进空格数
|
||||
* @param filter 过滤器,同时可以修改编辑键和值
|
||||
* @return JSON字符串
|
||||
* @since 5.7.15
|
||||
*/
|
||||
public String toJSONString(int indentFactor, Filter<MutablePair<Object, Object>> filter) {
|
||||
final StringWriter sw = new StringWriter();
|
||||
synchronized (sw.getBuffer()) {
|
||||
return this.write(sw, indentFactor, 0, filter).toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Writer write(Writer writer, int indentFactor, int indent) throws JSONException {
|
||||
return write(writer, indentFactor, indent, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将JSON内容写入Writer<br>
|
||||
* 支持过滤器,即选择哪些字段或值不写出
|
||||
*
|
||||
* @param writer writer
|
||||
* @param indentFactor 缩进因子,定义每一级别增加的缩进量
|
||||
* @param indent 本级别缩进量
|
||||
* @param filter 过滤器,同时可以修改编辑键和值
|
||||
* @return Writer
|
||||
* @throws JSONException JSON相关异常
|
||||
* @since 5.7.15
|
||||
*/
|
||||
public Writer write(Writer writer, int indentFactor, int indent, Filter<MutablePair<Object, Object>> filter) throws JSONException {
|
||||
final JSONWriter jsonWriter = JSONWriter.of(writer, indentFactor, indent, config)
|
||||
.beginObj();
|
||||
this.forEach((key, value) -> jsonWriter.writeField(new MutablePair<>(key, value), filter));
|
||||
jsonWriter.end();
|
||||
// 此处不关闭Writer,考虑writer后续还需要填内容
|
||||
return writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject clone() throws CloneNotSupportedException {
|
||||
final JSONObject clone = (JSONObject) super.clone();
|
||||
clone.config = this.config;
|
||||
return clone;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* 此类用于在JSONAray中便于遍历JSONObject而封装的Iterable,可以借助foreach语法遍历
|
||||
*
|
||||
* @author looly
|
||||
* @since 4.0.12
|
||||
*/
|
||||
public class JSONObjectIter implements Iterable<JSONObject> {
|
||||
|
||||
Iterator<Object> iterator;
|
||||
|
||||
public JSONObjectIter(Iterator<Object> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<JSONObject> iterator() {
|
||||
return new Iterator<JSONObject>() {
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject next() {
|
||||
return (JSONObject) iterator.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
iterator.remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Filter;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.mutable.Mutable;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.mutable.MutablePair;
|
||||
|
||||
/**
|
||||
* JSON字符串解析器
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public class JSONParser {
|
||||
|
||||
/**
|
||||
* 创建JSONParser
|
||||
*
|
||||
* @param tokener {@link JSONTokener}
|
||||
* @return JSONParser
|
||||
*/
|
||||
public static JSONParser of(JSONTokener tokener) {
|
||||
return new JSONParser(tokener);
|
||||
}
|
||||
|
||||
private final JSONTokener tokener;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param tokener {@link JSONTokener}
|
||||
*/
|
||||
public JSONParser(JSONTokener tokener) {
|
||||
this.tokener = tokener;
|
||||
}
|
||||
|
||||
// region parseTo
|
||||
|
||||
/**
|
||||
* 解析{@link JSONTokener}中的字符到目标的{@link JSONObject}中
|
||||
*
|
||||
* @param jsonObject {@link JSONObject}
|
||||
* @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤
|
||||
*/
|
||||
public void parseTo(JSONObject jsonObject, Filter<MutablePair<String, Object>> filter) {
|
||||
final JSONTokener tokener = this.tokener;
|
||||
|
||||
if (tokener.nextClean() != '{') {
|
||||
throw tokener.syntaxError("A JSONObject text must begin with '{'");
|
||||
}
|
||||
|
||||
char prev;
|
||||
char c;
|
||||
String key;
|
||||
while (true) {
|
||||
prev = tokener.getPrevious();
|
||||
c = tokener.nextClean();
|
||||
switch (c) {
|
||||
case 0:
|
||||
throw tokener.syntaxError("A JSONObject text must end with '}'");
|
||||
case '}':
|
||||
return;
|
||||
case '{':
|
||||
case '[':
|
||||
if (prev == '{') {
|
||||
throw tokener.syntaxError("A JSONObject can not directly nest another JSONObject or JSONArray.");
|
||||
}
|
||||
default:
|
||||
tokener.back();
|
||||
key = tokener.nextValue().toString();
|
||||
}
|
||||
|
||||
// The key is followed by ':'.
|
||||
|
||||
c = tokener.nextClean();
|
||||
if (c != ':') {
|
||||
throw tokener.syntaxError("Expected a ':' after a key");
|
||||
}
|
||||
|
||||
jsonObject.set(key, tokener.nextValue(), filter, jsonObject.getConfig().isCheckDuplicate());
|
||||
|
||||
// Pairs are separated by ','.
|
||||
|
||||
switch (tokener.nextClean()) {
|
||||
case ';':
|
||||
case ',':
|
||||
if (tokener.nextClean() == '}') {
|
||||
// issue#2380
|
||||
// 尾后逗号(Trailing Commas),JSON中虽然不支持,但是ECMAScript 2017支持,此处做兼容。
|
||||
return;
|
||||
}
|
||||
tokener.back();
|
||||
break;
|
||||
case '}':
|
||||
return;
|
||||
default:
|
||||
throw tokener.syntaxError("Expected a ',' or '}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析JSON字符串到{@link JSONArray}中
|
||||
*
|
||||
* @param jsonArray {@link .JSONArray}
|
||||
* @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null} 表示不过滤
|
||||
*/
|
||||
public void parseTo(JSONArray jsonArray, Filter<Mutable<Object>> filter) {
|
||||
final JSONTokener x = this.tokener;
|
||||
|
||||
if (x.nextClean() != '[') {
|
||||
throw x.syntaxError("A JSONArray text must start with '['");
|
||||
}
|
||||
if (x.nextClean() != ']') {
|
||||
x.back();
|
||||
for (; ; ) {
|
||||
if (x.nextClean() == ',') {
|
||||
x.back();
|
||||
jsonArray.addRaw(JSONNull.NULL, filter);
|
||||
} else {
|
||||
x.back();
|
||||
jsonArray.addRaw(x.nextValue(), filter);
|
||||
}
|
||||
switch (x.nextClean()) {
|
||||
case ',':
|
||||
if (x.nextClean() == ']') {
|
||||
return;
|
||||
}
|
||||
x.back();
|
||||
break;
|
||||
case ']':
|
||||
return;
|
||||
default:
|
||||
throw x.syntaxError("Expected a ',' or ']'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.util.CharUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* JSON字符串格式化工具,用于简单格式化JSON字符串<br>
|
||||
* from http://blog.csdn.net/lovelong8808/article/details/54580278
|
||||
*
|
||||
* @author looly
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public class JSONStrFormatter {
|
||||
|
||||
/**
|
||||
* 单位缩进字符串。
|
||||
*/
|
||||
private static final String SPACE = " ";
|
||||
/**
|
||||
* 换行符
|
||||
*/
|
||||
private static final char NEW_LINE = StrUtil.C_LF;
|
||||
|
||||
/**
|
||||
* 返回格式化JSON字符串。
|
||||
*
|
||||
* @param json 未格式化的JSON字符串。
|
||||
* @return 格式化的JSON字符串。
|
||||
*/
|
||||
public static String format(String json) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
|
||||
Character wrapChar = null;
|
||||
boolean isEscapeMode = false;
|
||||
int length = json.length();
|
||||
int number = 0;
|
||||
char key;
|
||||
for (int i = 0; i < length; i++) {
|
||||
key = json.charAt(i);
|
||||
|
||||
if (CharUtil.DOUBLE_QUOTES == key || CharUtil.SINGLE_QUOTE == key) {
|
||||
if (null == wrapChar) {
|
||||
// 字符串模式开始
|
||||
wrapChar = key;
|
||||
} else if (isEscapeMode) {
|
||||
// 在字符串模式下的转义
|
||||
isEscapeMode = false;
|
||||
} else if (wrapChar.equals(key)) {
|
||||
// 字符串包装结束
|
||||
wrapChar = null;
|
||||
}
|
||||
|
||||
if ((i > 1) && (json.charAt(i - 1) == CharUtil.COLON)) {
|
||||
result.append(CharUtil.SPACE);
|
||||
}
|
||||
|
||||
result.append(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CharUtil.BACKSLASH == key) {
|
||||
if (null != wrapChar) {
|
||||
// 字符串模式下转义有效
|
||||
isEscapeMode = !isEscapeMode;
|
||||
result.append(key);
|
||||
continue;
|
||||
} else {
|
||||
result.append(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (null != wrapChar) {
|
||||
// 字符串模式
|
||||
result.append(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果当前字符是前方括号、前花括号做如下处理:
|
||||
if ((key == CharUtil.BRACKET_START) || (key == CharUtil.DELIM_START)) {
|
||||
// 如果前面还有字符,并且字符为“:”,打印:换行和缩进字符字符串。
|
||||
if ((i > 1) && (json.charAt(i - 1) == CharUtil.COLON)) {
|
||||
result.append(NEW_LINE);
|
||||
result.append(indent(number));
|
||||
}
|
||||
result.append(key);
|
||||
// 前方括号、前花括号,的后面必须换行。打印:换行。
|
||||
result.append(NEW_LINE);
|
||||
// 每出现一次前方括号、前花括号;缩进次数增加一次。打印:新行缩进。
|
||||
number++;
|
||||
result.append(indent(number));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// 3、如果当前字符是后方括号、后花括号做如下处理:
|
||||
if ((key == CharUtil.BRACKET_END) || (key == CharUtil.DELIM_END)) {
|
||||
// (1)后方括号、后花括号,的前面必须换行。打印:换行。
|
||||
result.append(NEW_LINE);
|
||||
// (2)每出现一次后方括号、后花括号;缩进次数减少一次。打印:缩进。
|
||||
number--;
|
||||
result.append(indent(number));
|
||||
// (3)打印:当前字符。
|
||||
result.append(key);
|
||||
// (4)如果当前字符后面还有字符,并且字符不为“,”,打印:换行。
|
||||
// if (((i + 1) < length) && (json.charAt(i + 1) != ',')) {
|
||||
// result.append(NEW_LINE);
|
||||
// }
|
||||
// (5)继续下一次循环。
|
||||
continue;
|
||||
}
|
||||
|
||||
// 4、如果当前字符是逗号。逗号后面换行,并缩进,不改变缩进次数。
|
||||
if ((key == ',')) {
|
||||
result.append(key);
|
||||
result.append(NEW_LINE);
|
||||
result.append(indent(number));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((i > 1) && (json.charAt(i - 1) == CharUtil.COLON)) {
|
||||
result.append(CharUtil.SPACE);
|
||||
}
|
||||
|
||||
// 5、打印:当前字符。
|
||||
result.append(key);
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回指定次数的缩进字符串。每一次缩进4个空格,即SPACE。
|
||||
*
|
||||
* @param number 缩进次数。
|
||||
* @return 指定缩进次数的字符串。
|
||||
*/
|
||||
private static String indent(int number) {
|
||||
return StrUtil.repeat(SPACE, number);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
/**
|
||||
* {@code JSONString}接口定义了一个{@code toJSONString()}<br>
|
||||
* 实现此接口的类可以通过实现{@code toJSONString()}方法来改变转JSON字符串的方式。
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public interface JSONString {
|
||||
|
||||
/**
|
||||
* 自定义转JSON字符串的方法
|
||||
*
|
||||
* @return JSON字符串
|
||||
*/
|
||||
String toJSONString();
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.bean.BeanUtil;
|
||||
|
||||
/**
|
||||
* JSON支持<br>
|
||||
* 继承此类实现实体类与JSON的相互转换
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class JSONSupport implements JSONString, JSONBeanParser<JSON> {
|
||||
|
||||
/**
|
||||
* JSON String转Bean
|
||||
*
|
||||
* @param jsonString JSON String
|
||||
*/
|
||||
public void parse(String jsonString) {
|
||||
parse(new JSONObject(jsonString));
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON转Bean
|
||||
*
|
||||
* @param json JSON
|
||||
*/
|
||||
@Override
|
||||
public void parse(JSON json) {
|
||||
final JSONSupport support = JSONConverter.jsonToBean(getClass(), json, false);
|
||||
BeanUtil.copyProperties(support, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JSON对象
|
||||
*/
|
||||
public JSONObject toJSON() {
|
||||
return new JSONObject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toJSONString() {
|
||||
return toJSON().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 美化的JSON(使用回车缩进显示JSON),用于打印输出debug
|
||||
*
|
||||
* @return 美化的JSON
|
||||
*/
|
||||
public String toPrettyString() {
|
||||
return toJSON().toStringPretty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toJSONString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,458 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.io.IoUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* JSON解析器,用于将JSON字符串解析为JSONObject或者JSONArray
|
||||
*
|
||||
* @author from JSON.org
|
||||
*/
|
||||
public class JSONTokener {
|
||||
|
||||
private long character;
|
||||
/**
|
||||
* 是否结尾 End of stream
|
||||
*/
|
||||
private boolean eof;
|
||||
/**
|
||||
* 在Reader的位置(解析到第几个字符)
|
||||
*/
|
||||
private long index;
|
||||
/**
|
||||
* 当前所在行
|
||||
*/
|
||||
private long line;
|
||||
/**
|
||||
* 前一个字符
|
||||
*/
|
||||
private char previous;
|
||||
/**
|
||||
* 是否使用前一个字符
|
||||
*/
|
||||
private boolean usePrevious;
|
||||
/**
|
||||
* 源
|
||||
*/
|
||||
private final Reader reader;
|
||||
|
||||
/**
|
||||
* JSON配置
|
||||
*/
|
||||
private final JSONConfig config;
|
||||
|
||||
// ------------------------------------------------------------------------------------ Constructor start
|
||||
|
||||
/**
|
||||
* 从Reader中构建
|
||||
*
|
||||
* @param reader Reader
|
||||
* @param config JSON配置
|
||||
*/
|
||||
public JSONTokener(Reader reader, JSONConfig config) {
|
||||
this.reader = reader.markSupported() ? reader : new BufferedReader(reader);
|
||||
this.eof = false;
|
||||
this.usePrevious = false;
|
||||
this.previous = 0;
|
||||
this.index = 0;
|
||||
this.character = 1;
|
||||
this.line = 1;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从InputStream中构建,使用UTF-8编码
|
||||
*
|
||||
* @param inputStream InputStream
|
||||
* @param config JSON配置
|
||||
*/
|
||||
public JSONTokener(InputStream inputStream, JSONConfig config) throws JSONException {
|
||||
this(IoUtil.getUtf8Reader(inputStream), config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字符串中构建
|
||||
*
|
||||
* @param s JSON字符串
|
||||
* @param config JSON配置
|
||||
*/
|
||||
public JSONTokener(CharSequence s, JSONConfig config) {
|
||||
this(new StringReader(StrUtil.str(s)), config);
|
||||
}
|
||||
// ------------------------------------------------------------------------------------ Constructor end
|
||||
|
||||
/**
|
||||
* 将标记回退到第一个字符,重新开始解析新的JSON
|
||||
*/
|
||||
public void back() throws JSONException {
|
||||
if (this.usePrevious || this.index <= 0) {
|
||||
throw new JSONException("Stepping back two steps is not supported");
|
||||
}
|
||||
this.index -= 1;
|
||||
this.character -= 1;
|
||||
this.usePrevious = true;
|
||||
this.eof = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否进入结尾
|
||||
*/
|
||||
public boolean end() {
|
||||
return this.eof && !this.usePrevious;
|
||||
}
|
||||
|
||||
/**
|
||||
* 源字符串是否有更多的字符
|
||||
*
|
||||
* @return 如果未达到结尾返回true,否则false
|
||||
*/
|
||||
public boolean more() throws JSONException {
|
||||
this.next();
|
||||
if (this.end()) {
|
||||
return false;
|
||||
}
|
||||
this.back();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得源字符串中的下一个字符
|
||||
*
|
||||
* @return 下一个字符, or 0 if past the end of the source string.
|
||||
* @throws JSONException JSON异常,包装IO异常
|
||||
*/
|
||||
public char next() throws JSONException {
|
||||
int c;
|
||||
if (this.usePrevious) {
|
||||
this.usePrevious = false;
|
||||
c = this.previous;
|
||||
} else {
|
||||
try {
|
||||
c = this.reader.read();
|
||||
} catch (IOException exception) {
|
||||
throw new JSONException(exception);
|
||||
}
|
||||
|
||||
if (c <= 0) { // End of stream
|
||||
this.eof = true;
|
||||
c = 0;
|
||||
}
|
||||
}
|
||||
this.index += 1;
|
||||
if (this.previous == '\r') {
|
||||
this.line += 1;
|
||||
this.character = c == '\n' ? 0 : 1;
|
||||
} else if (c == '\n') {
|
||||
this.line += 1;
|
||||
this.character = 0;
|
||||
} else {
|
||||
this.character += 1;
|
||||
}
|
||||
this.previous = (char) c;
|
||||
return this.previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last character read from the input or '\0' if nothing has been read yet.
|
||||
*
|
||||
* @return the last character read from the input.
|
||||
*/
|
||||
protected char getPrevious() {
|
||||
return this.previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取下一个字符,并比对是否和指定字符匹配
|
||||
*
|
||||
* @param c 被匹配的字符
|
||||
* @return The character 匹配到的字符
|
||||
* @throws JSONException 如果不匹配抛出此异常
|
||||
*/
|
||||
public char next(char c) throws JSONException {
|
||||
char n = this.next();
|
||||
if (n != c) {
|
||||
throw this.syntaxError("Expected '" + c + "' and instead saw '" + n + "'");
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得接下来的n个字符
|
||||
*
|
||||
* @param n 字符数
|
||||
* @return 获得的n个字符组成的字符串
|
||||
* @throws JSONException 如果源中余下的字符数不足以提供所需的字符数,抛出此异常
|
||||
*/
|
||||
public String next(int n) throws JSONException {
|
||||
if (n == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
char[] chars = new char[n];
|
||||
int pos = 0;
|
||||
while (pos < n) {
|
||||
chars[pos] = this.next();
|
||||
if (this.end()) {
|
||||
throw this.syntaxError("Substring bounds error");
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得下一个字符,跳过空白符
|
||||
*
|
||||
* @return 获得的字符,0表示没有更多的字符
|
||||
* @throws JSONException 获得下一个字符时抛出的异常
|
||||
*/
|
||||
public char nextClean() throws JSONException {
|
||||
char c;
|
||||
while (true) {
|
||||
c = this.next();
|
||||
if (c == 0 || c > ' ') {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前位置到指定引号前的所有字符,反斜杠的转义符也会被处理。<br>
|
||||
* 标准的JSON是不允许使用单引号包含字符串的,但是此实现允许。
|
||||
*
|
||||
* @param quote 字符引号, 包括 {@code "}(双引号) 或 {@code '}(单引号)。
|
||||
* @return 截止到引号前的字符串
|
||||
* @throws JSONException 出现无结束的字符串时抛出此异常
|
||||
*/
|
||||
public String nextString(char quote) throws JSONException {
|
||||
char c;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (true) {
|
||||
c = this.next();
|
||||
switch (c) {
|
||||
case 0:
|
||||
case '\n':
|
||||
case '\r':
|
||||
throw this.syntaxError("Unterminated string");
|
||||
case '\\':// 转义符
|
||||
c = this.next();
|
||||
switch (c) {
|
||||
case 'b':
|
||||
sb.append('\b');
|
||||
break;
|
||||
case 't':
|
||||
sb.append('\t');
|
||||
break;
|
||||
case 'n':
|
||||
sb.append('\n');
|
||||
break;
|
||||
case 'f':
|
||||
sb.append('\f');
|
||||
break;
|
||||
case 'r':
|
||||
sb.append('\r');
|
||||
break;
|
||||
case 'u':// Unicode符
|
||||
sb.append((char) Integer.parseInt(this.next(4), 16));
|
||||
break;
|
||||
case '"':
|
||||
case '\'':
|
||||
case '\\':
|
||||
case '/':
|
||||
sb.append(c);
|
||||
break;
|
||||
default:
|
||||
throw this.syntaxError("Illegal escape.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (c == quote) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text up but not including the specified character or the end of line, whichever comes first. <br>
|
||||
* 获得从当前位置直到分隔符(不包括分隔符)或行尾的的所有字符。
|
||||
*
|
||||
* @param delimiter 分隔符
|
||||
* @return 字符串
|
||||
*/
|
||||
public String nextTo(char delimiter) throws JSONException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (; ; ) {
|
||||
char c = this.next();
|
||||
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text up but not including one of the specified delimiter characters or the end of line, whichever comes first.
|
||||
*
|
||||
* @param delimiters A set of delimiter characters.
|
||||
* @return A string, trimmed.
|
||||
*/
|
||||
public String nextTo(String delimiters) throws JSONException {
|
||||
char c;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (; ; ) {
|
||||
c = this.next();
|
||||
if (delimiters.indexOf(c) >= 0 || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得下一个值,值类型可以是Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the JSONObject.NULL
|
||||
*
|
||||
* @return Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the JSONObject.NULL
|
||||
* @throws JSONException 语法错误
|
||||
*/
|
||||
public Object nextValue() throws JSONException {
|
||||
char c = this.nextClean();
|
||||
String string;
|
||||
|
||||
switch (c) {
|
||||
case '"':
|
||||
case '\'':
|
||||
return this.nextString(c);
|
||||
case '{':
|
||||
this.back();
|
||||
try {
|
||||
return new JSONObject(this, this.config);
|
||||
} catch (final StackOverflowError e) {
|
||||
throw new JSONException("JSONObject depth too large to process.", e);
|
||||
}
|
||||
case '[':
|
||||
this.back();
|
||||
try {
|
||||
return new JSONArray(this, this.config);
|
||||
} catch (final StackOverflowError e) {
|
||||
throw new JSONException("JSONArray depth too large to process.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle unquoted text. This could be the values true, false, or null, or it can be a number.
|
||||
* An implementation (such as this one) is allowed to also accept non-standard forms. Accumulate
|
||||
* characters until we reach the end of the text or a formatting character.
|
||||
*/
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
|
||||
sb.append(c);
|
||||
c = this.next();
|
||||
}
|
||||
this.back();
|
||||
|
||||
string = sb.toString().trim();
|
||||
if (0 == string.length()) {
|
||||
throw this.syntaxError("Missing value");
|
||||
}
|
||||
return InternalJSONUtil.stringToValue(string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip characters until the next character is the requested character. If the requested character is not found, no characters are skipped. 在遇到指定字符前,跳过其它字符。如果字符未找到,则不跳过任何字符。
|
||||
*
|
||||
* @param to 需要定位的字符
|
||||
* @return 定位的字符,如果字符未找到返回0
|
||||
*/
|
||||
public char skipTo(char to) throws JSONException {
|
||||
char c;
|
||||
try {
|
||||
long startIndex = this.index;
|
||||
long startCharacter = this.character;
|
||||
long startLine = this.line;
|
||||
this.reader.mark(1000000);
|
||||
do {
|
||||
c = this.next();
|
||||
if (c == 0) {
|
||||
this.reader.reset();
|
||||
this.index = startIndex;
|
||||
this.character = startCharacter;
|
||||
this.line = startLine;
|
||||
return c;
|
||||
}
|
||||
} while (c != to);
|
||||
} catch (IOException exception) {
|
||||
throw new JSONException(exception);
|
||||
}
|
||||
this.back();
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a JSONException to signal a syntax error. <br>
|
||||
* 构建 JSONException 用于表示语法错误
|
||||
*
|
||||
* @param message 错误消息
|
||||
* @return A JSONException object, suitable for throwing
|
||||
*/
|
||||
public JSONException syntaxError(String message) {
|
||||
return new JSONException(message + this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为 {@link JSONArray}
|
||||
*
|
||||
* @return {@link JSONArray}
|
||||
*/
|
||||
public JSONArray toJSONArray() {
|
||||
JSONArray jsonArray = new JSONArray(this.config);
|
||||
if (this.nextClean() != '[') {
|
||||
throw this.syntaxError("A JSONArray text must start with '['");
|
||||
}
|
||||
if (this.nextClean() != ']') {
|
||||
this.back();
|
||||
while (true) {
|
||||
if (this.nextClean() == ',') {
|
||||
this.back();
|
||||
jsonArray.add(JSONNull.NULL);
|
||||
} else {
|
||||
this.back();
|
||||
jsonArray.add(this.nextValue());
|
||||
}
|
||||
switch (this.nextClean()) {
|
||||
case ',':
|
||||
if (this.nextClean() == ']') {
|
||||
return jsonArray;
|
||||
}
|
||||
this.back();
|
||||
break;
|
||||
case ']':
|
||||
return jsonArray;
|
||||
default:
|
||||
throw this.syntaxError("Expected a ',' or ']'");
|
||||
}
|
||||
}
|
||||
}
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a printable string of this JSONTokener.
|
||||
*
|
||||
* @return " at {index} [character {character} line {line}]"
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return " at " + this.index + " [character " + this.character + " line " + this.line + "]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,983 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.io.IORuntimeException;
|
||||
import aiyh.utils.tool.cn.hutool.core.io.file.FileReader;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.TypeReference;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.MapWrapper;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.*;
|
||||
import aiyh.utils.tool.cn.hutool.json.serialize.GlobalSerializeMapping;
|
||||
import aiyh.utils.tool.cn.hutool.json.serialize.JSONArraySerializer;
|
||||
import aiyh.utils.tool.cn.hutool.json.serialize.JSONDeserializer;
|
||||
import aiyh.utils.tool.cn.hutool.json.serialize.JSONObjectSerializer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.Charset;
|
||||
import java.sql.SQLException;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* JSON工具类
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class JSONUtil {
|
||||
|
||||
// -------------------------------------------------------------------- Pause start
|
||||
|
||||
/**
|
||||
* 创建JSONObject
|
||||
*
|
||||
* @return JSONObject
|
||||
*/
|
||||
public static JSONObject createObj() {
|
||||
return new JSONObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建JSONObject
|
||||
*
|
||||
* @param config JSON配置
|
||||
* @return JSONObject
|
||||
* @since 5.2.5
|
||||
*/
|
||||
public static JSONObject createObj(JSONConfig config) {
|
||||
return new JSONObject(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 JSONArray
|
||||
*
|
||||
* @return JSONArray
|
||||
*/
|
||||
public static JSONArray createArray() {
|
||||
return new JSONArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 JSONArray
|
||||
*
|
||||
* @param config JSON配置
|
||||
* @return JSONArray
|
||||
* @since 5.2.5
|
||||
*/
|
||||
public static JSONArray createArray(JSONConfig config) {
|
||||
return new JSONArray(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字符串转JSONObject对象
|
||||
*
|
||||
* @param jsonStr JSON字符串
|
||||
* @return JSONObject
|
||||
*/
|
||||
public static JSONObject parseObj(String jsonStr) {
|
||||
return new JSONObject(jsonStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字符串转JSONObject对象<br>
|
||||
* 此方法会忽略空值,但是对JSON字符串不影响
|
||||
*
|
||||
* @param obj Bean对象或者Map
|
||||
* @return JSONObject
|
||||
*/
|
||||
public static JSONObject parseObj(Object obj) {
|
||||
return parseObj(obj, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字符串转JSONObject对象<br>
|
||||
* 此方法会忽略空值,但是对JSON字符串不影响
|
||||
*
|
||||
* @param obj Bean对象或者Map
|
||||
* @param config JSON配置
|
||||
* @return JSONObject
|
||||
* @since 5.3.1
|
||||
*/
|
||||
public static JSONObject parseObj(Object obj, JSONConfig config) {
|
||||
return new JSONObject(obj, ObjectUtil.defaultIfNull(config, JSONConfig::create));
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字符串转JSONObject对象
|
||||
*
|
||||
* @param obj Bean对象或者Map
|
||||
* @param ignoreNullValue 是否忽略空值,如果source为JSON字符串,不忽略空值
|
||||
* @return JSONObject
|
||||
* @since 3.0.9
|
||||
*/
|
||||
public static JSONObject parseObj(Object obj, boolean ignoreNullValue) {
|
||||
return new JSONObject(obj, ignoreNullValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字符串转JSONObject对象
|
||||
*
|
||||
* @param obj Bean对象或者Map
|
||||
* @param ignoreNullValue 是否忽略空值,如果source为JSON字符串,不忽略空值
|
||||
* @param isOrder 是否有序
|
||||
* @return JSONObject
|
||||
* @since 4.2.2
|
||||
* @deprecated isOrder参数不再有效
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Deprecated
|
||||
public static JSONObject parseObj(Object obj, boolean ignoreNullValue, boolean isOrder) {
|
||||
return new JSONObject(obj, ignoreNullValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字符串转JSONArray
|
||||
*
|
||||
* @param jsonStr JSON字符串
|
||||
* @return JSONArray
|
||||
*/
|
||||
public static JSONArray parseArray(String jsonStr) {
|
||||
return new JSONArray(jsonStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字符串转JSONArray
|
||||
*
|
||||
* @param arrayOrCollection 数组或集合对象
|
||||
* @return JSONArray
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static JSONArray parseArray(Object arrayOrCollection) {
|
||||
return parseArray(arrayOrCollection, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字符串转JSONArray
|
||||
*
|
||||
* @param arrayOrCollection 数组或集合对象
|
||||
* @param config JSON配置
|
||||
* @return JSONArray
|
||||
* @since 5.3.1
|
||||
*/
|
||||
public static JSONArray parseArray(Object arrayOrCollection, JSONConfig config) {
|
||||
return new JSONArray(arrayOrCollection, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字符串转JSONArray
|
||||
*
|
||||
* @param arrayOrCollection 数组或集合对象
|
||||
* @param ignoreNullValue 是否忽略空值
|
||||
* @return JSONArray
|
||||
* @since 3.2.3
|
||||
*/
|
||||
public static JSONArray parseArray(Object arrayOrCollection, boolean ignoreNullValue) {
|
||||
return new JSONArray(arrayOrCollection, ignoreNullValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换对象为JSON,如果用户不配置JSONConfig,则JSON的有序与否与传入对象有关。<br>
|
||||
* 支持的对象:
|
||||
* <ul>
|
||||
* <li>String: 转换为相应的对象</li>
|
||||
* <li>Array、Iterable、Iterator:转换为JSONArray</li>
|
||||
* <li>Bean对象:转为JSONObject</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param obj 对象
|
||||
* @return JSON
|
||||
*/
|
||||
public static JSON parse(Object obj) {
|
||||
return parse(obj, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换对象为JSON,如果用户不配置JSONConfig,则JSON的有序与否与传入对象有关。<br>
|
||||
* 支持的对象:
|
||||
* <ul>
|
||||
* <li>String: 转换为相应的对象</li>
|
||||
* <li>Array、Iterable、Iterator:转换为JSONArray</li>
|
||||
* <li>Bean对象:转为JSONObject</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param obj 对象
|
||||
* @param config JSON配置,{@code null}使用默认配置
|
||||
* @return JSON
|
||||
* @since 5.3.1
|
||||
*/
|
||||
public static JSON parse(Object obj, JSONConfig config) {
|
||||
if (null == obj) {
|
||||
return null;
|
||||
}
|
||||
JSON json;
|
||||
if (obj instanceof JSON) {
|
||||
json = (JSON) obj;
|
||||
} else if (obj instanceof CharSequence) {
|
||||
final String jsonStr = StrUtil.trim((CharSequence) obj);
|
||||
json = isTypeJSONArray(jsonStr) ? parseArray(jsonStr, config) : parseObj(jsonStr, config);
|
||||
} else if (obj instanceof MapWrapper) {
|
||||
// MapWrapper实现了Iterable会被当作JSONArray,此处做修正
|
||||
json = parseObj(obj, config);
|
||||
} else if (obj instanceof Iterable || obj instanceof Iterator || ArrayUtil.isArray(obj)) {// 列表
|
||||
json = parseArray(obj, config);
|
||||
} else {// 对象
|
||||
json = parseObj(obj, config);
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* XML字符串转为JSONObject
|
||||
*
|
||||
* @param xmlStr XML字符串
|
||||
* @return JSONObject
|
||||
*/
|
||||
public static JSONObject parseFromXml(String xmlStr) {
|
||||
return XML.toJSONObject(xmlStr);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------- Parse end
|
||||
|
||||
// -------------------------------------------------------------------- Read start
|
||||
|
||||
/**
|
||||
* 读取JSON
|
||||
*
|
||||
* @param file JSON文件
|
||||
* @param charset 编码
|
||||
* @return JSON(包括JSONObject和JSONArray)
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static JSON readJSON(File file, Charset charset) throws IORuntimeException {
|
||||
return parse(FileReader.create(file, charset).readString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取JSONObject
|
||||
*
|
||||
* @param file JSON文件
|
||||
* @param charset 编码
|
||||
* @return JSONObject
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static JSONObject readJSONObject(File file, Charset charset) throws IORuntimeException {
|
||||
return parseObj(FileReader.create(file, charset).readString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取JSONArray
|
||||
*
|
||||
* @param file JSON文件
|
||||
* @param charset 编码
|
||||
* @return JSONArray
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static JSONArray readJSONArray(File file, Charset charset) throws IORuntimeException {
|
||||
return parseArray(FileReader.create(file, charset).readString());
|
||||
}
|
||||
// -------------------------------------------------------------------- Read end
|
||||
|
||||
// -------------------------------------------------------------------- toString start
|
||||
|
||||
/**
|
||||
* 转为JSON字符串
|
||||
*
|
||||
* @param json JSON
|
||||
* @param indentFactor 每一级别的缩进
|
||||
* @return JSON字符串
|
||||
*/
|
||||
public static String toJsonStr(JSON json, int indentFactor) {
|
||||
if (null == json) {
|
||||
return null;
|
||||
}
|
||||
return json.toJSONString(indentFactor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为JSON字符串
|
||||
*
|
||||
* @param json JSON
|
||||
* @return JSON字符串
|
||||
*/
|
||||
public static String toJsonStr(JSON json) {
|
||||
if (null == json) {
|
||||
return null;
|
||||
}
|
||||
return json.toJSONString(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为JSON字符串,并写出到write
|
||||
*
|
||||
* @param json JSON
|
||||
* @param writer Writer
|
||||
* @since 5.3.3
|
||||
*/
|
||||
public static void toJsonStr(JSON json, Writer writer) {
|
||||
if (null != json) {
|
||||
json.write(writer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为JSON字符串
|
||||
*
|
||||
* @param json JSON
|
||||
* @return JSON字符串
|
||||
*/
|
||||
public static String toJsonPrettyStr(JSON json) {
|
||||
if (null == json) {
|
||||
return null;
|
||||
}
|
||||
return json.toJSONString(4);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为JSON字符串
|
||||
*
|
||||
* @param obj 被转为JSON的对象
|
||||
* @return JSON字符串
|
||||
*/
|
||||
public static String toJsonStr(Object obj) {
|
||||
return toJsonStr(obj, (JSONConfig) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为JSON字符串
|
||||
*
|
||||
* @param obj 被转为JSON的对象
|
||||
* @param jsonConfig JSON配置
|
||||
* @return JSON字符串
|
||||
* @since 5.7.12
|
||||
*/
|
||||
public static String toJsonStr(Object obj, JSONConfig jsonConfig) {
|
||||
if (null == obj) {
|
||||
return null;
|
||||
}
|
||||
if (obj instanceof CharSequence) {
|
||||
return StrUtil.str((CharSequence) obj);
|
||||
}
|
||||
return toJsonStr(parse(obj, jsonConfig));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为JSON字符串并写出到writer
|
||||
*
|
||||
* @param obj 被转为JSON的对象
|
||||
* @param writer Writer
|
||||
* @since 5.3.3
|
||||
*/
|
||||
public static void toJsonStr(Object obj, Writer writer) {
|
||||
if (null != obj) {
|
||||
toJsonStr(parse(obj), writer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为格式化后的JSON字符串
|
||||
*
|
||||
* @param obj Bean对象
|
||||
* @return JSON字符串
|
||||
*/
|
||||
public static String toJsonPrettyStr(Object obj) {
|
||||
return toJsonPrettyStr(parse(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为XML字符串
|
||||
*
|
||||
* @param json JSON
|
||||
* @return XML字符串
|
||||
*/
|
||||
public static String toXmlStr(JSON json) {
|
||||
return XML.toXml(json);
|
||||
}
|
||||
// -------------------------------------------------------------------- toString end
|
||||
|
||||
// -------------------------------------------------------------------- toBean start
|
||||
|
||||
/**
|
||||
* JSON字符串转为实体类对象,转换异常将被抛出
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param jsonString JSON字符串
|
||||
* @param beanClass 实体类对象
|
||||
* @return 实体类对象
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public static <T> T toBean(String jsonString, Class<T> beanClass) {
|
||||
return toBean(parseObj(jsonString), beanClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字符串转为实体类对象,转换异常将被抛出<br>
|
||||
* 通过{@link JSONConfig}可选是否忽略大小写、忽略null等配置
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param jsonString JSON字符串
|
||||
* @param config JSON配置
|
||||
* @param beanClass 实体类对象
|
||||
* @return 实体类对象
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public static <T> T toBean(String jsonString, JSONConfig config, Class<T> beanClass) {
|
||||
return toBean(parseObj(jsonString, config), beanClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为实体类对象,转换异常将被抛出
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param json JSONObject
|
||||
* @param beanClass 实体类对象
|
||||
* @return 实体类对象
|
||||
*/
|
||||
public static <T> T toBean(JSONObject json, Class<T> beanClass) {
|
||||
return null == json ? null : json.toBean(beanClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字符串转为实体类对象,转换异常将被抛出
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param jsonString JSON字符串
|
||||
* @param typeReference {@link TypeReference}类型参考子类,可以获取其泛型参数中的Type类型
|
||||
* @param ignoreError 是否忽略错误
|
||||
* @return 实体类对象
|
||||
* @since 4.3.2
|
||||
*/
|
||||
public static <T> T toBean(String jsonString, TypeReference<T> typeReference, boolean ignoreError) {
|
||||
return toBean(jsonString, typeReference.getType(), ignoreError);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字符串转为实体类对象,转换异常将被抛出
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param jsonString JSON字符串
|
||||
* @param beanType 实体类对象类型
|
||||
* @param ignoreError 是否忽略错误
|
||||
* @return 实体类对象
|
||||
* @since 4.3.2
|
||||
*/
|
||||
public static <T> T toBean(String jsonString, Type beanType, boolean ignoreError) {
|
||||
return parse(jsonString, JSONConfig.create().setIgnoreError(ignoreError)).toBean(beanType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为实体类对象
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param json JSONObject
|
||||
* @param typeReference {@link TypeReference}类型参考子类,可以获取其泛型参数中的Type类型
|
||||
* @param ignoreError 是否忽略转换错误
|
||||
* @return 实体类对象
|
||||
* @since 4.6.2
|
||||
*/
|
||||
public static <T> T toBean(JSON json, TypeReference<T> typeReference, boolean ignoreError) {
|
||||
return toBean(json, typeReference.getType(), ignoreError);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为实体类对象
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param json JSONObject
|
||||
* @param beanType 实体类对象类型
|
||||
* @param ignoreError 是否忽略转换错误
|
||||
* @return 实体类对象
|
||||
* @since 4.3.2
|
||||
*/
|
||||
public static <T> T toBean(JSON json, Type beanType, boolean ignoreError) {
|
||||
if (null == json) {
|
||||
return null;
|
||||
}
|
||||
return json.toBean(beanType, ignoreError);
|
||||
}
|
||||
// -------------------------------------------------------------------- toBean end
|
||||
|
||||
/**
|
||||
* 将JSONArray字符串转换为Bean的List,默认为ArrayList
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param jsonArray JSONArray字符串
|
||||
* @param elementType List中元素类型
|
||||
* @return List
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public static <T> List<T> toList(String jsonArray, Class<T> elementType) {
|
||||
return toList(parseArray(jsonArray), elementType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将JSONArray转换为Bean的List,默认为ArrayList
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param jsonArray {@link JSONArray}
|
||||
* @param elementType List中元素类型
|
||||
* @return List
|
||||
* @since 4.0.7
|
||||
*/
|
||||
public static <T> List<T> toList(JSONArray jsonArray, Class<T> elementType) {
|
||||
return null == jsonArray ? null : jsonArray.toList(elementType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过表达式获取JSON中嵌套的对象<br>
|
||||
* <ol>
|
||||
* <li>.表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值</li>
|
||||
* <li>[]表达式,可以获取集合等对象中对应index的值</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* 表达式栗子:
|
||||
*
|
||||
* <pre>
|
||||
* persion
|
||||
* persion.name
|
||||
* persons[3]
|
||||
* person.friends[5].name
|
||||
* </pre>
|
||||
*
|
||||
* @param json {@link JSON}
|
||||
* @param expression 表达式
|
||||
* @return 对象
|
||||
* @see JSON#getByPath(String)
|
||||
*/
|
||||
public static Object getByPath(JSON json, String expression) {
|
||||
return getByPath(json, expression, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过表达式获取JSON中嵌套的对象<br>
|
||||
* <ol>
|
||||
* <li>.表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值</li>
|
||||
* <li>[]表达式,可以获取集合等对象中对应index的值</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* 表达式栗子:
|
||||
*
|
||||
* <pre>
|
||||
* persion
|
||||
* persion.name
|
||||
* persons[3]
|
||||
* person.friends[5].name
|
||||
* </pre>
|
||||
*
|
||||
* @param <T> 值类型
|
||||
* @param json {@link JSON}
|
||||
* @param expression 表达式
|
||||
* @param defaultValue 默认值
|
||||
* @return 对象
|
||||
* @see JSON#getByPath(String)
|
||||
* @since 5.6.0
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getByPath(JSON json, String expression, T defaultValue) {
|
||||
if ((null == json || StrUtil.isBlank(expression))) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (null != defaultValue) {
|
||||
final Class<T> type = (Class<T>) defaultValue.getClass();
|
||||
return ObjectUtil.defaultIfNull(json.getByPath(expression, type), defaultValue);
|
||||
}
|
||||
return (T) json.getByPath(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置表达式指定位置(或filed对应)的值<br>
|
||||
* 若表达式指向一个JSONArray则设置其坐标对应位置的值,若指向JSONObject则put对应key的值<br>
|
||||
* 注意:如果为JSONArray,则设置值得下标不能大于已有JSONArray的长度<br>
|
||||
* <ol>
|
||||
* <li>.表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值</li>
|
||||
* <li>[]表达式,可以获取集合等对象中对应index的值</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* 表达式栗子:
|
||||
*
|
||||
* <pre>
|
||||
* persion
|
||||
* persion.name
|
||||
* persons[3]
|
||||
* person.friends[5].name
|
||||
* </pre>
|
||||
*
|
||||
* @param json JSON,可以为JSONObject或JSONArray
|
||||
* @param expression 表达式
|
||||
* @param value 值
|
||||
*/
|
||||
public static void putByPath(JSON json, String expression, Object value) {
|
||||
json.putByPath(expression, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对所有双引号做转义处理(使用双反斜杠做转义)<br>
|
||||
* 为了能在HTML中较好的显示,会将</转义为<\/<br>
|
||||
* JSON字符串中不能包含控制字符和未经转义的引号和反斜杠
|
||||
*
|
||||
* @param string 字符串
|
||||
* @return 适合在JSON中显示的字符串
|
||||
*/
|
||||
public static String quote(String string) {
|
||||
return quote(string, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对所有双引号做转义处理(使用双反斜杠做转义)<br>
|
||||
* 为了能在HTML中较好的显示,会将</转义为<\/<br>
|
||||
* JSON字符串中不能包含控制字符和未经转义的引号和反斜杠
|
||||
*
|
||||
* @param string 字符串
|
||||
* @param isWrap 是否使用双引号包装字符串
|
||||
* @return 适合在JSON中显示的字符串
|
||||
* @since 3.3.1
|
||||
*/
|
||||
public static String quote(String string, boolean isWrap) {
|
||||
StringWriter sw = new StringWriter();
|
||||
try {
|
||||
return quote(string, sw, isWrap).toString();
|
||||
} catch (IOException ignored) {
|
||||
// will never happen - we are writing to a string writer
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对所有双引号做转义处理(使用双反斜杠做转义)<br>
|
||||
* 为了能在HTML中较好的显示,会将</转义为<\/<br>
|
||||
* JSON字符串中不能包含控制字符和未经转义的引号和反斜杠
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param writer Writer
|
||||
* @return Writer
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
public static Writer quote(String str, Writer writer) throws IOException {
|
||||
return quote(str, writer, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对所有双引号做转义处理(使用双反斜杠做转义)<br>
|
||||
* 为了能在HTML中较好的显示,会将</转义为<\/<br>
|
||||
* JSON字符串中不能包含控制字符和未经转义的引号和反斜杠
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param writer Writer
|
||||
* @param isWrap 是否使用双引号包装字符串
|
||||
* @return Writer
|
||||
* @throws IOException IO异常
|
||||
* @since 3.3.1
|
||||
*/
|
||||
public static Writer quote(String str, Writer writer, boolean isWrap) throws IOException {
|
||||
if (StrUtil.isEmpty(str)) {
|
||||
if (isWrap) {
|
||||
writer.write("\"\"");
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
|
||||
char c; // 当前字符
|
||||
int len = str.length();
|
||||
if (isWrap) {
|
||||
writer.write('"');
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
c = str.charAt(i);
|
||||
switch (c) {
|
||||
case '\\':
|
||||
case '"':
|
||||
writer.write("\\");
|
||||
writer.write(c);
|
||||
break;
|
||||
default:
|
||||
writer.write(escape(c));
|
||||
}
|
||||
}
|
||||
if (isWrap) {
|
||||
writer.write('"');
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转义显示不可见字符
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return 转义后的字符串
|
||||
*/
|
||||
public static String escape(String str) {
|
||||
if (StrUtil.isEmpty(str)) {
|
||||
return str;
|
||||
}
|
||||
|
||||
final int len = str.length();
|
||||
final StringBuilder builder = new StringBuilder(len);
|
||||
char c;
|
||||
for (int i = 0; i < len; i++) {
|
||||
c = str.charAt(i);
|
||||
builder.append(escape(c));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在需要的时候包装对象<br>
|
||||
* 包装包括:
|
||||
* <ul>
|
||||
* <li>{@code null} =》 {@code JSONNull.NULL}</li>
|
||||
* <li>array or collection =》 JSONArray</li>
|
||||
* <li>map =》 JSONObject</li>
|
||||
* <li>standard property (Double, String, et al) =》 原对象</li>
|
||||
* <li>来自于java包 =》 字符串</li>
|
||||
* <li>其它 =》 尝试包装为JSONObject,否则返回{@code null}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param object 被包装的对象
|
||||
* @param jsonConfig JSON选项
|
||||
* @return 包装后的值,null表示此值需被忽略
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public static Object wrap(Object object, JSONConfig jsonConfig) {
|
||||
if (object == null) {
|
||||
return jsonConfig.isIgnoreNullValue() ? null : JSONNull.NULL;
|
||||
}
|
||||
if (object instanceof JSON //
|
||||
|| ObjectUtil.isNull(object) //
|
||||
|| object instanceof JSONString //
|
||||
|| object instanceof CharSequence //
|
||||
|| object instanceof Number //
|
||||
|| ObjectUtil.isBasicType(object) //
|
||||
) {
|
||||
return object;
|
||||
}
|
||||
|
||||
try {
|
||||
// fix issue#1399@Github
|
||||
if (object instanceof SQLException) {
|
||||
return object.toString();
|
||||
}
|
||||
|
||||
// JSONArray
|
||||
if (object instanceof Iterable || ArrayUtil.isArray(object)) {
|
||||
return new JSONArray(object, jsonConfig);
|
||||
}
|
||||
// JSONObject
|
||||
if (object instanceof Map || object instanceof Map.Entry) {
|
||||
return new JSONObject(object, jsonConfig);
|
||||
}
|
||||
|
||||
// 日期类型原样保存,便于格式化
|
||||
if (object instanceof Date
|
||||
|| object instanceof Calendar
|
||||
|| object instanceof TemporalAccessor
|
||||
) {
|
||||
return object;
|
||||
}
|
||||
// 枚举类保存其字符串形式(4.0.2新增)
|
||||
if (object instanceof Enum) {
|
||||
return object.toString();
|
||||
}
|
||||
|
||||
// Java内部类不做转换
|
||||
if (ClassUtil.isJdkClass(object.getClass())) {
|
||||
return object.toString();
|
||||
}
|
||||
|
||||
// 默认按照JSONObject对待
|
||||
return new JSONObject(object, jsonConfig);
|
||||
} catch (final Exception exception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化JSON字符串,此方法并不严格检查JSON的格式正确与否
|
||||
*
|
||||
* @param jsonStr JSON字符串
|
||||
* @return 格式化后的字符串
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public static String formatJsonStr(String jsonStr) {
|
||||
return JSONStrFormatter.format(jsonStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为JSON字符串,首尾都为大括号或中括号判定为JSON字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return 是否为JSON字符串
|
||||
* @since 3.3.0
|
||||
* @deprecated 方法名称有歧义,请使用 {@link #isTypeJSON(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isJson(String str) {
|
||||
return isTypeJSON(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为JSON类型字符串,首尾都为大括号或中括号判定为JSON字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return 是否为JSON类型字符串
|
||||
* @since 5.7.22
|
||||
*/
|
||||
public static boolean isTypeJSON(String str) {
|
||||
return isTypeJSONObject(str) || isTypeJSONArray(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为JSONObject字符串,首尾都为大括号判定为JSONObject字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return 是否为JSON字符串
|
||||
* @since 3.3.0
|
||||
* @deprecated 方法名称有歧义,请使用 {@link #isTypeJSONObject(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isJsonObj(String str) {
|
||||
return isTypeJSONObject(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为JSONObject类型字符串,首尾都为大括号判定为JSONObject字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return 是否为JSON字符串
|
||||
* @since 5.7.22
|
||||
*/
|
||||
public static boolean isTypeJSONObject(String str) {
|
||||
if (StrUtil.isBlank(str)) {
|
||||
return false;
|
||||
}
|
||||
return StrUtil.isWrap(StrUtil.trim(str), '{', '}');
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为JSONArray字符串,首尾都为中括号判定为JSONArray字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return 是否为JSON字符串
|
||||
* @since 3.3.0
|
||||
* @deprecated 方法名称有歧义,请使用 {@link #isTypeJSONArray(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isJsonArray(String str) {
|
||||
return isTypeJSONArray(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为JSONArray类型的字符串,首尾都为中括号判定为JSONArray字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return 是否为JSONArray类型字符串
|
||||
* @since 5.7.22
|
||||
*/
|
||||
public static boolean isTypeJSONArray(String str) {
|
||||
if (StrUtil.isBlank(str)) {
|
||||
return false;
|
||||
}
|
||||
return StrUtil.isWrap(StrUtil.trim(str), '[', ']');
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为null对象,null的情况包括:
|
||||
*
|
||||
* <pre>
|
||||
* 1. {@code null}
|
||||
* 2. {@link JSONNull}
|
||||
* </pre>
|
||||
*
|
||||
* @param obj 对象
|
||||
* @return 是否为null
|
||||
* @since 4.5.7
|
||||
*/
|
||||
public static boolean isNull(Object obj) {
|
||||
return null == obj || obj instanceof JSONNull;
|
||||
}
|
||||
|
||||
/**
|
||||
* XML转JSONObject<br>
|
||||
* 转换过程中一些信息可能会丢失,JSON中无法区分节点和属性,相同的节点将被处理为JSONArray。
|
||||
*
|
||||
* @param xml XML字符串
|
||||
* @return JSONObject
|
||||
* @since 4.0.8
|
||||
*/
|
||||
public static JSONObject xmlToJson(String xml) {
|
||||
return XML.toJSONObject(xml);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入自定义的序列化器
|
||||
*
|
||||
* @param type 对象类型
|
||||
* @param serializer 序列化器实现
|
||||
* @see GlobalSerializeMapping#put(Type, JSONArraySerializer)
|
||||
* @since 4.6.5
|
||||
*/
|
||||
public static void putSerializer(Type type, JSONArraySerializer<?> serializer) {
|
||||
GlobalSerializeMapping.put(type, serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入自定义的序列化器
|
||||
*
|
||||
* @param type 对象类型
|
||||
* @param serializer 序列化器实现
|
||||
* @see GlobalSerializeMapping#put(Type, JSONObjectSerializer)
|
||||
* @since 4.6.5
|
||||
*/
|
||||
public static void putSerializer(Type type, JSONObjectSerializer<?> serializer) {
|
||||
GlobalSerializeMapping.put(type, serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入自定义的反序列化器
|
||||
*
|
||||
* @param type 对象类型
|
||||
* @param deserializer 反序列化器实现
|
||||
* @see GlobalSerializeMapping#put(Type, JSONDeserializer)
|
||||
* @since 4.6.5
|
||||
*/
|
||||
public static void putDeserializer(Type type, JSONDeserializer<?> deserializer) {
|
||||
GlobalSerializeMapping.put(type, deserializer);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------- Private method start
|
||||
|
||||
/**
|
||||
* 转义不可见字符<br>
|
||||
* 见:<a href="https://en.wikibooks.org/wiki/Unicode/Character_reference/0000-0FFF">https://en.wikibooks.org/wiki/Unicode/Character_reference/0000-0FFF</a>
|
||||
*
|
||||
* @param c 字符
|
||||
* @return 转义后的字符串
|
||||
*/
|
||||
private static String escape(char c) {
|
||||
switch (c) {
|
||||
case '\b':
|
||||
return "\\b";
|
||||
case '\t':
|
||||
return "\\t";
|
||||
case '\n':
|
||||
return "\\n";
|
||||
case '\f':
|
||||
return "\\f";
|
||||
case '\r':
|
||||
return "\\r";
|
||||
default:
|
||||
if (c < StrUtil.C_SPACE || //
|
||||
(c >= '\u0080' && c <= '\u00a0') || //
|
||||
(c >= '\u2000' && c <= '\u2010') || //
|
||||
(c >= '\u2028' && c <= '\u202F') || //
|
||||
(c >= '\u2066' && c <= '\u206F')//
|
||||
) {
|
||||
return HexUtil.toUnicodeHex(c);
|
||||
} else {
|
||||
return Character.toString(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
// --------------------------------------------------------------------------------------------- Private method end
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.bean.BeanUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.collection.ArrayIter;
|
||||
import aiyh.utils.tool.cn.hutool.core.convert.Convert;
|
||||
import aiyh.utils.tool.cn.hutool.core.io.IoUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Filter;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.mutable.Mutable;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.mutable.MutablePair;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.TypeUtil;
|
||||
import aiyh.utils.tool.cn.hutool.json.serialize.GlobalSerializeMapping;
|
||||
import aiyh.utils.tool.cn.hutool.json.serialize.JSONObjectSerializer;
|
||||
import aiyh.utils.tool.cn.hutool.json.serialize.JSONSerializer;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* 对象和JSON映射器,用于转换对象为JSON,支持:
|
||||
* <ul>
|
||||
* <li>Map 转 JSONObject,将键值对加入JSON对象</li>
|
||||
* <li>Map.Entry 转 JSONObject</li>
|
||||
* <li>CharSequence 转 JSONObject,使用JSONTokener解析</li>
|
||||
* <li>{@link Reader} 转 JSONObject,使用JSONTokener解析</li>
|
||||
* <li>{@link InputStream} 转 JSONObject,使用JSONTokener解析</li>
|
||||
* <li>JSONTokener 转 JSONObject,直接解析</li>
|
||||
* <li>ResourceBundle 转 JSONObject</li>
|
||||
* <li>Bean 转 JSONObject,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象。例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public class ObjectMapper {
|
||||
|
||||
/**
|
||||
* 创建ObjectMapper
|
||||
*
|
||||
* @param source 来源对象
|
||||
* @return ObjectMapper
|
||||
*/
|
||||
public static ObjectMapper of(Object source) {
|
||||
return new ObjectMapper(source);
|
||||
}
|
||||
|
||||
private final Object source;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param source 来源对象
|
||||
*/
|
||||
public ObjectMapper(Object source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将给定对象转换为{@link JSONObject}
|
||||
*
|
||||
* @param jsonObject 目标{@link JSONObject}
|
||||
* @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public void map(JSONObject jsonObject, Filter<MutablePair<String, Object>> filter) {
|
||||
final Object source = this.source;
|
||||
if (null == source) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 自定义序列化
|
||||
final JSONSerializer serializer = GlobalSerializeMapping.getSerializer(source.getClass());
|
||||
if (serializer instanceof JSONObjectSerializer) {
|
||||
serializer.serialize(jsonObject, source);
|
||||
return;
|
||||
}
|
||||
|
||||
if (source instanceof JSONArray) {
|
||||
// 不支持集合类型转换为JSONObject
|
||||
throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass());
|
||||
}
|
||||
|
||||
if (source instanceof Map) {
|
||||
// Map
|
||||
for (final Map.Entry<?, ?> e : ((Map<?, ?>) source).entrySet()) {
|
||||
jsonObject.set(Convert.toStr(e.getKey()), e.getValue(), filter, jsonObject.getConfig().isCheckDuplicate());
|
||||
}
|
||||
} else if (source instanceof Map.Entry) {
|
||||
final Map.Entry entry = (Map.Entry) source;
|
||||
jsonObject.set(Convert.toStr(entry.getKey()), entry.getValue(), filter, jsonObject.getConfig().isCheckDuplicate());
|
||||
} else if (source instanceof CharSequence) {
|
||||
// 可能为JSON字符串
|
||||
mapFromStr((CharSequence) source, jsonObject, filter);
|
||||
} else if (source instanceof Reader) {
|
||||
mapFromTokener(new JSONTokener((Reader) source, jsonObject.getConfig()), jsonObject, filter);
|
||||
} else if (source instanceof InputStream) {
|
||||
mapFromTokener(new JSONTokener((InputStream) source, jsonObject.getConfig()), jsonObject, filter);
|
||||
} else if (source instanceof byte[]) {
|
||||
mapFromTokener(new JSONTokener(IoUtil.toStream((byte[]) source), jsonObject.getConfig()), jsonObject, filter);
|
||||
} else if (source instanceof JSONTokener) {
|
||||
// JSONTokener
|
||||
mapFromTokener((JSONTokener) source, jsonObject, filter);
|
||||
} else if (source instanceof ResourceBundle) {
|
||||
// JSONTokener
|
||||
mapFromResourceBundle((ResourceBundle) source, jsonObject, filter);
|
||||
} else if (BeanUtil.isReadableBean(source.getClass())) {
|
||||
// 普通Bean
|
||||
// TODO 过滤器对Bean无效,需补充。
|
||||
mapFromBean(source, jsonObject);
|
||||
} else {
|
||||
// 不支持对象类型转换为JSONObject
|
||||
throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param jsonArray 目标{@link JSONArray}
|
||||
* @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤
|
||||
* @throws JSONException 非数组或集合
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public void map(JSONArray jsonArray, Filter<Mutable<Object>> filter) throws JSONException {
|
||||
final Object source = this.source;
|
||||
if (null == source) {
|
||||
return;
|
||||
}
|
||||
|
||||
final JSONSerializer serializer = GlobalSerializeMapping.getSerializer(source.getClass());
|
||||
if (null != serializer && JSONArray.class.equals(TypeUtil.getTypeArgument(serializer.getClass()))) {
|
||||
// 自定义序列化
|
||||
serializer.serialize(jsonArray, source);
|
||||
} else if (source instanceof CharSequence) {
|
||||
// JSON字符串
|
||||
mapFromStr((CharSequence) source, jsonArray, filter);
|
||||
} else if (source instanceof Reader) {
|
||||
mapFromTokener(new JSONTokener((Reader) source, jsonArray.getConfig()), jsonArray, filter);
|
||||
} else if (source instanceof InputStream) {
|
||||
mapFromTokener(new JSONTokener((InputStream) source, jsonArray.getConfig()), jsonArray, filter);
|
||||
} else if (source instanceof byte[]) {
|
||||
final byte[] bytesSource = (byte[]) source;
|
||||
// 如果是普通的的byte[], 要避免下标越界
|
||||
if (bytesSource.length > 1 && '[' == bytesSource[0] && ']' == bytesSource[bytesSource.length - 1]) {
|
||||
mapFromTokener(new JSONTokener(IoUtil.toStream(bytesSource), jsonArray.getConfig()), jsonArray, filter);
|
||||
} else {
|
||||
// https://github.com/dromara/hutool/issues/2369
|
||||
// 非标准的二进制流,则按照普通数组对待
|
||||
for (final byte b : bytesSource) {
|
||||
jsonArray.add(b);
|
||||
}
|
||||
}
|
||||
} else if (source instanceof JSONTokener) {
|
||||
mapFromTokener((JSONTokener) source, jsonArray, filter);
|
||||
} else {
|
||||
Iterator<?> iter;
|
||||
if (ArrayUtil.isArray(source)) {// 数组
|
||||
iter = new ArrayIter<>(source);
|
||||
} else if (source instanceof Iterator<?>) {// Iterator
|
||||
iter = ((Iterator<?>) source);
|
||||
} else if (source instanceof Iterable<?>) {// Iterable
|
||||
iter = ((Iterable<?>) source).iterator();
|
||||
} else {
|
||||
throw new JSONException("JSONArray initial value should be a string or collection or array.");
|
||||
}
|
||||
|
||||
final JSONConfig config = jsonArray.getConfig();
|
||||
Object next;
|
||||
while (iter.hasNext()) {
|
||||
next = iter.next();
|
||||
// 检查循环引用
|
||||
if (next != source) {
|
||||
jsonArray.addRaw(JSONUtil.wrap(next, config), filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从{@link ResourceBundle}转换
|
||||
*
|
||||
* @param bundle ResourceBundle
|
||||
* @param jsonObject {@link JSONObject}
|
||||
* @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤
|
||||
* @since 5.3.1
|
||||
*/
|
||||
private static void mapFromResourceBundle(ResourceBundle bundle, JSONObject jsonObject, Filter<MutablePair<String, Object>> filter) {
|
||||
Enumeration<String> keys = bundle.getKeys();
|
||||
while (keys.hasMoreElements()) {
|
||||
String key = keys.nextElement();
|
||||
if (key != null) {
|
||||
InternalJSONUtil.propertyPut(jsonObject, key, bundle.getString(key), filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字符串转换
|
||||
*
|
||||
* @param source JSON字符串
|
||||
* @param jsonObject {@link JSONObject}
|
||||
* @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤
|
||||
*/
|
||||
private static void mapFromStr(CharSequence source, JSONObject jsonObject, Filter<MutablePair<String, Object>> filter) {
|
||||
final String jsonStr = StrUtil.trim(source);
|
||||
if (StrUtil.startWith(jsonStr, '<')) {
|
||||
// 可能为XML
|
||||
XML.toJSONObject(jsonObject, jsonStr, false);
|
||||
return;
|
||||
}
|
||||
mapFromTokener(new JSONTokener(StrUtil.trim(source), jsonObject.getConfig()), jsonObject, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param source JSON字符串
|
||||
* @param jsonArray {@link JSONArray}
|
||||
* @param filter 值过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤
|
||||
*/
|
||||
private void mapFromStr(CharSequence source, JSONArray jsonArray, Filter<Mutable<Object>> filter) {
|
||||
if (null != source) {
|
||||
mapFromTokener(new JSONTokener(StrUtil.trim(source), jsonArray.getConfig()), jsonArray, filter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从{@link JSONTokener}转换
|
||||
*
|
||||
* @param x JSONTokener
|
||||
* @param jsonObject {@link JSONObject}
|
||||
* @param filter 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作
|
||||
*/
|
||||
private static void mapFromTokener(JSONTokener x, JSONObject jsonObject, Filter<MutablePair<String, Object>> filter) {
|
||||
JSONParser.of(x).parseTo(jsonObject, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param x {@link JSONTokener}
|
||||
* @param jsonArray {@link JSONArray}
|
||||
* @param filter 值过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤
|
||||
*/
|
||||
private static void mapFromTokener(JSONTokener x, JSONArray jsonArray, Filter<Mutable<Object>> filter) {
|
||||
JSONParser.of(x).parseTo(jsonArray, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Bean转换
|
||||
*
|
||||
* @param bean Bean对象
|
||||
* @param jsonObject {@link JSONObject}
|
||||
*/
|
||||
private static void mapFromBean(Object bean, JSONObject jsonObject) {
|
||||
BeanUtil.beanToMap(bean, jsonObject, InternalJSONUtil.toCopyOptions(jsonObject.getConfig()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.util.CharUtil;
|
||||
import aiyh.utils.tool.cn.hutool.json.xml.JSONXMLParser;
|
||||
import aiyh.utils.tool.cn.hutool.json.xml.JSONXMLSerializer;
|
||||
|
||||
/**
|
||||
* 提供静态方法在XML和JSONObject之间转换
|
||||
*
|
||||
* @author JSON.org, looly
|
||||
* @see JSONXMLParser
|
||||
* @see JSONXMLSerializer
|
||||
*/
|
||||
public class XML {
|
||||
|
||||
/**
|
||||
* The Character '&'.
|
||||
*/
|
||||
public static final Character AMP = CharUtil.AMP;
|
||||
|
||||
/**
|
||||
* The Character '''.
|
||||
*/
|
||||
public static final Character APOS = CharUtil.SINGLE_QUOTE;
|
||||
|
||||
/**
|
||||
* The Character '!'.
|
||||
*/
|
||||
public static final Character BANG = '!';
|
||||
|
||||
/**
|
||||
* The Character '='.
|
||||
*/
|
||||
public static final Character EQ = '=';
|
||||
|
||||
/**
|
||||
* The Character '>'.
|
||||
*/
|
||||
public static final Character GT = '>';
|
||||
|
||||
/**
|
||||
* The Character '<'.
|
||||
*/
|
||||
public static final Character LT = '<';
|
||||
|
||||
/**
|
||||
* The Character '?'.
|
||||
*/
|
||||
public static final Character QUEST = '?';
|
||||
|
||||
/**
|
||||
* The Character '"'.
|
||||
*/
|
||||
public static final Character QUOT = CharUtil.DOUBLE_QUOTES;
|
||||
|
||||
/**
|
||||
* The Character '/'.
|
||||
*/
|
||||
public static final Character SLASH = CharUtil.SLASH;
|
||||
|
||||
/**
|
||||
* 转换XML为JSONObject
|
||||
* 转换过程中一些信息可能会丢失,JSON中无法区分节点和属性,相同的节点将被处理为JSONArray。
|
||||
* Content text may be placed in a "content" member. Comments, prologs, DTDs, and {@code <[ [ ]]>} are ignored.
|
||||
*
|
||||
* @param string The source string.
|
||||
* @return A JSONObject containing the structured data from the XML string.
|
||||
* @throws JSONException Thrown if there is an errors while parsing the string
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
return toJSONObject(string, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换XML为JSONObject
|
||||
* 转换过程中一些信息可能会丢失,JSON中无法区分节点和属性,相同的节点将被处理为JSONArray。
|
||||
* Content text may be placed in a "content" member. Comments, prologs, DTDs, and {@code <[ [ ]]>} are ignored.
|
||||
* All values are converted as strings, for 1, 01, 29.0 will not be coerced to numbers but will instead be the exact value as seen in the XML document.
|
||||
*
|
||||
* @param string The source string.
|
||||
* @param keepStrings If true, then values will not be coerced into boolean or numeric values and will instead be left as strings
|
||||
* @return A JSONObject containing the structured data from the XML string.
|
||||
* @throws JSONException Thrown if there is an errors while parsing the string
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException {
|
||||
return toJSONObject(new JSONObject(), string, keepStrings);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换XML为JSONObject
|
||||
* 转换过程中一些信息可能会丢失,JSON中无法区分节点和属性,相同的节点将被处理为JSONArray。
|
||||
*
|
||||
* @param jo JSONObject
|
||||
* @param xmlStr XML字符串
|
||||
* @param keepStrings 如果为{@code true},则值保持String类型,不转换为数字或boolean
|
||||
* @return A JSONObject 解析后的JSON对象,与传入的jo为同一对象
|
||||
* @throws JSONException 解析异常
|
||||
* @since 5.3.1
|
||||
*/
|
||||
public static JSONObject toJSONObject(JSONObject jo, String xmlStr, boolean keepStrings) throws JSONException {
|
||||
JSONXMLParser.parseJSONObject(jo, xmlStr, keepStrings);
|
||||
return jo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换JSONObject为XML
|
||||
*
|
||||
* @param object JSON对象或数组
|
||||
* @return XML字符串
|
||||
* @throws JSONException JSON解析异常
|
||||
*/
|
||||
public static String toXml(Object object) throws JSONException {
|
||||
return toXml(object, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换JSONObject为XML
|
||||
*
|
||||
* @param object JSON对象或数组
|
||||
* @param tagName 可选标签名称,名称为空时忽略标签
|
||||
* @return A string.
|
||||
* @throws JSONException JSON解析异常
|
||||
*/
|
||||
public static String toXml(Object object, String tagName) throws JSONException {
|
||||
return toXml(object, tagName, "content");
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换JSONObject为XML
|
||||
*
|
||||
* @param object JSON对象或数组
|
||||
* @param tagName 可选标签名称,名称为空时忽略标签
|
||||
* @param contentKeys 标识为内容的key,遇到此key直接解析内容而不增加对应名称标签
|
||||
* @return A string.
|
||||
* @throws JSONException JSON解析异常
|
||||
*/
|
||||
public static String toXml(Object object, String tagName, String... contentKeys) throws JSONException {
|
||||
return JSONXMLSerializer.toXml(object, tagName, contentKeys);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,362 @@
|
|||
package aiyh.utils.tool.cn.hutool.json;
|
||||
|
||||
|
||||
/**
|
||||
* XML分析器,继承自JSONTokener,提供XML的语法分析
|
||||
*
|
||||
* @author JSON.org
|
||||
*/
|
||||
public class XMLTokener extends JSONTokener {
|
||||
|
||||
/**
|
||||
* The table of entity values.
|
||||
* It initially contains Character values for amp, apos, gt, lt, quot.
|
||||
*/
|
||||
public static final java.util.HashMap<String, Character> entity;
|
||||
|
||||
static {
|
||||
entity = new java.util.HashMap<>(8);
|
||||
entity.put("amp", XML.AMP);
|
||||
entity.put("apos", XML.APOS);
|
||||
entity.put("gt", XML.GT);
|
||||
entity.put("lt", XML.LT);
|
||||
entity.put("quot", XML.QUOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an XMLTokener from a string.
|
||||
*
|
||||
* @param s A source string.
|
||||
* @param config JSON配置
|
||||
*/
|
||||
public XMLTokener(CharSequence s, JSONConfig config) {
|
||||
super(s, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text in the CDATA block.
|
||||
*
|
||||
* @return The string up to the {@code ]]>}.
|
||||
* @throws JSONException If the {@code ]]>} is not found.
|
||||
*/
|
||||
public String nextCDATA() throws JSONException {
|
||||
char c;
|
||||
int i;
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (; ; ) {
|
||||
c = next();
|
||||
if (end()) {
|
||||
throw syntaxError("Unclosed CDATA");
|
||||
}
|
||||
sb.append(c);
|
||||
i = sb.length() - 3;
|
||||
if (i >= 0 && sb.charAt(i) == ']' && sb.charAt(i + 1) == ']' && sb.charAt(i + 2) == '>') {
|
||||
sb.setLength(i);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next XML outer token, trimming whitespace.
|
||||
* There are two kinds of tokens: the '>' character which begins a markup tag, and the content text between markup tags.
|
||||
*
|
||||
* @return A string, or a '>' Character, or null if there is no more source text.
|
||||
* @throws JSONException JSON
|
||||
*/
|
||||
public Object nextContent() throws JSONException {
|
||||
char c;
|
||||
final StringBuilder sb;
|
||||
do {
|
||||
c = next();
|
||||
} while (Character.isWhitespace(c));
|
||||
if (c == 0) {
|
||||
return null;
|
||||
}
|
||||
if (c == '<') {
|
||||
return XML.LT;
|
||||
}
|
||||
sb = new StringBuilder();
|
||||
for (; ; ) {
|
||||
if (c == '<' || c == 0) {
|
||||
back();
|
||||
return sb.toString().trim();
|
||||
}
|
||||
if (c == '&') {
|
||||
sb.append(nextEntity(c));
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
c = next();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next entity. These entities are translated to Characters: {@code & ' > < "}.
|
||||
*
|
||||
* @param ampersand An ampersand character.
|
||||
* @return A Character or an entity String if the entity is not recognized.
|
||||
* @throws JSONException If missing ';' in XML entity.
|
||||
*/
|
||||
public Object nextEntity(char ampersand) throws JSONException {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
char c;
|
||||
for (; ; ) {
|
||||
c = next();
|
||||
if (Character.isLetterOrDigit(c) || c == '#') {
|
||||
sb.append(Character.toLowerCase(c));
|
||||
} else if (c == ';') {
|
||||
break;
|
||||
} else {
|
||||
throw syntaxError("Missing ';' in XML entity: &" + sb);
|
||||
}
|
||||
}
|
||||
return unescapeEntity(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unescape an XML entity encoding;
|
||||
*
|
||||
* @param e entity (only the actual entity value, not the preceding & or ending ;
|
||||
* @return Unescape str
|
||||
*/
|
||||
static String unescapeEntity(final String e) {
|
||||
// validate
|
||||
if (e == null || e.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
// if our entity is an encoded unicode point, parse it.
|
||||
if (e.charAt(0) == '#') {
|
||||
final int cp;
|
||||
if (e.charAt(1) == 'x' || e.charAt(1) == 'X') {
|
||||
// hex encoded unicode
|
||||
cp = Integer.parseInt(e.substring(2), 16);
|
||||
} else {
|
||||
// decimal encoded unicode
|
||||
cp = Integer.parseInt(e.substring(1));
|
||||
}
|
||||
return new String(new int[]{cp}, 0, 1);
|
||||
}
|
||||
final Character knownEntity = entity.get(e);
|
||||
if (knownEntity == null) {
|
||||
// we don't know the entity so keep it encoded
|
||||
return '&' + e + ';';
|
||||
}
|
||||
return knownEntity.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next XML meta token. This is used for skipping over <!...> and <?...?> structures.
|
||||
*
|
||||
* @return Syntax characters ({@code < > / = ! ?}) are returned as Character, and strings and names are returned as Boolean. We don't care what the values actually are.
|
||||
* @throws JSONException 字符串中属性未关闭或XML结构错误抛出此异常。If a string is not properly closed or if the XML is badly structured.
|
||||
*/
|
||||
public Object nextMeta() throws JSONException {
|
||||
char c;
|
||||
char q;
|
||||
do {
|
||||
c = next();
|
||||
} while (Character.isWhitespace(c));
|
||||
switch (c) {
|
||||
case 0:
|
||||
throw syntaxError("Misshaped meta tag");
|
||||
case '<':
|
||||
return XML.LT;
|
||||
case '>':
|
||||
return XML.GT;
|
||||
case '/':
|
||||
return XML.SLASH;
|
||||
case '=':
|
||||
return XML.EQ;
|
||||
case '!':
|
||||
return XML.BANG;
|
||||
case '?':
|
||||
return XML.QUEST;
|
||||
case '"':
|
||||
case '\'':
|
||||
q = c;
|
||||
for (; ; ) {
|
||||
c = next();
|
||||
if (c == 0) {
|
||||
throw syntaxError("Unterminated string");
|
||||
}
|
||||
if (c == q) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
default:
|
||||
for (; ; ) {
|
||||
c = next();
|
||||
if (Character.isWhitespace(c)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
switch (c) {
|
||||
case 0:
|
||||
case '<':
|
||||
case '>':
|
||||
case '/':
|
||||
case '=':
|
||||
case '!':
|
||||
case '?':
|
||||
case '"':
|
||||
case '\'':
|
||||
back();
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next XML Token. These tokens are found inside of angle brackets. <br>
|
||||
* It may be one of these characters: {@code / > = ! ?} or it may be a string wrapped in single quotes or double
|
||||
* quotes, or it may be a name.
|
||||
*
|
||||
* @return a String or a Character.
|
||||
* @throws JSONException If the XML is not well formed.
|
||||
*/
|
||||
public Object nextToken() throws JSONException {
|
||||
char c;
|
||||
char q;
|
||||
StringBuilder sb;
|
||||
do {
|
||||
c = next();
|
||||
} while (Character.isWhitespace(c));
|
||||
switch (c) {
|
||||
case 0:
|
||||
throw syntaxError("Misshaped element");
|
||||
case '<':
|
||||
throw syntaxError("Misplaced '<'");
|
||||
case '>':
|
||||
return XML.GT;
|
||||
case '/':
|
||||
return XML.SLASH;
|
||||
case '=':
|
||||
return XML.EQ;
|
||||
case '!':
|
||||
return XML.BANG;
|
||||
case '?':
|
||||
return XML.QUEST;
|
||||
|
||||
// Quoted string
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
q = c;
|
||||
sb = new StringBuilder();
|
||||
for (; ; ) {
|
||||
c = next();
|
||||
if (c == 0) {
|
||||
throw syntaxError("Unterminated string");
|
||||
}
|
||||
if (c == q) {
|
||||
return sb.toString();
|
||||
}
|
||||
if (c == '&') {
|
||||
sb.append(nextEntity(c));
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
||||
// Name
|
||||
|
||||
sb = new StringBuilder();
|
||||
for (; ; ) {
|
||||
sb.append(c);
|
||||
c = next();
|
||||
if (Character.isWhitespace(c)) {
|
||||
return sb.toString();
|
||||
}
|
||||
switch (c) {
|
||||
case 0:
|
||||
return sb.toString();
|
||||
case '>':
|
||||
case '/':
|
||||
case '=':
|
||||
case '!':
|
||||
case '?':
|
||||
case '[':
|
||||
case ']':
|
||||
back();
|
||||
return sb.toString();
|
||||
case '<':
|
||||
case '"':
|
||||
case '\'':
|
||||
throw syntaxError("Bad character in a name");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip characters until past the requested string. If it is not found, we are left at the end of the source with a result of false.
|
||||
*
|
||||
* @param to A string to skip past.
|
||||
* @return 是否成功skip
|
||||
* @throws JSONException JSON异常
|
||||
*/
|
||||
public boolean skipPast(String to) throws JSONException {
|
||||
boolean b;
|
||||
char c;
|
||||
int i;
|
||||
int j;
|
||||
int offset = 0;
|
||||
int length = to.length();
|
||||
char[] circle = new char[length];
|
||||
|
||||
/*
|
||||
* First fill the circle buffer with as many characters as are in the to string. If we reach an early end, bail.
|
||||
*/
|
||||
|
||||
for (i = 0; i < length; i += 1) {
|
||||
c = next();
|
||||
if (c == 0) {
|
||||
return false;
|
||||
}
|
||||
circle[i] = c;
|
||||
}
|
||||
|
||||
/* We will loop, possibly for all of the remaining characters. */
|
||||
|
||||
for (; ; ) {
|
||||
j = offset;
|
||||
b = true;
|
||||
|
||||
/* Compare the circle buffer with the to string. */
|
||||
|
||||
for (i = 0; i < length; i += 1) {
|
||||
if (circle[j] != to.charAt(i)) {
|
||||
b = false;
|
||||
break;
|
||||
}
|
||||
j += 1;
|
||||
if (j >= length) {
|
||||
j -= length;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we exit the loop with b intact, then victory is ours. */
|
||||
|
||||
if (b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Get the next character. If there isn't one, then defeat is ours. */
|
||||
|
||||
c = next();
|
||||
if (c == 0) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* Shove the character in the circle buffer and advance the circle offset. The offset is mod n.
|
||||
*/
|
||||
circle[offset] = c;
|
||||
offset += 1;
|
||||
if (offset >= length) {
|
||||
offset -= length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* JSON封装,基于json.org官方库改造
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
package aiyh.utils.tool.cn.hutool.json;
|
|
@ -0,0 +1,112 @@
|
|||
package aiyh.utils.tool.cn.hutool.json.serialize;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.map.SafeConcurrentHashMap;
|
||||
import aiyh.utils.tool.cn.hutool.json.JSON;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 全局的序列化和反序列化器映射<br>
|
||||
* 在JSON和Java对象转换过程中,优先使用注册于此处的自定义转换
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class GlobalSerializeMapping {
|
||||
|
||||
private static Map<Type, JSONSerializer<? extends JSON, ?>> serializerMap;
|
||||
private static Map<Type, JSONDeserializer<?>> deserializerMap;
|
||||
|
||||
static {
|
||||
serializerMap = new SafeConcurrentHashMap<>();
|
||||
deserializerMap = new SafeConcurrentHashMap<>();
|
||||
|
||||
final TemporalAccessorSerializer localDateSerializer = new TemporalAccessorSerializer(LocalDate.class);
|
||||
serializerMap.put(LocalDate.class, localDateSerializer);
|
||||
deserializerMap.put(LocalDate.class, localDateSerializer);
|
||||
|
||||
final TemporalAccessorSerializer localDateTimeSerializer = new TemporalAccessorSerializer(LocalDateTime.class);
|
||||
serializerMap.put(LocalDateTime.class, localDateTimeSerializer);
|
||||
deserializerMap.put(LocalDateTime.class, localDateTimeSerializer);
|
||||
|
||||
final TemporalAccessorSerializer localTimeSerializer = new TemporalAccessorSerializer(LocalTime.class);
|
||||
serializerMap.put(LocalTime.class, localTimeSerializer);
|
||||
deserializerMap.put(LocalTime.class, localTimeSerializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入自定义的序列化器
|
||||
*
|
||||
* @param type 对象类型
|
||||
* @param serializer 序列化器实现
|
||||
*/
|
||||
public static void put(Type type, JSONArraySerializer<?> serializer) {
|
||||
putInternal(type, serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入自定义的序列化器
|
||||
*
|
||||
* @param type 对象类型
|
||||
* @param serializer 序列化器实现
|
||||
*/
|
||||
public static void put(Type type, JSONObjectSerializer<?> serializer) {
|
||||
putInternal(type, serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入自定义的序列化器
|
||||
*
|
||||
* @param type 对象类型
|
||||
* @param serializer 序列化器实现
|
||||
*/
|
||||
synchronized private static void putInternal(Type type, JSONSerializer<? extends JSON, ?> serializer) {
|
||||
if (null == serializerMap) {
|
||||
serializerMap = new SafeConcurrentHashMap<>();
|
||||
}
|
||||
serializerMap.put(type, serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入自定义的反序列化器
|
||||
*
|
||||
* @param type 对象类型
|
||||
* @param deserializer 反序列化器实现
|
||||
*/
|
||||
synchronized public static void put(Type type, JSONDeserializer<?> deserializer) {
|
||||
if (null == deserializerMap) {
|
||||
deserializerMap = new ConcurrentHashMap<>();
|
||||
}
|
||||
deserializerMap.put(type, deserializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义的序列化器,如果未定义返回{@code null}
|
||||
*
|
||||
* @param type 类型
|
||||
* @return 自定义的序列化器或者{@code null}
|
||||
*/
|
||||
public static JSONSerializer<? extends JSON, ?> getSerializer(Type type) {
|
||||
if (null == serializerMap) {
|
||||
return null;
|
||||
}
|
||||
return serializerMap.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义的反序列化器,如果未定义返回{@code null}
|
||||
*
|
||||
* @param type 类型
|
||||
* @return 自定义的反序列化器或者{@code null}
|
||||
*/
|
||||
public static JSONDeserializer<?> getDeserializer(Type type) {
|
||||
if (null == deserializerMap) {
|
||||
return null;
|
||||
}
|
||||
return deserializerMap.get(type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package aiyh.utils.tool.cn.hutool.json.serialize;
|
||||
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.json.JSONArray;
|
||||
|
||||
/**
|
||||
* JSON列表的序列化接口,用于将特定对象序列化为{@link JSONArray}
|
||||
*
|
||||
* @param <V> 对象类型
|
||||
* @author Looly
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface JSONArraySerializer<V> extends JSONSerializer<JSONArray, V> {
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package aiyh.utils.tool.cn.hutool.json.serialize;
|
||||
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.json.JSON;
|
||||
|
||||
/**
|
||||
* JSON反序列话自定义实现类
|
||||
*
|
||||
* @param <T> 反序列化后的类型
|
||||
* @author Looly
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface JSONDeserializer<T> {
|
||||
|
||||
/**
|
||||
* 反序列化,通过实现此方法,自定义实现JSON转换为指定类型的逻辑
|
||||
*
|
||||
* @param json {@link JSON}
|
||||
* @return 目标对象
|
||||
*/
|
||||
T deserialize(JSON json);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package aiyh.utils.tool.cn.hutool.json.serialize;
|
||||
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.json.JSONObject;
|
||||
|
||||
/**
|
||||
* 对象的序列化接口,用于将特定对象序列化为{@link JSONObject}
|
||||
*
|
||||
* @param <V> 对象类型
|
||||
* @author Looly
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface JSONObjectSerializer<V> extends JSONSerializer<JSONObject, V> {
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package aiyh.utils.tool.cn.hutool.json.serialize;
|
||||
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.json.JSON;
|
||||
|
||||
/**
|
||||
* 序列化接口,通过实现此接口,实现自定义的对象转换为JSON的操作
|
||||
*
|
||||
* @param <T> JSON类型,可以是JSONObject或者JSONArray
|
||||
* @param <V> 对象类型
|
||||
* @author Looly
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface JSONSerializer<T extends JSON, V> {
|
||||
|
||||
/**
|
||||
* 序列化实现,通过实现此方法,将指定类型的对象转换为{@link JSON}对象<br>
|
||||
* 转换后的对象可以为JSONObject也可以为JSONArray,首先new一个空的JSON,然后将需要的数据字段put到JSON对象中去即可。
|
||||
*
|
||||
* @param json JSON,可以为JSONObject或者JSONArray
|
||||
* @param bean 指定类型对象
|
||||
*/
|
||||
void serialize(T json, V bean);
|
||||
}
|
|
@ -0,0 +1,431 @@
|
|||
package aiyh.utils.tool.cn.hutool.json.serialize;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.convert.Convert;
|
||||
import aiyh.utils.tool.cn.hutool.core.date.DateUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.date.TemporalAccessorUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.date.format.GlobalCustomFormat;
|
||||
import aiyh.utils.tool.cn.hutool.core.io.IORuntimeException;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Filter;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.mutable.MutablePair;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.CharUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.NumberUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||
import aiyh.utils.tool.cn.hutool.json.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.time.MonthDay;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JSON数据写出器<br>
|
||||
* 通过简单的append方式将JSON的键值对等信息写出到{@link Writer}中。
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.3
|
||||
*/
|
||||
public class JSONWriter extends Writer {
|
||||
|
||||
/**
|
||||
* 缩进因子,定义每一级别增加的缩进量
|
||||
*/
|
||||
private final int indentFactor;
|
||||
/**
|
||||
* 本级别缩进量
|
||||
*/
|
||||
private final int indent;
|
||||
/**
|
||||
* Writer
|
||||
*/
|
||||
private final Writer writer;
|
||||
/**
|
||||
* JSON选项
|
||||
*/
|
||||
private final JSONConfig config;
|
||||
|
||||
/**
|
||||
* 写出当前值是否需要分隔符
|
||||
*/
|
||||
private boolean needSeparator;
|
||||
/**
|
||||
* 是否为JSONArray模式
|
||||
*/
|
||||
private boolean arrayMode;
|
||||
|
||||
/**
|
||||
* 创建JSONWriter
|
||||
*
|
||||
* @param writer {@link Writer}
|
||||
* @param indentFactor 缩进因子,定义每一级别增加的缩进量
|
||||
* @param indent 本级别缩进量
|
||||
* @param config JSON选项
|
||||
* @return JSONWriter
|
||||
*/
|
||||
public static JSONWriter of(Writer writer, int indentFactor, int indent, JSONConfig config) {
|
||||
return new JSONWriter(writer, indentFactor, indent, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param writer {@link Writer}
|
||||
* @param indentFactor 缩进因子,定义每一级别增加的缩进量
|
||||
* @param indent 本级别缩进量
|
||||
* @param config JSON选项
|
||||
*/
|
||||
public JSONWriter(Writer writer, int indentFactor, int indent, JSONConfig config) {
|
||||
this.writer = writer;
|
||||
this.indentFactor = indentFactor;
|
||||
this.indent = indent;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSONObject写出开始,默认写出"{"
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public JSONWriter beginObj() {
|
||||
writeRaw(CharUtil.DELIM_START);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSONArray写出开始,默认写出"["
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public JSONWriter beginArray() {
|
||||
writeRaw(CharUtil.BRACKET_START);
|
||||
arrayMode = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束,默认根据开始的类型,补充"}"或"]"
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public JSONWriter end() {
|
||||
// 换行缩进
|
||||
writeLF().writeSpace(indent);
|
||||
writeRaw(arrayMode ? CharUtil.BRACKET_END : CharUtil.DELIM_END);
|
||||
flush();
|
||||
arrayMode = false;
|
||||
// 当前对象或数组结束,当新的
|
||||
needSeparator = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出键,自动处理分隔符和缩进,并包装键名
|
||||
*
|
||||
* @param key 键名
|
||||
* @return this
|
||||
*/
|
||||
public JSONWriter writeKey(String key) {
|
||||
if (needSeparator) {
|
||||
writeRaw(CharUtil.COMMA);
|
||||
}
|
||||
// 换行缩进
|
||||
writeLF().writeSpace(indentFactor + indent);
|
||||
return writeRaw(JSONUtil.quote(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出值,自动处理分隔符和缩进,自动判断类型,并根据不同类型写出特定格式的值<br>
|
||||
* 如果写出的值为{@code null}或者{@link JSONNull},且配置忽略null,则跳过。
|
||||
*
|
||||
* @param value 值
|
||||
* @return this
|
||||
*/
|
||||
public JSONWriter writeValue(Object value) {
|
||||
if (JSONUtil.isNull(value) && config.isIgnoreNullValue()) {
|
||||
return this;
|
||||
}
|
||||
return writeValueDirect(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出字段名及字段值,如果字段值是{@code null}且忽略null值,则不写出任何内容
|
||||
*
|
||||
* @param key 字段名
|
||||
* @param value 字段值
|
||||
* @return this
|
||||
* @since 5.7.6
|
||||
* @deprecated 请使用 {@link #writeField(MutablePair, Filter)}
|
||||
*/
|
||||
@Deprecated
|
||||
public JSONWriter writeField(String key, Object value) {
|
||||
if (JSONUtil.isNull(value) && config.isIgnoreNullValue()) {
|
||||
return this;
|
||||
}
|
||||
return writeKey(key).writeValueDirect(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出字段名及字段值,如果字段值是{@code null}且忽略null值,则不写出任何内容
|
||||
*
|
||||
* @param pair 键值对
|
||||
* @param filter 键值对的过滤器,可以编辑键值对
|
||||
* @return this
|
||||
* @since 5.8.6
|
||||
*/
|
||||
public JSONWriter writeField(MutablePair<Object, Object> pair, Filter<MutablePair<Object, Object>> filter) {
|
||||
if (JSONUtil.isNull(pair.getValue()) && config.isIgnoreNullValue()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (null == filter || filter.accept(pair)) {
|
||||
if (!arrayMode) {
|
||||
// JSONArray只写值,JSONObject写键值对
|
||||
writeKey(StrUtil.toString(pair.getKey()));
|
||||
}
|
||||
return writeValueDirect(pair.getValue(), filter);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(char[] cbuf, int off, int len) throws IOException {
|
||||
this.writer.write(cbuf, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
try {
|
||||
this.writer.flush();
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.writer.close();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------ Private methods
|
||||
|
||||
/**
|
||||
* 写出值,自动处理分隔符和缩进,自动判断类型,并根据不同类型写出特定格式的值
|
||||
*
|
||||
* @param value 值
|
||||
* @param filter 键值对过滤器
|
||||
* @return this
|
||||
*/
|
||||
private JSONWriter writeValueDirect(Object value, Filter<MutablePair<Object, Object>> filter) {
|
||||
if (arrayMode) {
|
||||
if (needSeparator) {
|
||||
writeRaw(CharUtil.COMMA);
|
||||
}
|
||||
// 换行缩进
|
||||
writeLF().writeSpace(indentFactor + indent);
|
||||
} else {
|
||||
writeRaw(CharUtil.COLON).writeSpace(1);
|
||||
}
|
||||
needSeparator = true;
|
||||
return writeObjValue(value, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出JSON的值,根据值类型不同,输出不同内容
|
||||
*
|
||||
* @param value 值
|
||||
* @param filter 过滤器
|
||||
* @return this
|
||||
*/
|
||||
private JSONWriter writeObjValue(Object value, Filter<MutablePair<Object, Object>> filter) {
|
||||
final int indent = indentFactor + this.indent;
|
||||
if (value == null || value instanceof JSONNull) {
|
||||
writeRaw(JSONNull.NULL.toString());
|
||||
} else if (value instanceof JSON) {
|
||||
if (value instanceof JSONObject) {
|
||||
((JSONObject) value).write(writer, indentFactor, indent, filter);
|
||||
} else if (value instanceof JSONArray) {
|
||||
((JSONArray) value).write(writer, indentFactor, indent, filter);
|
||||
}
|
||||
} else if (value instanceof Map || value instanceof Map.Entry) {
|
||||
new JSONObject(value).write(writer, indentFactor, indent);
|
||||
} else if (value instanceof Iterable || value instanceof Iterator || ArrayUtil.isArray(value)) {
|
||||
new JSONArray(value).write(writer, indentFactor, indent);
|
||||
} else if (value instanceof Number) {
|
||||
writeNumberValue((Number) value);
|
||||
} else if (value instanceof Date || value instanceof Calendar || value instanceof TemporalAccessor) {
|
||||
// issue#2572@Github
|
||||
if (value instanceof MonthDay) {
|
||||
writeStrValue(value.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
final String format = (null == config) ? null : config.getDateFormat();
|
||||
writeRaw(formatDate(value, format));
|
||||
} else if (value instanceof Boolean) {
|
||||
writeBooleanValue((Boolean) value);
|
||||
} else if (value instanceof JSONString) {
|
||||
writeJSONStringValue((JSONString) value);
|
||||
} else {
|
||||
writeStrValue(value.toString());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数字,根据{@link JSONConfig#isStripTrailingZeros()} 配置不同,写出不同数字<br>
|
||||
* 主要针对Double型是否去掉小数点后多余的0<br>
|
||||
* 此方法输出的值不包装引号。
|
||||
*
|
||||
* @param number 数字
|
||||
*/
|
||||
private void writeNumberValue(Number number) {
|
||||
// since 5.6.2可配置是否去除末尾多余0,例如如果为true,5.0返回5
|
||||
final boolean isStripTrailingZeros = null == config || config.isStripTrailingZeros();
|
||||
writeRaw(NumberUtil.toStr(number, isStripTrailingZeros));
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出Boolean值,直接写出true或false,不适用引号包装
|
||||
*
|
||||
* @param value Boolean值
|
||||
*/
|
||||
private void writeBooleanValue(Boolean value) {
|
||||
writeRaw(value.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出实现了{@link JSONString}接口的对象,通过调用{@link JSONString#toJSONString()}获取JSON字符串<br>
|
||||
* {@link JSONString}按照JSON对象对待,此方法输出的JSON字符串不包装引号。<br>
|
||||
* 如果toJSONString()返回null,调用toString()方法并使用双引号包装。
|
||||
*
|
||||
* @param jsonString {@link JSONString}
|
||||
*/
|
||||
private void writeJSONStringValue(JSONString jsonString) {
|
||||
String valueStr;
|
||||
try {
|
||||
valueStr = jsonString.toJSONString();
|
||||
} catch (Exception e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
if (null != valueStr) {
|
||||
writeRaw(valueStr);
|
||||
} else {
|
||||
writeStrValue(jsonString.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出字符串值,并包装引号并转义字符<br>
|
||||
* 对所有双引号做转义处理(使用双反斜杠做转义)<br>
|
||||
* 为了能在HTML中较好的显示,会将</转义为<\/<br>
|
||||
* JSON字符串中不能包含控制字符和未经转义的引号和反斜杠
|
||||
*
|
||||
* @param csq 字符串
|
||||
*/
|
||||
private void writeStrValue(String csq) {
|
||||
try {
|
||||
JSONUtil.quote(csq, writer);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出空格
|
||||
*
|
||||
* @param count 空格数
|
||||
*/
|
||||
private void writeSpace(int count) {
|
||||
if (indentFactor > 0) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
writeRaw(CharUtil.SPACE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出换换行符
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
private JSONWriter writeLF() {
|
||||
if (indentFactor > 0) {
|
||||
writeRaw(CharUtil.LF);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入原始字符串值,不做任何处理
|
||||
*
|
||||
* @param csq 字符串
|
||||
* @return this
|
||||
*/
|
||||
private JSONWriter writeRaw(String csq) {
|
||||
try {
|
||||
writer.append(csq);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入原始字符值,不做任何处理
|
||||
*
|
||||
* @param c 字符串
|
||||
* @return this
|
||||
*/
|
||||
private JSONWriter writeRaw(char c) {
|
||||
try {
|
||||
writer.write(c);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照给定格式格式化日期,格式为空时返回时间戳字符串
|
||||
*
|
||||
* @param dateObj Date或者Calendar对象
|
||||
* @param format 格式
|
||||
* @return 日期字符串
|
||||
*/
|
||||
private static String formatDate(Object dateObj, String format) {
|
||||
if (StrUtil.isNotBlank(format)) {
|
||||
final String dateStr;
|
||||
if (dateObj instanceof TemporalAccessor) {
|
||||
dateStr = TemporalAccessorUtil.format((TemporalAccessor) dateObj, format);
|
||||
} else {
|
||||
dateStr = DateUtil.format(Convert.toDate(dateObj), format);
|
||||
}
|
||||
|
||||
if (GlobalCustomFormat.FORMAT_SECONDS.equals(format)
|
||||
|| GlobalCustomFormat.FORMAT_MILLISECONDS.equals(format)) {
|
||||
// Hutool自定义的秒和毫秒表示,默认不包装双引号
|
||||
return dateStr;
|
||||
}
|
||||
// 用户定义了日期格式
|
||||
return JSONUtil.quote(dateStr);
|
||||
}
|
||||
|
||||
// 默认使用时间戳
|
||||
long timeMillis;
|
||||
if (dateObj instanceof TemporalAccessor) {
|
||||
timeMillis = TemporalAccessorUtil.toEpochMilli((TemporalAccessor) dateObj);
|
||||
} else if (dateObj instanceof Date) {
|
||||
timeMillis = ((Date) dateObj).getTime();
|
||||
} else if (dateObj instanceof Calendar) {
|
||||
timeMillis = ((Calendar) dateObj).getTimeInMillis();
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Unsupported Date type: " + dateObj.getClass());
|
||||
}
|
||||
return String.valueOf(timeMillis);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package aiyh.utils.tool.cn.hutool.json.serialize;
|
||||
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.json.JSON;
|
||||
import aiyh.utils.tool.cn.hutool.json.JSONException;
|
||||
import aiyh.utils.tool.cn.hutool.json.JSONObject;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
|
||||
/**
|
||||
* {@link TemporalAccessor}的JSON自定义序列化实现
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.22
|
||||
*/
|
||||
public class TemporalAccessorSerializer implements JSONObjectSerializer<TemporalAccessor>, JSONDeserializer<TemporalAccessor> {
|
||||
|
||||
private static final String YEAR_KEY = "year";
|
||||
private static final String MONTH_KEY = "month";
|
||||
private static final String DAY_KEY = "day";
|
||||
private static final String HOUR_KEY = "hour";
|
||||
private static final String MINUTE_KEY = "minute";
|
||||
private static final String SECOND_KEY = "second";
|
||||
private static final String NANO_KEY = "nano";
|
||||
|
||||
private final Class<? extends TemporalAccessor> temporalAccessorClass;
|
||||
|
||||
public TemporalAccessorSerializer(Class<? extends TemporalAccessor> temporalAccessorClass) {
|
||||
this.temporalAccessorClass = temporalAccessorClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(JSONObject json, TemporalAccessor bean) {
|
||||
if (bean instanceof LocalDate) {
|
||||
final LocalDate localDate = (LocalDate) bean;
|
||||
json.set(YEAR_KEY, localDate.getYear());
|
||||
json.set(MONTH_KEY, localDate.getMonthValue());
|
||||
json.set(DAY_KEY, localDate.getDayOfMonth());
|
||||
} else if (bean instanceof LocalDateTime) {
|
||||
final LocalDateTime localDateTime = (LocalDateTime) bean;
|
||||
json.set(YEAR_KEY, localDateTime.getYear());
|
||||
json.set(MONTH_KEY, localDateTime.getMonthValue());
|
||||
json.set(DAY_KEY, localDateTime.getDayOfMonth());
|
||||
json.set(HOUR_KEY, localDateTime.getHour());
|
||||
json.set(MINUTE_KEY, localDateTime.getMinute());
|
||||
json.set(SECOND_KEY, localDateTime.getSecond());
|
||||
json.set(NANO_KEY, localDateTime.getNano());
|
||||
} else if (bean instanceof LocalTime) {
|
||||
final LocalTime localTime = (LocalTime) bean;
|
||||
json.set(HOUR_KEY, localTime.getHour());
|
||||
json.set(MINUTE_KEY, localTime.getMinute());
|
||||
json.set(SECOND_KEY, localTime.getSecond());
|
||||
json.set(NANO_KEY, localTime.getNano());
|
||||
} else {
|
||||
throw new JSONException("Unsupported type to JSON: {}", bean.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TemporalAccessor deserialize(JSON json) {
|
||||
final JSONObject jsonObject = (JSONObject) json;
|
||||
if (LocalDate.class.equals(this.temporalAccessorClass)) {
|
||||
return LocalDate.of(jsonObject.getInt(YEAR_KEY), jsonObject.getInt(MONTH_KEY), jsonObject.getInt(DAY_KEY));
|
||||
} else if (LocalDateTime.class.equals(this.temporalAccessorClass)) {
|
||||
return LocalDateTime.of(jsonObject.getInt(YEAR_KEY), jsonObject.getInt(MONTH_KEY), jsonObject.getInt(DAY_KEY),
|
||||
jsonObject.getInt(HOUR_KEY), jsonObject.getInt(MINUTE_KEY), jsonObject.getInt(SECOND_KEY), jsonObject.getInt(NANO_KEY));
|
||||
} else if (LocalTime.class.equals(this.temporalAccessorClass)) {
|
||||
return LocalTime.of(jsonObject.getInt(HOUR_KEY), jsonObject.getInt(MINUTE_KEY), jsonObject.getInt(SECOND_KEY), jsonObject.getInt(NANO_KEY));
|
||||
}
|
||||
|
||||
throw new JSONException("Unsupported type from JSON: {}", this.temporalAccessorClass);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* JSON自定义序列化和反序列化接口和默认实现
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
package aiyh.utils.tool.cn.hutool.json.serialize;
|
|
@ -0,0 +1,179 @@
|
|||
package aiyh.utils.tool.cn.hutool.json.xml;
|
||||
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.json.*;
|
||||
|
||||
/**
|
||||
* XML解析器,将XML解析为JSON对象
|
||||
*
|
||||
* @author JSON.org, looly
|
||||
* @since 5.7.11
|
||||
*/
|
||||
public class JSONXMLParser {
|
||||
|
||||
/**
|
||||
* 转换XML为JSONObject
|
||||
* 转换过程中一些信息可能会丢失,JSON中无法区分节点和属性,相同的节点将被处理为JSONArray。
|
||||
*
|
||||
* @param jo JSONObject
|
||||
* @param xmlStr XML字符串
|
||||
* @param keepStrings 如果为{@code true},则值保持String类型,不转换为数字或boolean
|
||||
* @throws JSONException 解析异常
|
||||
*/
|
||||
public static void parseJSONObject(JSONObject jo, String xmlStr, boolean keepStrings) throws JSONException {
|
||||
XMLTokener x = new XMLTokener(xmlStr, jo.getConfig());
|
||||
while (x.more() && x.skipPast("<")) {
|
||||
parse(x, jo, null, keepStrings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the content following the named tag, attaching it to the context.
|
||||
*
|
||||
* @param x The XMLTokener containing the source string.
|
||||
* @param context The JSONObject that will include the new material.
|
||||
* @param name The tag name.
|
||||
* @return true if the close tag is processed.
|
||||
* @throws JSONException JSON异常
|
||||
*/
|
||||
private static boolean parse(XMLTokener x, JSONObject context, String name, boolean keepStrings) throws JSONException {
|
||||
char c;
|
||||
int i;
|
||||
JSONObject jsonobject;
|
||||
String string;
|
||||
String tagName;
|
||||
Object token;
|
||||
|
||||
token = x.nextToken();
|
||||
|
||||
if (token == XML.BANG) {
|
||||
c = x.next();
|
||||
if (c == '-') {
|
||||
if (x.next() == '-') {
|
||||
x.skipPast("-->");
|
||||
return false;
|
||||
}
|
||||
x.back();
|
||||
} else if (c == '[') {
|
||||
token = x.nextToken();
|
||||
if ("CDATA".equals(token)) {
|
||||
if (x.next() == '[') {
|
||||
string = x.nextCDATA();
|
||||
if (string.length() > 0) {
|
||||
context.accumulate("content", string);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
throw x.syntaxError("Expected 'CDATA['");
|
||||
}
|
||||
i = 1;
|
||||
do {
|
||||
token = x.nextMeta();
|
||||
if (token == null) {
|
||||
throw x.syntaxError("Missing '>' after '<!'.");
|
||||
} else if (token == XML.LT) {
|
||||
i += 1;
|
||||
} else if (token == XML.GT) {
|
||||
i -= 1;
|
||||
}
|
||||
} while (i > 0);
|
||||
return false;
|
||||
} else if (token == XML.QUEST) {
|
||||
|
||||
// <?
|
||||
x.skipPast("?>");
|
||||
return false;
|
||||
} else if (token == XML.SLASH) {
|
||||
|
||||
// Close tag </
|
||||
|
||||
token = x.nextToken();
|
||||
if (name == null) {
|
||||
throw x.syntaxError("Mismatched close tag " + token);
|
||||
}
|
||||
if (!token.equals(name)) {
|
||||
throw x.syntaxError("Mismatched " + name + " and " + token);
|
||||
}
|
||||
if (x.nextToken() != XML.GT) {
|
||||
throw x.syntaxError("Misshaped close tag");
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if (token instanceof Character) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
|
||||
// Open tag <
|
||||
|
||||
} else {
|
||||
tagName = (String) token;
|
||||
token = null;
|
||||
jsonobject = new JSONObject();
|
||||
for (; ; ) {
|
||||
if (token == null) {
|
||||
token = x.nextToken();
|
||||
}
|
||||
|
||||
// attribute = value
|
||||
if (token instanceof String) {
|
||||
string = (String) token;
|
||||
token = x.nextToken();
|
||||
if (token == XML.EQ) {
|
||||
token = x.nextToken();
|
||||
if (!(token instanceof String)) {
|
||||
throw x.syntaxError("Missing value");
|
||||
}
|
||||
jsonobject.accumulate(string, keepStrings ? token : InternalJSONUtil.stringToValue((String) token));
|
||||
token = null;
|
||||
} else {
|
||||
jsonobject.accumulate(string, "");
|
||||
}
|
||||
|
||||
} else if (token == XML.SLASH) {
|
||||
// Empty tag <.../>
|
||||
if (x.nextToken() != XML.GT) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
if (jsonobject.size() > 0) {
|
||||
context.accumulate(tagName, jsonobject);
|
||||
} else {
|
||||
context.accumulate(tagName, "");
|
||||
}
|
||||
return false;
|
||||
|
||||
} else if (token == XML.GT) {
|
||||
// Content, between <...> and </...>
|
||||
for (; ; ) {
|
||||
token = x.nextContent();
|
||||
if (token == null) {
|
||||
if (tagName != null) {
|
||||
throw x.syntaxError("Unclosed tag " + tagName);
|
||||
}
|
||||
return false;
|
||||
} else if (token instanceof String) {
|
||||
string = (String) token;
|
||||
if (string.length() > 0) {
|
||||
jsonobject.accumulate("content", keepStrings ? token : InternalJSONUtil.stringToValue(string));
|
||||
}
|
||||
|
||||
} else if (token == XML.LT) {
|
||||
// Nested element
|
||||
if (parse(x, jsonobject, tagName, keepStrings)) {
|
||||
if (jsonobject.size() == 0) {
|
||||
context.accumulate(tagName, "");
|
||||
} else if (jsonobject.size() == 1 && jsonobject.get("content") != null) {
|
||||
context.accumulate(tagName, jsonobject.get("content"));
|
||||
} else {
|
||||
context.accumulate(tagName, jsonobject);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
package aiyh.utils.tool.cn.hutool.json.xml;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.CharUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.EscapeUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||
import aiyh.utils.tool.cn.hutool.json.JSONArray;
|
||||
import aiyh.utils.tool.cn.hutool.json.JSONException;
|
||||
import aiyh.utils.tool.cn.hutool.json.JSONObject;
|
||||
|
||||
/**
|
||||
* JSON转XML字符串工具
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.11
|
||||
*/
|
||||
public class JSONXMLSerializer {
|
||||
/**
|
||||
* 转换JSONObject为XML
|
||||
* Convert a JSONObject into a well-formed, element-normal XML string.
|
||||
*
|
||||
* @param object A JSONObject.
|
||||
* @return A string.
|
||||
* @throws JSONException Thrown if there is an error parsing the string
|
||||
*/
|
||||
public static String toXml(Object object) throws JSONException {
|
||||
return toXml(object, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换JSONObject为XML
|
||||
*
|
||||
* @param object JSON对象或数组
|
||||
* @param tagName 可选标签名称,名称为空时忽略标签
|
||||
* @return A string.
|
||||
* @throws JSONException JSON解析异常
|
||||
*/
|
||||
public static String toXml(Object object, String tagName) throws JSONException {
|
||||
return toXml(object, tagName, "content");
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换JSONObject为XML
|
||||
*
|
||||
* @param object JSON对象或数组
|
||||
* @param tagName 可选标签名称,名称为空时忽略标签
|
||||
* @param contentKeys 标识为内容的key,遇到此key直接解析内容而不增加对应名称标签
|
||||
* @return A string.
|
||||
* @throws JSONException JSON解析异常
|
||||
*/
|
||||
public static String toXml(Object object, String tagName, String... contentKeys) throws JSONException {
|
||||
if (null == object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
if (object instanceof JSONObject) {
|
||||
|
||||
// Emit <tagName>
|
||||
appendTag(sb, tagName, false);
|
||||
|
||||
// Loop thru the keys.
|
||||
((JSONObject) object).forEach((key, value) -> {
|
||||
if (ArrayUtil.isArray(value)) {
|
||||
value = new JSONArray(value);
|
||||
}
|
||||
|
||||
// Emit content in body
|
||||
if (ArrayUtil.contains(contentKeys, key)) {
|
||||
if (value instanceof JSONArray) {
|
||||
int i = 0;
|
||||
for (Object val : (JSONArray) value) {
|
||||
if (i > 0) {
|
||||
sb.append(CharUtil.LF);
|
||||
}
|
||||
sb.append(EscapeUtil.escapeXml(val.toString()));
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
sb.append(EscapeUtil.escapeXml(value.toString()));
|
||||
}
|
||||
|
||||
// Emit an array of similar keys
|
||||
|
||||
} else if (StrUtil.isEmptyIfStr(value)) {
|
||||
sb.append(wrapWithTag(null, key));
|
||||
} else if (value instanceof JSONArray) {
|
||||
for (Object val : (JSONArray) value) {
|
||||
if (val instanceof JSONArray) {
|
||||
sb.append(wrapWithTag(toXml(val, null, contentKeys), key));
|
||||
} else {
|
||||
sb.append(toXml(val, key, contentKeys));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sb.append(toXml(value, key, contentKeys));
|
||||
}
|
||||
});
|
||||
|
||||
// Emit the </tagname> close tag
|
||||
appendTag(sb, tagName, true);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
if (ArrayUtil.isArray(object)) {
|
||||
object = new JSONArray(object);
|
||||
}
|
||||
|
||||
if (object instanceof JSONArray) {
|
||||
for (Object val : (JSONArray) object) {
|
||||
// XML does not have good support for arrays. If an array
|
||||
// appears in a place where XML is lacking, synthesize an
|
||||
// <array> element.
|
||||
sb.append(toXml(val, tagName == null ? "array" : tagName, contentKeys));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
return wrapWithTag(EscapeUtil.escapeXml(object.toString()), tagName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加标签
|
||||
*
|
||||
* @param sb XML内容
|
||||
* @param tagName 标签名
|
||||
* @param isEndTag 是否结束标签
|
||||
* @since 5.7.11
|
||||
*/
|
||||
private static void appendTag(StringBuilder sb, String tagName, boolean isEndTag) {
|
||||
if (StrUtil.isNotBlank(tagName)) {
|
||||
sb.append('<');
|
||||
if (isEndTag) {
|
||||
sb.append('/');
|
||||
}
|
||||
sb.append(tagName).append('>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将内容使用标签包装为XML
|
||||
*
|
||||
* @param tagName 标签名
|
||||
* @param content 内容
|
||||
* @return 包装后的XML
|
||||
* @since 5.7.11
|
||||
*/
|
||||
private static String wrapWithTag(String content, String tagName) {
|
||||
if (StrUtil.isBlank(tagName)) {
|
||||
return StrUtil.wrap(content, "\"");
|
||||
}
|
||||
|
||||
if (StrUtil.isEmpty(content)) {
|
||||
return "<" + tagName + "/>";
|
||||
} else {
|
||||
return "<" + tagName + ">" + content + "</" + tagName + ">";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* JSON与XML相互转换封装,基于json.org官方库改造
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
package aiyh.utils.tool.cn.hutool.json.xml;
|
|
@ -91,8 +91,7 @@ public class CaElectronicSignatureAction extends SafeCusBaseAction {
|
|||
Map<String, Object> responseMap = responeVo.getResponseMap();
|
||||
Map<String, Object> data = (Map<String, Object>) responseMap.get("data");
|
||||
if (Objects.isNull(data)) {
|
||||
Util.actionFail(requestInfo.getRequestManager(), URLDecoder.decode(Util.null2String(responseMap.get("ret_msg")), "UTF-8"));
|
||||
return;
|
||||
Util.actionFailException(requestInfo.getRequestManager(), URLDecoder.decode(Util.null2String(responseMap.get("ret_msg")), "UTF-8"));
|
||||
}
|
||||
String documentNo = Util.null2String(data.get("document_no"));
|
||||
String pdf = Util.null2String(data.get("ofd"));
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <h1>获取关键字页码</h1>
|
||||
|
@ -28,7 +29,11 @@ public class GetOfdKeywordPageValue implements CusInterfaceGetValue {
|
|||
@Override
|
||||
public Object execute(Map<String, Object> mainMap, Map<String, Object> detailMap, String currentValue, Map<String, String> pathParam) {
|
||||
try {
|
||||
DocImageInfo docImageInfo = Util.selectImageInfoByDocId(currentValue);
|
||||
List<DocImageInfo> docImageInfoList = Util.selectImageInfoByDocIds(currentValue);
|
||||
DocImageInfo docImageInfo = findMaxImageFileId(docImageInfoList);
|
||||
if (Objects.isNull(docImageInfo)) {
|
||||
throw new CustomerException("未找到实体文件数据!");
|
||||
}
|
||||
InputStream inputStream = ImageFileManager.getInputStreamById(docImageInfo.getImageFileId());
|
||||
String filePath = Util.createTempFile(inputStream, docImageInfo.getImageFileName(), "ofd");
|
||||
String keywordType = pathParam.get("keywordType");
|
||||
|
@ -47,14 +52,28 @@ public class GetOfdKeywordPageValue implements CusInterfaceGetValue {
|
|||
List<OFDReader.KeywordInfoRange> keywordInfos = reader.findKeywords();
|
||||
Files.delete(Paths.get(filePath));
|
||||
if (keywordInfos.isEmpty()) {
|
||||
throw new CustomerException("关键字定位异常!未找到关键字");
|
||||
throw new CustomerException("关键字定位异常!未找到关键字:" + keywordValue);
|
||||
} else {
|
||||
String pageNumber = keywordInfos.get(keywordInfos.size() - 1).getStart().getPageNumber();
|
||||
return Integer.parseInt(pageNumber) + 1;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("关键字定位异常: " + Util.getErrString(e));
|
||||
throw new CustomerException("关键字定位异常!", e);
|
||||
throw new CustomerException("关键字定位异常!");
|
||||
}
|
||||
}
|
||||
|
||||
public DocImageInfo findMaxImageFileId(List<DocImageInfo> list) {
|
||||
DocImageInfo maxImageFileIdObj = null;
|
||||
Integer maxImageFileId = null;
|
||||
|
||||
for (DocImageInfo obj : list) {
|
||||
if (maxImageFileId == null || obj.getImageFileId() > maxImageFileId) {
|
||||
maxImageFileId = obj.getImageFileId();
|
||||
maxImageFileIdObj = obj;
|
||||
}
|
||||
}
|
||||
|
||||
return maxImageFileIdObj;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package weaver.youhong.ai.intellectualproperty.util;
|
||||
|
||||
import lombok.ToString;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
@ -98,6 +99,7 @@ public class OFDReader {
|
|||
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream);
|
||||
NodeList textNodes = doc.getElementsByTagName("ofd:TextObject");
|
||||
int startK = 0;
|
||||
KeywordInfo startNode = null;
|
||||
List<KeywordInfo> keywordInfos = new ArrayList<>();
|
||||
for (int i = 0; i < textNodes.getLength(); i++) {
|
||||
Node textNode = textNodes.item(i);
|
||||
|
@ -111,21 +113,24 @@ public class OFDReader {
|
|||
if (contentChar == keywordChar) {
|
||||
j++;
|
||||
if (j == chars.length) {
|
||||
if (k == keywordChars.length - 1) {
|
||||
// 已经找到关键字
|
||||
startK = 0;
|
||||
KeywordInfo keywordNode = createKeywordNode(pageFolder, keywordInfos, textNode);
|
||||
if (startNode != null) {
|
||||
keywordInfos.add(startNode);
|
||||
startNode = null;
|
||||
}
|
||||
keywordInfos.add(keywordNode);
|
||||
break;
|
||||
}
|
||||
startK = j;
|
||||
Node boundaryNode = textNode.getAttributes().getNamedItem("Boundary");
|
||||
String boundary = boundaryNode.getNodeValue();
|
||||
String[] boundarySegments = boundary.split(" ");
|
||||
double x = Double.parseDouble(boundarySegments[0]);
|
||||
double y = Double.parseDouble(boundarySegments[1]);
|
||||
double width = Double.parseDouble(boundarySegments[2]);
|
||||
double height = Double.parseDouble(boundarySegments[3]);
|
||||
KeywordInfo keywordInfo = new KeywordInfo(pageFolder, x, y, width, height);
|
||||
keywordInfos.add(keywordInfo);
|
||||
startNode = createKeywordNode(pageFolder, keywordInfos, textNode);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
startK = 0;
|
||||
keywordInfos.clear();
|
||||
startNode = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -138,6 +143,18 @@ public class OFDReader {
|
|||
return new KeywordInfoRange(keywordInfos.get(0), keywordInfos.get(keywordInfos.size() - 1));
|
||||
}
|
||||
|
||||
private static KeywordInfo createKeywordNode(String pageFolder, List<KeywordInfo> keywordInfos, Node textNode) {
|
||||
Node boundaryNode = textNode.getAttributes().getNamedItem("Boundary");
|
||||
String boundary = boundaryNode.getNodeValue();
|
||||
String[] boundarySegments = boundary.split(" ");
|
||||
double x = Double.parseDouble(boundarySegments[0]);
|
||||
double y = Double.parseDouble(boundarySegments[1]);
|
||||
double width = Double.parseDouble(boundarySegments[2]);
|
||||
double height = Double.parseDouble(boundarySegments[3]);
|
||||
return new KeywordInfo(pageFolder, x, y, width, height);
|
||||
}
|
||||
|
||||
@ToString
|
||||
public static class KeywordInfoRange {
|
||||
private final KeywordInfo start;
|
||||
|
||||
|
@ -157,6 +174,7 @@ public class OFDReader {
|
|||
}
|
||||
}
|
||||
|
||||
@ToString
|
||||
public static class KeywordInfo {
|
||||
private final String pageFolder;
|
||||
private final double x;
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
package youhong.ai.intellectualproperty;
|
||||
|
||||
import aiyh.utils.GenerateFileUtil;
|
||||
import aiyh.utils.Util;
|
||||
import basetest.BaseTest;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.junit.Test;
|
||||
import weaver.youhong.ai.intellectualproperty.action.CaElectronicSignatureAction;
|
||||
import weaver.youhong.ai.intellectualproperty.util.OFDReader;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <h1>测试</h1>
|
||||
|
@ -19,4 +27,38 @@ public class TestAction extends BaseTest {
|
|||
public void test() {
|
||||
GenerateFileUtil.createActionDocument(CaElectronicSignatureAction.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testet() throws Exception {
|
||||
String path = "/Users/aoey.oct.22/Downloads/关于反馈《中国(上海)自由贸易试验区十周年总结报告》征求意见的复函(5.29).ofd";
|
||||
OFDReader ofdReader = new OFDReader(path, "@Signature_position@");
|
||||
List<OFDReader.KeywordInfoRange> keywords = ofdReader.findKeywords();
|
||||
System.out.println(keywords);
|
||||
Map<String, String> stringStringMap = Util.parseCusInterfacePathParam("weaver.youhong.ai.intellectualproperty.cusgetvalue.GetOfdKeywordPageValue?keywordType=0&keyword=`@Signature_position@`");
|
||||
System.out.println(JSON.toJSONString(stringStringMap));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
|
||||
public void tesetet() {
|
||||
|
||||
String longString = "";
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader("/Users/aoey.oct.22/Downloads/base64.txt"))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
longString += line;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
byte[] decodedBytes = Base64.getDecoder().decode(longString); // 解码为字节数组
|
||||
try (OutputStream outputStream = new FileOutputStream("/Users/aoey.oct.22/Downloads/example.ofd")) {
|
||||
outputStream.write(decodedBytes); // 将字节数组写入文件
|
||||
System.out.println("文件已写入成功!");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public class PDFODFTest extends BaseTest {
|
|||
|
||||
@Test
|
||||
public void tesetOfdKeyWorkdasdf() throws Exception {
|
||||
weaver.youhong.ai.intellectualproperty.util.OFDReader reader = new OFDReader("/Users/aoey.oct.22/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/2.0b4.0.9/44e92f459afe6c0b31d119efb268a257/Message/MessageTemp/65ac172b9cb23c7967462468b4b5be81/File/1684915381834-ofd-4e579378-0925-43b3-9f89-89b661772521测试文档01.ofd", "签章位置001");
|
||||
weaver.youhong.ai.intellectualproperty.util.OFDReader reader = new OFDReader("/Users/aoey.oct.22/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/2.0b4.0.9/44e92f459afe6c0b31d119efb268a257/Message/MessageTemp/65ac172b9cb23c7967462468b4b5be81/File/2b6319c0-3395-4a74-8062-07bc2089c607.ofd", "签章位置001");
|
||||
System.out.println(JSON.toJSONString(reader.findKeywords()));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue