This commit is contained in:
Looly
2024-09-23 15:08:20 +08:00
parent ff187f6fde
commit 2872f15e91
49 changed files with 796 additions and 600 deletions

View File

@@ -16,24 +16,23 @@
package org.dromara.hutool.json;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.bean.copier.CopyOptions;
import org.dromara.hutool.core.codec.binary.HexUtil;
import org.dromara.hutool.core.convert.ConvertUtil;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.core.map.CaseInsensitiveLinkedMap;
import org.dromara.hutool.core.map.CaseInsensitiveTreeMap;
import org.dromara.hutool.core.math.NumberUtil;
import org.dromara.hutool.core.text.CharUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.split.SplitUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.mapper.JSONValueMapper;
import org.dromara.hutool.json.serializer.JSONMapper;
import org.dromara.hutool.json.reader.JSONTokener;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* 内部JSON工具类仅用于JSON内部使用
@@ -88,65 +87,6 @@ public final class InternalJSONUtil {
return string;
}
/**
* 值转为String用于JSON中。规则为
* <ul>
* <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(final Object value) throws JSONException {
if (value == null) {
return StrUtil.NULL;
}
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) {
final Map<?, ?> map = (Map<?, ?>) value;
return JSONUtil.parseObj(map).toString();
} else if (value instanceof Collection) {
final Collection<?> coll = (Collection<?>) value;
return JSONUtil.parseArray(coll).toString();
} else if (ArrayUtil.isArray(value)) {
return JSONUtil.parseArray(value).toString();
} else {
return quote(value.toString());
}
}
/**
* 将Property的键转化为JSON形式<br>
* 用于识别类似于org.dromara.hutool.json这类用点隔开的键<br>
* 注意:不允许重复键
*
* @param jsonObject JSONObject
* @param key 键
* @param value 值
*/
public static void propertyPut(final JSONObject jsonObject, final Object key, final Object value) {
final String[] path = SplitUtil.splitToArray(ConvertUtil.toStr(key), StrUtil.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.config());
target.set(segment, nextTarget);
}
target = nextTarget;
}
target.set(path[last], value);
}
/**
* 默认情况下是否忽略null值的策略选择以下对象不忽略null值其它对象忽略
*
@@ -179,7 +119,7 @@ public final class InternalJSONUtil {
.setIgnoreError(config.isIgnoreError())
.setIgnoreNullValue(config.isIgnoreNullValue())
.setTransientSupport(config.isTransientSupport())
.setConverter((targetType, value) -> JSONValueMapper.of(config, null).map(value));
.setConverter((targetType, value) -> JSONMapper.of(config, null).map(value));
}
/**

View File

@@ -18,9 +18,10 @@ package org.dromara.hutool.json;
import org.dromara.hutool.core.bean.path.BeanPath;
import org.dromara.hutool.core.lang.mutable.MutableEntry;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.serializer.JSONDeserializer;
import org.dromara.hutool.json.serializer.JSONMapper;
import org.dromara.hutool.json.serializer.TypeAdapterManager;
import org.dromara.hutool.json.support.JSONNodeBeanCreator;
import org.dromara.hutool.json.writer.JSONWriter;
import java.io.Serializable;
@@ -138,7 +139,7 @@ public interface JSON extends Serializable {
* @param value 值
*/
default void putByPath(final String expression, final Object value) {
BeanPath.of(expression).setValue(this, value);
BeanPath.of(expression).setBeanCreator(new JSONNodeBeanCreator(config())).setValue(this, value);
}
/**
@@ -228,33 +229,8 @@ public interface JSON extends Serializable {
* @param <T> Bean类型
* @param type {@link Type}
* @return 实体类对象
* @since 4.3.2
*/
@SuppressWarnings("unchecked")
default <T> T toBean(final Type type) {
if(null == type || Object.class == type){
if(this instanceof JSONPrimitive){
return (T) ((JSONPrimitive) this).getValue();
}
return (T) this;
}
final JSONDeserializer<Object> deserializer = TypeAdapterManager.getInstance().getDeserializer(this, type);
final boolean ignoreError = ObjUtil.defaultIfNull(config(), JSONConfig::isIgnoreError, false);
if(null == deserializer){
if(ignoreError){
return null;
}
throw new JSONException("No deserializer for type: " + type);
}
try{
return (T) deserializer.deserialize(this, type);
} catch (final Exception e){
if(ignoreError){
return null;
}
throw e;
}
return JSONMapper.of(config(), null).toBean(this, type);
}
}

View File

@@ -23,7 +23,7 @@ import org.dromara.hutool.core.convert.impl.ArrayConverter;
import org.dromara.hutool.core.lang.Validator;
import org.dromara.hutool.core.lang.mutable.MutableEntry;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.mapper.JSONValueMapper;
import org.dromara.hutool.json.serializer.JSONMapper;
import org.dromara.hutool.json.writer.JSONWriter;
import java.util.*;
@@ -52,7 +52,7 @@ public class JSONArray extends ListWrapper<JSON> implements JSON, JSONGetter<Int
* 配置项
*/
private final JSONConfig config;
private final JSONValueMapper mapper;
private final JSONMapper mapper;
// region ----- Constructors
@@ -97,7 +97,7 @@ public class JSONArray extends ListWrapper<JSON> implements JSON, JSONGetter<Int
public JSONArray(final int initialCapacity, final JSONConfig config) {
super(new ArrayList<>(initialCapacity));
this.config = ObjUtil.defaultIfNull(config, JSONConfig::of);
this.mapper = JSONValueMapper.of(config, null);
this.mapper = JSONMapper.of(config, null);
}
// endregion

View File

@@ -25,7 +25,7 @@ import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.map.MapWrapper;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.mapper.JSONValueMapper;
import org.dromara.hutool.json.serializer.JSONMapper;
import org.dromara.hutool.json.writer.JSONWriter;
import java.util.Arrays;
@@ -53,7 +53,7 @@ public class JSONObject extends MapWrapper<String, JSON> implements JSON, JSONGe
* 配置项
*/
private final JSONConfig config;
private final JSONValueMapper mapper;
private final JSONMapper mapper;
/**
* 构造,初始容量为 {@link #DEFAULT_CAPACITY}KEY有序
@@ -81,7 +81,7 @@ public class JSONObject extends MapWrapper<String, JSON> implements JSON, JSONGe
public JSONObject(final int capacity, final JSONConfig config) {
super(InternalJSONUtil.createRawMap(capacity, config));
this.config = ObjUtil.defaultIfNull(config, JSONConfig::of);
this.mapper = JSONValueMapper.of(config, null);
this.mapper = JSONMapper.of(config, null);
}
@Override

View File

@@ -23,7 +23,8 @@ import org.dromara.hutool.core.lang.mutable.MutableEntry;
import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.mapper.JSONValueMapper;
import org.dromara.hutool.json.serializer.JSONMapper;
import org.dromara.hutool.json.support.JSONStrFormatter;
import org.dromara.hutool.json.writer.JSONWriter;
import org.dromara.hutool.json.xml.JSONXMLUtil;
@@ -160,8 +161,8 @@ public class JSONUtil {
*/
public static JSONArray parseArray(final Object arrayOrCollection, final JSONConfig config, final Predicate<MutableEntry<Object, Object>> predicate) {
if(arrayOrCollection instanceof JSONObject){
final JSONValueMapper jsonValueMapper = JSONValueMapper.of(config, predicate);
return jsonValueMapper.mapFromJSONObject((JSONObject) arrayOrCollection);
final JSONMapper jsonMapper = JSONMapper.of(config, predicate);
return jsonMapper.mapFromJSONObject((JSONObject) arrayOrCollection);
}
return (JSONArray) parse(arrayOrCollection, config, predicate);
}
@@ -214,11 +215,11 @@ public class JSONUtil {
* @return JSONJSONObject or JSONArray
*/
public static JSON parse(final Object obj, final JSONConfig config, final Predicate<MutableEntry<Object, Object>> predicate) {
final JSONValueMapper jsonValueMapper = JSONValueMapper.of(config, predicate);
final JSONMapper jsonMapper = JSONMapper.of(config, predicate);
if (obj instanceof CharSequence) {
return jsonValueMapper.map((CharSequence) obj);
return jsonMapper.map((CharSequence) obj);
}
return jsonValueMapper.map(obj);
return jsonMapper.map(obj);
}
/**

View File

@@ -1,104 +0,0 @@
/*
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.json.mapper;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.lang.mutable.MutableEntry;
import org.dromara.hutool.json.JSONArray;
import org.dromara.hutool.json.JSONConfig;
import org.dromara.hutool.json.JSONException;
import org.dromara.hutool.json.reader.JSONParser;
import org.dromara.hutool.json.reader.JSONTokener;
import java.util.function.Predicate;
/**
* 对象和JSONArray映射器用于转换对象为JSONArray支持
* <ul>
* <li>JSONTokener 转 JSONArray直接解析</li>
* <li>Iterable 转 JSONArray</li>
* <li>Iterator 转 JSONArray</li>
* <li>数组 转 JSONArray</li>
* </ul>
*
* @author looly
* @since 6.0.0
*/
class JSONArrayMapper {
/**
* 创建ArrayMapper
*
* @param source 来源对象
* @param predicate 值过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤,,{@link Predicate#test(Object)}为{@code true}保留
* @return ObjectMapper
*/
public static JSONArrayMapper of(final Object source, final Predicate<MutableEntry<Object, Object>> predicate) {
return new JSONArrayMapper(source, predicate);
}
private final Object source;
private final Predicate<MutableEntry<Object, Object>> predicate;
/**
* 构造
*
* @param source 来源对象
* @param predicate 值过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤,,{@link Predicate#test(Object)}为{@code true}保留
*/
public JSONArrayMapper(final Object source, final Predicate<MutableEntry<Object, Object>> predicate) {
this.source = source;
this.predicate = predicate;
}
/**
* 将给定对象转换为{@link JSONArray}
*
* @param jsonArray 目标{@link JSONArray}
* @throws JSONException 非数组或集合
*/
public void mapTo(final JSONArray jsonArray) throws JSONException {
final Object source = this.source;
if (null == source) {
return;
}
if (source instanceof byte[]) {
final byte[] bytesSource = (byte[]) source;
if ('[' == bytesSource[0] && ']' == bytesSource[bytesSource.length - 1]) {
mapFromTokener(new JSONTokener(IoUtil.toStream(bytesSource)), jsonArray.config(), jsonArray);
} else {
// https://github.com/dromara/hutool/issues/2369
// 非标准的二进制流,则按照普通数组对待
for (final byte b : bytesSource) {
jsonArray.set(b);
}
}
}
throw new JSONException("Unsupported [{}] to JSONArray", source.getClass());
}
/**
* 从JSONTokener中读取数据并添加到JSONArray中
*
* @param x {@link JSONTokener}
* @param jsonArray {@link JSONArray}
*/
private void mapFromTokener(final JSONTokener x, final JSONConfig config, final JSONArray jsonArray) {
JSONParser.of(x, config).setPredicate(this.predicate).parseTo(jsonArray);
}
}

View File

@@ -1,122 +0,0 @@
/*
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.json.mapper;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.lang.mutable.MutableEntry;
import org.dromara.hutool.json.InternalJSONUtil;
import org.dromara.hutool.json.JSONConfig;
import org.dromara.hutool.json.JSONException;
import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.reader.JSONParser;
import org.dromara.hutool.json.reader.JSONTokener;
import java.util.Enumeration;
import java.util.ResourceBundle;
import java.util.function.Predicate;
/**
* 对象和JSONObject映射器用于转换对象为JSONObject支持
* <ul>
* <li>Map 转 JSONObject将键值对加入JSON对象</li>
* <li>Map.Entry 转 JSONObject</li>
* <li>JSONTokener 转 JSONObject直接解析</li>
* <li>ResourceBundle 转 JSONObject</li>
* <li>Bean 转 JSONObject调用其getters方法getXXX或者isXXX获得值加入到JSON对象。例如如果JavaBean对象中有个方法getName(),值为"张三"获得的键值对为name: "张三"</li>
* </ul>
*
* @author looly
*/
class JSONObjectMapper {
/**
* 创建ObjectMapper
*
* @param source 来源对象
* @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留
* @return ObjectMapper
*/
public static JSONObjectMapper of(final Object source, final Predicate<MutableEntry<Object, Object>> predicate) {
return new JSONObjectMapper(source, predicate);
}
private final Object source;
private final Predicate<MutableEntry<Object, Object>> predicate;
/**
* 构造
*
* @param source 来源对象
* @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留
*/
public JSONObjectMapper(final Object source, final Predicate<MutableEntry<Object, Object>> predicate) {
this.source = source;
this.predicate = predicate;
}
/**
* 将给定对象转换为{@link JSONObject}
*
* @param jsonObject 目标{@link JSONObject}
*/
public void mapTo(final JSONObject jsonObject) {
final Object source = this.source;
if (null == source) {
return;
}
if (source instanceof byte[]) {
mapFromTokener(new JSONTokener(IoUtil.toStream((byte[]) source)), jsonObject.config(), jsonObject);
} else if (source instanceof ResourceBundle) {
// ResourceBundle
mapFromResourceBundle((ResourceBundle) source, jsonObject);
} else {
if (!jsonObject.config().isIgnoreError()) {
// 不支持对象类型转换为JSONObject
throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass());
}
}
// 如果用户选择跳过异常,则跳过此值转换
}
/**
* 从{@link ResourceBundle}转换
*
* @param bundle ResourceBundle
* @param jsonObject {@link JSONObject}
*/
private void mapFromResourceBundle(final ResourceBundle bundle, final JSONObject jsonObject) {
final Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
final String key = keys.nextElement();
if (key != null) {
InternalJSONUtil.propertyPut(jsonObject, key, bundle.getString(key));
}
}
}
/**
* 从{@link JSONTokener}转换
*
* @param x JSONTokener
* @param config JSON配置
* @param jsonObject {@link JSONObject}
*/
private void mapFromTokener(final JSONTokener x, final JSONConfig config, final JSONObject jsonObject) {
JSONParser.of(x, config).setPredicate(this.predicate).parseTo(jsonObject);
}
}

View File

@@ -1,199 +0,0 @@
/*
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.json.mapper;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.lang.Opt;
import org.dromara.hutool.core.lang.mutable.MutableEntry;
import org.dromara.hutool.core.map.MapWrapper;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.*;
import org.dromara.hutool.json.reader.JSONParser;
import org.dromara.hutool.json.reader.JSONTokener;
import org.dromara.hutool.json.serializer.JSONSerializer;
import org.dromara.hutool.json.serializer.SimpleJSONContext;
import org.dromara.hutool.json.serializer.TypeAdapterManager;
import org.dromara.hutool.json.xml.JSONXMLParser;
import org.dromara.hutool.json.xml.ParseConfig;
import java.io.InputStream;
import java.io.Reader;
import java.io.Serializable;
import java.util.Optional;
import java.util.function.Predicate;
/**
* 对象和JSON值映射器用于转换对象为JSON对象中的值<br>
* 有效的JSON值包括
* <ul>
* <li>JSONObject</li>
* <li>JSONArray</li>
* <li>String</li>
* <li>数字int、long等</li>
* <li>Boolean值如true或false</li>
* <li>{@code null}</li>
* </ul>
*
* @author looly
* @since 6.0.0
*/
public class JSONValueMapper implements Serializable {
private static final long serialVersionUID = -6714488573738940582L;
/**
* 创建ObjectMapper
*
* @param jsonConfig 来源对象
* @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留
* @return ObjectMapper
*/
public static JSONValueMapper of(final JSONConfig jsonConfig, final Predicate<MutableEntry<Object, Object>> predicate) {
return new JSONValueMapper(jsonConfig, predicate);
}
private final JSONConfig jsonConfig;
private final Predicate<MutableEntry<Object, Object>> predicate;
/**
* 构造
*
* @param jsonConfig JSON配置
* @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留
*/
public JSONValueMapper(final JSONConfig jsonConfig, final Predicate<MutableEntry<Object, Object>> predicate) {
this.jsonConfig = ObjUtil.defaultIfNull(jsonConfig, JSONConfig::of);
this.predicate = predicate;
}
/**
* 将JSONObject转换为JSONArray
*
* @param jsonObject JSONObject
* @return JSONArray
*/
public JSONArray mapFromJSONObject(final JSONObject jsonObject){
final JSONArray array = new JSONArray(jsonConfig);
JSONArrayMapper.of(jsonObject, this.predicate)
.mapTo(array);
return array;
}
/**
* 解析JSON字符串或XML字符串为JSON结构
*
* @param source JSON字符串或XML字符串
* @return JSON对象
*/
public JSON map(final CharSequence source) {
final String jsonStr = StrUtil.trim(source);
if (StrUtil.startWith(jsonStr, '<')) {
// 可能为XML
final JSONObject jsonObject = new JSONObject(jsonConfig);
JSONXMLParser.of(ParseConfig.of(), this.predicate).parseJSONObject(jsonStr, jsonObject);
return jsonObject;
}
return mapFromTokener(new JSONTokener(source));
}
/**
* 在需要的时候转换映射对象<br>
* 包装包括:
* <ul>
* <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 obj 被映射的对象
* @return 映射后的值null表示此值需被忽略
*/
public JSON map(Object obj) {
if (null == obj) {
return null;
}
if (obj instanceof Optional) {
obj = ((Optional<?>) obj).orElse(null);
} else if (obj instanceof Opt) {
obj = ((Opt<?>) obj).getOrNull();
}
if (obj instanceof JSON) {
return (JSON) obj;
}
// 读取JSON流
if(obj instanceof JSONTokener){
return mapFromTokener((JSONTokener) obj);
}else if(obj instanceof JSONParser){
return ((JSONParser)obj).parse();
} else if (obj instanceof Reader) {
return mapFromTokener(new JSONTokener((Reader) obj));
} else if (obj instanceof InputStream) {
return mapFromTokener(new JSONTokener((InputStream) obj));
}
// 自定义序列化
final JSONSerializer<Object> serializer = TypeAdapterManager.getInstance()
.getSerializer(obj, obj.getClass());
if (null != serializer) {
return serializer.serialize(obj, new SimpleJSONContext(null, this.jsonConfig));
}
// 原始类型
if (JSONPrimitive.isTypeForJSONPrimitive(obj)) {
return new JSONPrimitive(obj, jsonConfig);
}
// 特定对象转换
try {
// JSONArray
if (// MapWrapper实现了Iterable会被当作JSONArray此处做修正
!(obj instanceof MapWrapper) &&
(obj instanceof Iterable || ArrayUtil.isArray(obj))) {
final JSONArray jsonArray = new JSONArray(jsonConfig);
JSONArrayMapper.of(obj, predicate).mapTo(jsonArray);
return jsonArray;
}
// 默认按照JSONObject对待
final JSONObject jsonObject = new JSONObject(jsonConfig);
JSONObjectMapper.of(obj, predicate).mapTo(jsonObject);
return jsonObject;
} catch (final Exception exception) {
if (jsonConfig.isIgnoreError()) {
return null;
}
throw exception instanceof JSONException ? (JSONException) exception : new JSONException(exception);
}
}
/**
* 从{@link JSONTokener} 中读取JSON字符串并转换为JSON
*
* @param tokener {@link JSONTokener}
* @return JSON
*/
private JSON mapFromTokener(final JSONTokener tokener) {
return JSONParser.of(tokener, jsonConfig).setPredicate(this.predicate).parse();
}
}

View File

@@ -1,25 +0,0 @@
/*
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Bean和JSON之间的映射封装包括
* <ul>
* <li>JSONObjectMapper: 转换对象为JSONObject</li>
* <li>JSONArrayMapper: 转换对象为JSONArray</li>
* <li>JSONValueMapper: 转换对象为JSON值</li>
* </ul>
*/
package org.dromara.hutool.json.mapper;

View File

@@ -26,9 +26,9 @@
* JSON封装主要包括JSON表示和JSON转换
*
* <pre>{@code
* <--JSONConverter-- <---JSONParser----
* Java对象 <=================> JSON对象 <=================> JSON字符串
* ------mapper-----> ---JSONWriter---->
* <-----JSONMapper----- <---JSONParser----
* Java对象 <====================> JSON对象 <=================> JSON字符串
* -----JSONMapper-----> ---JSONWriter---->
* }</pre>
*
* 当然为了高效转换如果没有自定义需求Java对象可以不通过JSON对象与JSON字符串转换

View File

@@ -0,0 +1,248 @@
/*
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.json.serializer;
import org.dromara.hutool.core.lang.Opt;
import org.dromara.hutool.core.lang.mutable.MutableEntry;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.*;
import org.dromara.hutool.json.reader.JSONParser;
import org.dromara.hutool.json.reader.JSONTokener;
import org.dromara.hutool.json.xml.JSONXMLParser;
import org.dromara.hutool.json.xml.ParseConfig;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
/**
* 对象和JSON值映射器用于Java对象和JSON对象互转<br>
* <ul>
* <li>Java对象转JSON{@link #map(Object)}</li>
* <li>JSON转Java对象{@link #toBean(JSON, Type)}</li>
* </ul>
* <p>
* 转换依赖于{@link JSONSerializer}和{@link JSONDeserializer}的实现,通过{@link TypeAdapterManager}统一管理<br>
* 序列化和反序列化定义于两个作用域,首先查找本类中定义的,如果没有,使用{@link TypeAdapterManager#getInstance()} 查找全局定义的。
*
* @author looly
* @since 6.0.0
*/
public class JSONMapper implements Serializable {
private static final long serialVersionUID = -6714488573738940582L;
/**
* 创建ObjectMapper
*
* @param jsonConfig 来源对象
* @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留
* @return ObjectMapper
*/
public static JSONMapper of(final JSONConfig jsonConfig, final Predicate<MutableEntry<Object, Object>> predicate) {
return new JSONMapper(jsonConfig, predicate);
}
private final JSONConfig jsonConfig;
private final Predicate<MutableEntry<Object, Object>> predicate;
private TypeAdapterManager typeAdapterManager;
/**
* 构造
*
* @param jsonConfig JSON配置
* @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留
*/
public JSONMapper(final JSONConfig jsonConfig, final Predicate<MutableEntry<Object, Object>> predicate) {
this.jsonConfig = ObjUtil.defaultIfNull(jsonConfig, JSONConfig::of);
this.predicate = predicate;
}
/**
* 获取自定义类型转换器用于将自定义类型转换为JSONObject
*
* @return 类型转换器管理器
*/
public TypeAdapterManager getTypeAdapterManager() {
return this.typeAdapterManager;
}
/**
* 设置自定义类型转换器用于将自定义类型转换为JSONObject
*
* @param typeAdapterManager 类型转换器管理器
* @return this
*/
public JSONMapper setTypeAdapterManager(final TypeAdapterManager typeAdapterManager) {
this.typeAdapterManager = typeAdapterManager;
return this;
}
/**
* 转为实体类对象
*
* @param <T> Bean类型
* @param json JSON
* @param type {@link Type}
* @return 实体类对象
*/
@SuppressWarnings("unchecked")
public <T> T toBean(final JSON json, final Type type) {
if (null == type || Object.class == type) {
if (json instanceof JSONPrimitive) {
return (T) ((JSONPrimitive) json).getValue();
}
return (T) this;
}
JSONDeserializer<Object> deserializer = null;
// 自定义反序列化
if (null != this.typeAdapterManager) {
deserializer = this.typeAdapterManager.getDeserializer(json, type);
}
// 全局自定义反序列化
if (null == deserializer) {
deserializer = TypeAdapterManager.getInstance().getDeserializer(json, type);
}
final boolean ignoreError = ObjUtil.defaultIfNull(this.jsonConfig, JSONConfig::isIgnoreError, false);
if (null == deserializer) {
if (ignoreError) {
return null;
}
throw new JSONException("No deserializer for type: " + type);
}
try {
return (T) deserializer.deserialize(json, type);
} catch (final Exception e) {
if (ignoreError) {
return null;
}
throw e;
}
}
/**
* 将JSONObject转换为JSONArrayM<br>
* 在普通的Serializer中JSONObject会被直接返回如果想转为JSONArray
*
* @param jsonObject JSONObject
* @return JSONArray
*/
public JSONArray mapFromJSONObject(final JSONObject jsonObject) {
final JSONArray array = JSONUtil.ofArray(jsonConfig);
for (final Map.Entry<String, JSON> entry : jsonObject) {
array.add(entry.getValue());
}
return array;
}
/**
* 解析JSON字符串或XML字符串为JSON结构<br>
* 在普通的Serializer中字符串会被作为普通字符串转为{@link JSONPrimitive},此处做区分
*
* @param source JSON字符串或XML字符串
* @return JSON对象
*/
public JSON map(final CharSequence source) {
final String jsonStr = StrUtil.trim(source);
if (StrUtil.startWith(jsonStr, '<')) {
// 可能为XML
final JSONObject jsonObject = JSONUtil.ofObj(jsonConfig);
JSONXMLParser.of(ParseConfig.of(), this.predicate).parseJSONObject(jsonStr, jsonObject);
return jsonObject;
}
return mapFromTokener(new JSONTokener(source));
}
/**
* 在需要的时候转换映射对象<br>
* 包装包括:
* <ul>
* <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 obj 被映射的对象
* @return 映射后的值null表示此值需被忽略
*/
public JSON map(Object obj) {
if (null == obj) {
return null;
}
if (obj instanceof Optional) {
obj = ((Optional<?>) obj).orElse(null);
if (null == obj) {
return null;
}
} else if (obj instanceof Opt) {
obj = ((Opt<?>) obj).getOrNull();
if (null == obj) {
return null;
}
}
if (obj instanceof JSON) {
return (JSON) obj;
}
final Class<?> clazz = obj.getClass();
JSONSerializer<Object> serializer = null;
// 自定义序列化
if (null != this.typeAdapterManager) {
serializer = this.typeAdapterManager.getSerializer(obj, clazz);
}
// 全局自定义序列化
if (null == serializer) {
serializer = TypeAdapterManager.getInstance().getSerializer(obj, clazz);
}
final boolean ignoreError = ObjUtil.defaultIfNull(this.jsonConfig, JSONConfig::isIgnoreError, false);
if (null == serializer) {
if (ignoreError) {
return null;
}
throw new JSONException("No deserializer for type: " + obj.getClass());
}
try {
return serializer.serialize(obj, new SimpleJSONContext(null, this.jsonConfig));
} catch (final Exception e) {
if (ignoreError) {
return null;
}
throw e;
}
}
/**
* 从{@link JSONTokener} 中读取JSON字符串并转换为JSON
*
* @param tokener {@link JSONTokener}
* @return JSON
*/
private JSON mapFromTokener(final JSONTokener tokener) {
return JSONParser.of(tokener, jsonConfig).setPredicate(this.predicate).parse();
}
}

View File

@@ -284,6 +284,8 @@ public class TypeAdapterManager {
private static TypeAdapterManager registerDefault(final TypeAdapterManager manager) {
// 自定义序列化器
manager.register(ResourceBundleSerializer.INSTANCE);
manager.register(TokenerSerializer.INSTANCE);
// 自定义反序列化器
manager.register(KBeanDeserializer.INSTANCE);

View File

@@ -18,10 +18,11 @@ package org.dromara.hutool.json.serializer.impl;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.collection.iter.ArrayIter;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONArray;
import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.*;
import org.dromara.hutool.json.reader.JSONParser;
import org.dromara.hutool.json.reader.JSONTokener;
import org.dromara.hutool.json.serializer.JSONContext;
import org.dromara.hutool.json.serializer.MatcherJSONDeserializer;
import org.dromara.hutool.json.serializer.MatcherJSONSerializer;
@@ -58,6 +59,9 @@ public class ArrayTypeAdapter implements MatcherJSONSerializer<Object>, MatcherJ
@Override
public JSON serialize(final Object bean, final JSONContext context) {
if (bean instanceof byte[]) {
return serializeBytes((byte[]) bean, context);
}
return IterTypeAdapter.INSTANCE.serialize(new ArrayIter<>(bean), context);
}
@@ -74,6 +78,36 @@ public class ArrayTypeAdapter implements MatcherJSONSerializer<Object>, MatcherJ
return result;
}
/**
* 序列化二进制流为JSON
*
* @param bytes 二进制流
* @param context JSON上下文
* @return JSONArray
*/
private JSON serializeBytes(final byte[] bytes, final JSONContext context) {
final JSONConfig config = context.config();
switch (bytes[0]) {
case '{':
case '[':
return JSONParser.of(new JSONTokener(IoUtil.toStream(bytes)), config).parse();
}
// https://github.com/dromara/hutool/issues/2369
// 非标准的二进制流,则按照普通数组对待
final JSONArray result;
final JSON contextJson = context.getContextJson();
if (contextJson instanceof JSONArray) {
result = (JSONArray) contextJson;
} else {
result = JSONUtil.ofArray(config);
}
for (final byte b : bytes) {
result.set(b);
}
return result;
}
/**
* 将JSONObject填充到数组
*

View File

@@ -26,7 +26,7 @@ import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.InternalJSONUtil;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.convert.JSONObjectValueProvider;
import org.dromara.hutool.json.support.JSONObjectValueProvider;
import org.dromara.hutool.json.serializer.JSONContext;
import org.dromara.hutool.json.serializer.MatcherJSONDeserializer;
import org.dromara.hutool.json.serializer.MatcherJSONSerializer;

View File

@@ -17,6 +17,7 @@
package org.dromara.hutool.json.serializer.impl;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.map.MapWrapper;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.JSON;
@@ -46,6 +47,9 @@ public class IterTypeAdapter implements MatcherJSONSerializer<Object>, MatcherJS
@Override
public boolean match(final Object bean, final JSONContext context) {
if(bean instanceof MapWrapper){
return false;
}
return bean instanceof Iterable || bean instanceof Iterator;
}

View File

@@ -21,7 +21,7 @@ import org.dromara.hutool.core.reflect.kotlin.KClassUtil;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONGetter;
import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.convert.JSONObjectValueProvider;
import org.dromara.hutool.json.support.JSONObjectValueProvider;
import org.dromara.hutool.json.serializer.MatcherJSONDeserializer;
import java.lang.reflect.Type;

View File

@@ -20,7 +20,7 @@ import org.dromara.hutool.core.bean.RecordUtil;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.convert.JSONObjectValueProvider;
import org.dromara.hutool.json.support.JSONObjectValueProvider;
import org.dromara.hutool.json.serializer.MatcherJSONDeserializer;
import java.lang.reflect.Type;

View File

@@ -0,0 +1,104 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.json.serializer.impl;
import org.dromara.hutool.core.convert.ConvertUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.split.SplitUtil;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.JSONUtil;
import org.dromara.hutool.json.serializer.JSONContext;
import org.dromara.hutool.json.serializer.MatcherJSONSerializer;
import java.util.Enumeration;
import java.util.ResourceBundle;
/**
* {@link ResourceBundle}序列化器
*
* @author looly
* @since 6.0.0
*/
public class ResourceBundleSerializer implements MatcherJSONSerializer<ResourceBundle> {
/**
* 单例
*/
public static final ResourceBundleSerializer INSTANCE = new ResourceBundleSerializer();
@Override
public boolean match(final Object bean, final JSONContext context) {
return bean instanceof ResourceBundle;
}
@Override
public JSON serialize(final ResourceBundle bean, final JSONContext context) {
final JSONObject result;
final JSON json = context.getContextJson();
if (json instanceof JSONObject) {
result = (JSONObject) json;
} else {
result = JSONUtil.ofObj(context.config());
}
mapFromResourceBundle(bean, result);
return result;
}
/**
* 从{@link ResourceBundle}转换
*
* @param bundle ResourceBundle
* @param jsonObject {@link JSONObject}
*/
private void mapFromResourceBundle(final ResourceBundle bundle, final JSONObject jsonObject) {
final Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
final String key = keys.nextElement();
if (key != null) {
propertyPut(jsonObject, key, bundle.getString(key));
}
}
}
/**
* 将Property的键转化为JSON形式<br>
* 用于识别类似于org.dromara.hutool.json这类用点隔开的键<br>
* 注意:不允许重复键
*
* @param jsonObject JSONObject
* @param key 键
* @param value 值
*/
private static void propertyPut(final JSONObject jsonObject, final Object key, final Object value) {
final String[] path = SplitUtil.splitToArray(ConvertUtil.toStr(key), StrUtil.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 = JSONUtil.ofObj(target.config());
target.set(segment, nextTarget);
}
target = nextTarget;
}
target.set(path[last], value);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.json.serializer.impl;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONConfig;
import org.dromara.hutool.json.reader.JSONParser;
import org.dromara.hutool.json.reader.JSONTokener;
import org.dromara.hutool.json.serializer.JSONContext;
import org.dromara.hutool.json.serializer.MatcherJSONSerializer;
import java.io.InputStream;
import java.io.Reader;
/**
* JSONTokener及其读取流的JSON序列化器实现
*
* @author looly
* @since 6.0.0
*/
public class TokenerSerializer implements MatcherJSONSerializer<Object> {
/**
* 单例
*/
public static final TokenerSerializer INSTANCE = new TokenerSerializer();
@Override
public boolean match(final Object bean, final JSONContext context) {
return bean instanceof Reader
|| bean instanceof InputStream;
}
@Override
public JSON serialize(final Object bean, final JSONContext context) {
// 读取JSON流
if (bean instanceof JSONTokener) {
return mapFromTokener((JSONTokener) bean, context.config());
} else if (bean instanceof JSONParser) {
return ((JSONParser) bean).parse();
} else if (bean instanceof Reader) {
return mapFromTokener(new JSONTokener((Reader) bean), context.config());
} else if (bean instanceof InputStream) {
return mapFromTokener(new JSONTokener((InputStream) bean), context.config());
}
throw new IllegalArgumentException("Unsupported source: " + bean);
}
/**
* 从{@link JSONTokener} 中读取JSON字符串并转换为JSON
*
* @param tokener {@link JSONTokener}
* @return JSON
*/
private JSON mapFromTokener(final JSONTokener tokener, final JSONConfig config) {
return JSONParser.of(tokener, config).setPredicate(null).parse();
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.json.support;
import org.dromara.hutool.core.bean.path.BeanPath;
import org.dromara.hutool.core.bean.path.NodeBeanCreator;
import org.dromara.hutool.core.bean.path.node.NameNode;
import org.dromara.hutool.core.bean.path.node.Node;
import org.dromara.hutool.json.JSONConfig;
import org.dromara.hutool.json.JSONUtil;
/**
* JSON节点Bean创建器
*
* @author looly
* @since 6.0.0
*/
public class JSONNodeBeanCreator implements NodeBeanCreator {
private final JSONConfig config;
/**
* 构造
*
* @param config JSON配置
*/
public JSONNodeBeanCreator(final JSONConfig config) {
this.config = config;
}
@Override
public Object create(final Object parent, final BeanPath beanPath) {
final BeanPath next = beanPath.next();
if (null != next) {
final Node node = next.getNode();
if (node instanceof NameNode) {
final NameNode nameNode = (NameNode) node;
if (nameNode.isNumber()) {
return JSONUtil.ofArray(config);
}
return JSONUtil.ofObj(config);
}
}
return JSONUtil.ofObj(config);
}
}

View File

@@ -14,16 +14,18 @@
* limitations under the License.
*/
package org.dromara.hutool.json.convert;
package org.dromara.hutool.json.support;
import org.dromara.hutool.core.bean.copier.ValueProvider;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONObject;
import java.lang.reflect.Type;
/**
* JSONObject值提供者用于将JSONObject中的值注入Bean
* JSONObject值提供者用于将JSONObject中的值注入Bean<br>
* 兼容下划线模式的JSON转换为驼峰模式
*
* @author Looly
* @since 6.0.0
@@ -42,16 +44,19 @@ public class JSONObjectValueProvider implements ValueProvider<String> {
}
@Override
public Object value(final String key, final Type valueType) {
final JSON value = jsonObject.get(key);
if (null == value) {
return null;
}
return value.toBean(valueType);
public boolean containsKey(final String key) {
return jsonObject.containsKey(key) || jsonObject.containsKey(StrUtil.toUnderlineCase(key));
}
@Override
public boolean containsKey(final String key) {
return jsonObject.containsKey(key);
public Object value(final String key, final Type valueType) {
JSON value = jsonObject.get(key);
if (null == value) {
value = jsonObject.get(StrUtil.toUnderlineCase(key));
if(null == value){
return null;
}
}
return value.toBean(valueType);
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.dromara.hutool.json;
package org.dromara.hutool.json.support;
import org.dromara.hutool.core.text.CharUtil;
import org.dromara.hutool.core.text.StrUtil;

View File

@@ -15,9 +15,9 @@
*/
/**
* JSON对象值类型转换封装
* JSON的支持类如用于转换和BeanPath操作的对象还有用于格式化的对象等
*
* @author Looly
* @since 6.0.0
*/
package org.dromara.hutool.json.convert;
package org.dromara.hutool.json.support;