This commit is contained in:
Looly 2024-09-22 18:15:39 +08:00
parent 43a0201a20
commit ff187f6fde
44 changed files with 831 additions and 664 deletions

View File

@ -96,6 +96,7 @@ public class Pair<L, R> implements Serializable, Cloneable {
if (o == null || getClass() != o.getClass()) {
return false;
}
final Pair<?, ?> pair = (Pair<?, ?>) o;
return Objects.equals(left, pair.left) && Objects.equals(right, pair.right);
}

View File

@ -92,7 +92,7 @@ public class ActualTypeMapperPool {
}
/**
* 创建类中所有的泛型变量和泛型实际类型的对应关系Map
* 创建类中所有的泛型变量和泛型实际类型的对应关系Map<br>
*
* @param type 被解析的包含泛型参数的类
* @return 泛型对应关系Map

View File

@ -22,10 +22,10 @@ import java.lang.reflect.Type;
* Type类型参考<br>
* 通过构建一个类型参考子类可以获取其泛型参数中的Type类型例如
*
* <pre>
* TypeReference&lt;List&lt;String&gt;&gt; list = new TypeReference&lt;List&lt;String&gt;&gt;() {};
* <pre>{@code
* TypeReference<List<String>> list = new TypeReference<List<String>>() {};
* Type t = tr.getType();
* </pre>
* }</pre>
*
* 此类无法应用于通配符泛型参数wildcard parameters比如{@code Class<?>} 或者 {@code List? extends CharSequence>}
*

View File

@ -45,7 +45,10 @@ public class TypeUtil {
* @return 原始类如果无法获取原始类返回{@code null}
*/
public static Class<?> getClass(final Type type) {
if (null != type) {
if (null == type) {
return null;
}
if (type instanceof Class) {
return (Class<?>) type;
} else if (type instanceof ParameterizedType) {
@ -61,13 +64,13 @@ public class TypeUtil {
if (upperBounds.length == 1) {
return getClass(upperBounds[0]);
}
} else if(type instanceof GenericArrayType){
return Array.newInstance(getClass(((GenericArrayType)type).getGenericComponentType()), 0).getClass();
} else if(type instanceof TypeReference){
return getClass(((TypeReference<?>)type).getType());
} else if (type instanceof GenericArrayType) {
return Array.newInstance(getClass(((GenericArrayType) type).getGenericComponentType()), 0).getClass();
} else if (type instanceof TypeReference) {
return getClass(((TypeReference<?>) type).getType());
}
}
return null;
throw new IllegalArgumentException("Unsupported Type: " + type.getClass().getName());
}
/**
@ -107,7 +110,7 @@ public class TypeUtil {
return null == field ? null : field.getType();
}
// ----------------------------------------------------------------------------------- Param Type
// region ----- Param Type
/**
* 获取方法的第一个参数类型<br>
@ -190,8 +193,9 @@ public class TypeUtil {
public static Class<?>[] getParamClasses(final Method method) {
return null == method ? null : method.getParameterTypes();
}
// endregion
// ----------------------------------------------------------------------------------- Return Type
// region ----- Return Type
/**
* 获取方法的返回值类型<br>
@ -218,8 +222,9 @@ public class TypeUtil {
public static Class<?> getReturnClass(final Method method) {
return null == method ? null : method.getReturnType();
}
// endregion
// ----------------------------------------------------------------------------------- Type Argument
// region ----- Type Argument
/**
* 获得给定类的第一个泛型参数
@ -267,6 +272,7 @@ public class TypeUtil {
final ParameterizedType parameterizedType = toParameterizedType(type);
return (null == parameterizedType) ? null : parameterizedType.getActualTypeArguments();
}
// endregion
/**
* {@link Type} 转换为{@link ParameterizedType}<br>
@ -347,7 +353,7 @@ public class TypeUtil {
if (ArrayUtil.isNotEmpty(genericInterfaces)) {
for (final Type genericInterface : genericInterfaces) {
final ParameterizedType parameterizedType = toParameterizedType(genericInterface);
if(null != parameterizedType){
if (null != parameterizedType) {
result.add(parameterizedType);
}
}
@ -383,20 +389,7 @@ public class TypeUtil {
return false;
}
/**
* 获取泛型变量和泛型实际类型的对应关系Map例如
*
* <pre>
* T org.dromara.hutool.test.User
* E java.lang.Integer
* </pre>
*
* @param clazz 被解析的包含泛型参数的类
* @return 泛型对应关系Map
*/
public static Map<Type, Type> getTypeMap(final Class<?> clazz) {
return ActualTypeMapperPool.get(clazz);
}
// region ----- Actual type
/**
* 获得泛型字段对应的泛型实际类型如果此变量没有对应的实际类型返回null
@ -417,7 +410,7 @@ public class TypeUtil {
* 此方法可以处理
*
* <pre>
* 1. 泛型化对象类似于Map&lt;User, Key&lt;Long&gt;&gt;
* 1. 泛型化对象类似于{@code Map<User, Key<Long>>}
* 2. 泛型变量类似于T
* </pre>
*
@ -431,17 +424,30 @@ public class TypeUtil {
}
if (typeVariable instanceof TypeVariable) {
// TODO TypeReference无效
return ActualTypeMapperPool.getActualType(type, (TypeVariable<?>) typeVariable);
return getActualType(type, (TypeVariable<?>) typeVariable);
}
// 没有需要替换的泛型变量原样输出
return typeVariable;
}
/**
* 获得泛型变量对应的泛型实际类型如果此变量没有对应的实际类型返回typeVariable
*
* @param type
* @param typeVariable 泛型变量
* @return 实际类型可能为Class等
*/
public static Type getActualType(final Type type, final TypeVariable<?> typeVariable) {
return ObjUtil.defaultIfNull(ActualTypeMapperPool.getActualType(type, typeVariable), typeVariable);
}
/**
* 获得泛型变量对应的泛型实际类型如果此变量没有对应的实际类型返回null
* 此方法可以处理复杂的泛型化对象类似于Map&lt;User, Key&lt;Long&gt;&gt;
* 此方法可以处理复杂的泛型化对象类似于
* <pre>{@code
* Map<User, Key<Long>>
* }</pre>
*
* @param type
* @param parameterizedType 泛型变量例如List&lt;T&gt;
@ -473,4 +479,20 @@ public class TypeUtil {
public static Type[] getActualTypes(final Type type, final Type... typeVariables) {
return ActualTypeMapperPool.getActualTypes(type, typeVariables);
}
/**
* 获取泛型变量和泛型实际类型的对应关系Map例如
*
* <pre>
* T org.dromara.hutool.test.User
* E java.lang.Integer
* </pre>
*
* @param clazz 被解析的包含泛型参数的类
* @return 泛型对应关系Map
*/
public static Map<Type, Type> getTypeMap(final Class<?> clazz) {
return ActualTypeMapperPool.get(clazz);
}
// endregion
}

View File

@ -28,6 +28,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
*/
public class DefaultRegexDateParserTest {
@Test
void parseYearMonthDaySplitByDashedWithTTest() {
assertParse("2021-03-17 06:31:33", "2021-03-17T06:31:33");
assertParse("2021-03-17 06:31:33", "2021-03-17T06:31:33.999");
assertParse("2021-03-17 06:31:33", "2021-03-17T06:31:33.9999");
}
@Test
void parseYearMonthDaySplitByDashedTest() {
assertParse("2013-02-03 00:00:00", "2013-Feb-03");

View File

@ -18,6 +18,7 @@ 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.TypeAdapterManager;
import org.dromara.hutool.json.writer.JSONWriter;
@ -231,10 +232,29 @@ public interface JSON extends Serializable {
*/
@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;
}
}
}

View File

@ -19,6 +19,7 @@ package org.dromara.hutool.json;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.lang.wrapper.Wrapper;
import org.dromara.hutool.core.math.NumberUtil;
import org.dromara.hutool.core.reflect.ClassUtil;
import org.dromara.hutool.json.writer.JSONWriter;
import org.dromara.hutool.json.writer.NumberWriteMode;
@ -51,7 +52,22 @@ public class JSONPrimitive implements Wrapper<Object>, JSON {
* @return 是否为JSONPrimitive类型
*/
public static boolean isTypeForJSONPrimitive(final Object value) {
return value instanceof Boolean || value instanceof Number || value instanceof String;
return value instanceof Boolean
|| value instanceof Number
|| value instanceof Character
|| value instanceof String;
}
/**
* 判断给定类是否可以转为JSONPrimitive类型
*
* @param type
* @return 是否为JSONPrimitive类型
*/
public static boolean isTypeForJSONPrimitive(final Class<?> type) {
return ClassUtil.isBasicType(type)
|| Number.class.isAssignableFrom(type)
|| String.class == type;
}
private Object value;

View File

@ -27,6 +27,7 @@ import org.dromara.hutool.json.mapper.JSONValueMapper;
import org.dromara.hutool.json.writer.JSONWriter;
import org.dromara.hutool.json.xml.JSONXMLUtil;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.Writer;
import java.lang.reflect.Type;
@ -118,7 +119,10 @@ public class JSONUtil {
* @param predicate 键值对过滤编辑器可以通过实现此接口完成解析前对键值对的过滤和修改操作{@link Predicate#test(Object)}{@code true}保留
* @return JSONObject
*/
public static JSONObject parseObj(final Object obj, final JSONConfig config, final Predicate<MutableEntry<Object, Object>> predicate) {
public static JSONObject parseObj(Object obj, final JSONConfig config, final Predicate<MutableEntry<Object, Object>> predicate) {
if(obj instanceof byte[]){
obj = new ByteArrayInputStream((byte[]) obj);
}
return (JSONObject) parse(obj, config, predicate);
}

View File

@ -1,254 +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.convert;
import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.bean.copier.BeanCopier;
import org.dromara.hutool.core.convert.*;
import org.dromara.hutool.core.convert.impl.DateConverter;
import org.dromara.hutool.core.convert.impl.TemporalAccessorConverter;
import org.dromara.hutool.core.reflect.ConstructorUtil;
import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.*;
import org.dromara.hutool.json.mapper.JSONValueMapper;
import org.dromara.hutool.json.reader.JSONParser;
import org.dromara.hutool.json.reader.JSONTokener;
import org.dromara.hutool.json.serializer.JSONDeserializer;
import org.dromara.hutool.json.serializer.TypeAdapterManager;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
/**
* JSON转换器实现Object对象转换为{@link JSON}支持的对象
* <ul>
* <li>任意支持的对象转换为JSON</li>
* <li>JSON转换为指定对象Bean</li>
* </ul>
*
* @author looly
* @since 6.0.0
*/
public class JSONConverter implements Converter, Serializable {
private static final long serialVersionUID = 1L;
/**
* 单例
*/
public static final JSONConverter INSTANCE = new JSONConverter(null);
/**
* 创建JSON转换器
*
* @param config JSON配置
* @return JSONConverter
*/
public static JSONConverter of(final JSONConfig config) {
final JSONConverter jsonConverter = new JSONConverter(config);
jsonConverter.registerConverter = new RegisterConverter(jsonConverter)
.register(JSONObject.class, INSTANCE)
.register(JSONArray.class, INSTANCE)
.register(JSONPrimitive.class, INSTANCE);
jsonConverter.specialConverter = new SpecialConverter(jsonConverter);
return jsonConverter;
}
private final JSONConfig config;
private RegisterConverter registerConverter;
private SpecialConverter specialConverter;
/**
* 构造
*
* @param config JSON配置
*/
private JSONConverter(final JSONConfig config) {
this.config = config;
}
@Override
public Object convert(Type targetType, final Object value) throws ConvertException {
if (null == value) {
return null;
}
// JSON转对象
if (value instanceof JSON) {
if (targetType instanceof TypeReference) {
// 还原原始类型
targetType = ((TypeReference<?>) targetType).getType();
}
return toBean(targetType, (JSON) value);
}
// 对象转JSON
final Class<?> targetClass = TypeUtil.getClass(targetType);
if (null != targetClass) {
if (JSON.class.isAssignableFrom(targetClass)) {
return toJSON(value);
}
// 自定义日期格式
if (Date.class.isAssignableFrom(targetClass) || TemporalAccessor.class.isAssignableFrom(targetClass)) {
final Object date = toDateWithFormat(targetClass, value);
if (null != date) {
return date;
}
}
}
return ConvertUtil.convertWithCheck(targetType, value, null, config.isIgnoreError());
}
/**
* 实现Object对象转换为JSON对象根据RFC8259规范支持的对象
* <ul>
* <li>String: 转换为相应的对象"和'包围的字符串返回原字符串,""返回{@code null}</li>
* <li>ArrayIterableIterator转换为JSONArray</li>
* <li>Bean对象转为JSONObject</li>
* <li>NumberBoolean返回原对象</li>
* <li>null返回{@code null}</li>
* </ul>
*
* @param obj 被转换的对象
* @return 转换后的对象
* @throws JSONException 转换异常
*/
public JSON toJSON(final Object obj) throws JSONException {
if (null == obj) {
return null;
}
return JSONValueMapper.of(config, null).map(obj);
}
/**
* 实现{@link CharSequence}转换为JSON对象根据RFC8259规范<br>
* 转换为相应的对象"和'包围的字符串返回原字符串,""返回{@code null}
*
* @param str 被转换的字符串
* @return 转换后的对象
* @throws JSONException 转换异常
*/
public JSON toJSON(final CharSequence str) throws JSONException {
if (null == str) {
return null;
}
final String jsonStr = StrUtil.trim(str);
if (jsonStr.isEmpty()) {
// https://www.rfc-editor.org/rfc/rfc8259#section-7
// 未被包装的空串理解为null
return null;
}
// RFC8259JSON字符串值number, boolean, or null
final JSONParser jsonParser = JSONParser.of(new JSONTokener(jsonStr), config);
return jsonParser.parse();
}
// ----------------------------------------------------------- Private method start
/**
* JSON转Bean流程为
* <pre>{@code
* 自定义反序列化 --> 尝试转Kotlin --> 基于注册的标准转换器 --> CollectionMap等含有泛型的特殊转换器 --> 普通Bean转换器
* }</pre>
*
* @param <T> 目标类型
* @param targetType 目标类型
* @param json JSON
* @return bean
*/
@SuppressWarnings("unchecked")
private <T> T toBean(final Type targetType, final JSON json) {
// 自定义对象反序列化
final JSONDeserializer<?> deserializer = TypeAdapterManager.getInstance().getDeserializer(json, targetType);
if (null != deserializer) {
return (T) deserializer.deserialize(json, targetType);
}
// 当目标类型不确定时返回原JSON
final Class<T> rawType = (Class<T>) TypeUtil.getClass(targetType);
if (null == rawType || JSON.class.isAssignableFrom(rawType)) {
return (T) json;
//throw new JSONException("Can not get class from type: {}", targetType);
}
final Object value;
// JSON原始类型
if (json instanceof JSONPrimitive) {
value = ((JSONPrimitive) json).getValue();
} else {
value = json;
}
final JSONConfig config = ObjUtil.defaultIfNull(json.config(), JSONConfig::of);
final boolean ignoreError = config.isIgnoreError();
try {
// 标准转换器
final Converter converter = registerConverter.getConverter(targetType, value, true);
if (null != converter) {
return (T) converter.convert(targetType, value);
}
// 特殊类型转换包括CollectionMap强转Array等
final T result = (T) specialConverter.convert(targetType, rawType, value);
if (null != result) {
return result;
}
} catch (final ConvertException e) {
if (ignoreError) {
return null;
}
}
// 尝试转Bean
if (!(json instanceof JSONPrimitive) && BeanUtil.isWritableBean(rawType)) {
return BeanCopier.of(value,
ConstructorUtil.newInstanceIfPossible(rawType), targetType,
InternalJSONUtil.toCopyOptions(config)).copy();
}
// 跳过异常时返回null
if (ignoreError) {
return null;
}
// 无法转换
throw new JSONException("Can not convert from '{}': {} to '{}'",
json.getClass().getName(), json, targetType.getTypeName());
}
private Object toDateWithFormat(final Class<?> targetDateClass, final Object value) {
// 日期转换支持自定义日期格式
final String format = config.getDateFormat();
if (StrUtil.isNotBlank(format)) {
if (Date.class.isAssignableFrom(targetDateClass)) {
return new DateConverter(format).convert(targetDateClass, value);
} else {
return new TemporalAccessorConverter(format).convert(targetDateClass, value);
}
}
return null;
}
// ----------------------------------------------------------- Private method end
}

View File

@ -1,52 +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.convert;
import org.dromara.hutool.core.bean.copier.ValueProvider;
import org.dromara.hutool.json.JSONGetter;
import java.lang.reflect.Type;
/**
* JSONGetter的ValueProvider
*
* @param <K> 键类型
* @author looly
*/
public class JSONGetterValueProvider<K> implements ValueProvider<K> {
private final JSONGetter<K> jsonGetter;
/**
* 构造
*
* @param jsonGetter {@link JSONGetter}
*/
public JSONGetterValueProvider(final JSONGetter<K> jsonGetter) {
this.jsonGetter = jsonGetter;
}
@Override
public Object value(final K key, final Type valueType) {
return jsonGetter.get(key, valueType);
}
@Override
public boolean containsKey(final K key) {
return !jsonGetter.isNull(key);
}
}

View File

@ -30,7 +30,7 @@ import java.util.Date;
* @author Looly
* @since 6.0.0
*/
public class DateSerDesc implements GsonSerDesc<Date> {
public class DateSerDesc implements GsonTypeAdapter<Date> {
/**
* 默认日期格式化描述默认为null表示使用时间戳

View File

@ -116,11 +116,11 @@ public class GsonEngine extends AbstractJSONEngine implements Wrapper<Gson> {
private void registerDate(final GsonBuilder builder, final String dateFormat){
// java date
builder.registerTypeHierarchyAdapter(Date.class, new DateSerDesc(dateFormat));
builder.registerTypeHierarchyAdapter(TimeZone.class, TimeZoneSerDesc.INSTANCE);
builder.registerTypeHierarchyAdapter(TimeZone.class, TimeZoneGsonTypeAdapter.INSTANCE);
// java.time
builder.registerTypeAdapter(LocalDateTime.class, new TemporalSerDesc(LocalDateTime.class, dateFormat));
builder.registerTypeAdapter(LocalDate.class, new TemporalSerDesc(LocalDate.class, dateFormat));
builder.registerTypeAdapter(LocalTime.class, new TemporalSerDesc(LocalTime.class, dateFormat));
builder.registerTypeAdapter(LocalDateTime.class, new TemporalGsonTypeAdapter(LocalDateTime.class, dateFormat));
builder.registerTypeAdapter(LocalDate.class, new TemporalGsonTypeAdapter(LocalDate.class, dateFormat));
builder.registerTypeAdapter(LocalTime.class, new TemporalGsonTypeAdapter(LocalTime.class, dateFormat));
}
}

View File

@ -26,5 +26,5 @@ import com.google.gson.JsonSerializer;
* @author Looly
* @since 6.0.0
*/
public interface GsonSerDesc<T> extends JsonSerializer<T>, JsonDeserializer<T> {
public interface GsonTypeAdapter<T> extends JsonSerializer<T>, JsonDeserializer<T> {
}

View File

@ -30,7 +30,7 @@ import java.time.temporal.TemporalAccessor;
* @author Looly
* @since 6.0.0
*/
public class TemporalSerDesc implements GsonSerDesc<TemporalAccessor> {
public class TemporalGsonTypeAdapter implements GsonTypeAdapter<TemporalAccessor> {
private final Class<? extends TemporalAccessor> type;
private final String dateFormat;
@ -41,7 +41,7 @@ public class TemporalSerDesc implements GsonSerDesc<TemporalAccessor> {
* @param type 时间类型
* @param dateFormat 日期格式
*/
public TemporalSerDesc(final Class<? extends TemporalAccessor> type, final String dateFormat) {
public TemporalGsonTypeAdapter(final Class<? extends TemporalAccessor> type, final String dateFormat) {
this.type = type;
this.dateFormat = dateFormat;
}

View File

@ -27,12 +27,12 @@ import java.util.TimeZone;
* @author Looly
* @since 6.0.0
*/
public class TimeZoneSerDesc implements GsonSerDesc<TimeZone>{
public class TimeZoneGsonTypeAdapter implements GsonTypeAdapter<TimeZone> {
/**
* 默认时区格式化描述
*/
public static final TimeZoneSerDesc INSTANCE = new TimeZoneSerDesc();
public static final TimeZoneGsonTypeAdapter INSTANCE = new TimeZoneGsonTypeAdapter();
@Override
public JsonElement serialize(final TimeZone src, final Type typeOfSrc, final JsonSerializationContext context) {

View File

@ -16,8 +16,6 @@
package org.dromara.hutool.json.mapper;
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.lang.mutable.MutableEntry;
import org.dromara.hutool.json.JSONArray;
@ -26,7 +24,6 @@ import org.dromara.hutool.json.JSONException;
import org.dromara.hutool.json.reader.JSONParser;
import org.dromara.hutool.json.reader.JSONTokener;
import java.util.Iterator;
import java.util.function.Predicate;
/**
@ -90,51 +87,10 @@ class JSONArrayMapper {
jsonArray.set(b);
}
}
} else {
final 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 {
if (!jsonArray.config().isIgnoreError()) {
}
throw new JSONException("Unsupported [{}] to JSONArray", source.getClass());
}
// 如果用户选择跳过异常则跳过此值转换
return;
}
mapFromIterator(iter, jsonArray);
}
}
/**
* 从Iterator中读取数据并添加到JSONArray中
*
* @param iter {@link Iterator}
* @param jsonArray {@link JSONArray}
*/
private void mapFromIterator(final Iterator<?> iter, final JSONArray jsonArray) {
Object next;
while (iter.hasNext()) {
next = iter.next();
// 检查循环引用
if (next != source) {
if (null != this.predicate) {
final MutableEntry<Object, Object> entry = MutableEntry.of(jsonArray.size(), next);
if (predicate.test(entry)) {
// 使用修改后的键值对
next = entry.getValue();
jsonArray.set(next);
}
} else {
jsonArray.set(next);
}
}
}
}
/**
* 从JSONTokener中读取数据并添加到JSONArray中

View File

@ -16,14 +16,8 @@
package org.dromara.hutool.json.mapper;
import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.bean.RecordUtil;
import org.dromara.hutool.core.bean.copier.CopyOptions;
import org.dromara.hutool.core.convert.ConvertUtil;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.lang.mutable.MutableEntry;
import org.dromara.hutool.core.reflect.method.MethodUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.json.InternalJSONUtil;
import org.dromara.hutool.json.JSONConfig;
import org.dromara.hutool.json.JSONException;
@ -31,9 +25,7 @@ import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.reader.JSONParser;
import org.dromara.hutool.json.reader.JSONTokener;
import java.lang.reflect.Type;
import java.util.Enumeration;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.function.Predicate;
@ -81,32 +73,17 @@ class JSONObjectMapper {
*
* @param jsonObject 目标{@link JSONObject}
*/
@SuppressWarnings("rawtypes")
public void mapTo(final JSONObject jsonObject) {
final Object source = this.source;
if (null == source) {
return;
}
if (source instanceof Map) {
// Map
for (final Map.Entry<?, ?> e : ((Map<?, ?>) source).entrySet()) {
jsonObject.set(ConvertUtil.toStr(e.getKey()), e.getValue());
}
} else if (source instanceof Map.Entry) {
final Map.Entry entry = (Map.Entry) source;
jsonObject.set(ConvertUtil.toStr(entry.getKey()), entry.getValue());
} else if (source instanceof byte[]) {
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 (RecordUtil.isRecord(source.getClass())) {
// since 6.0.0
mapFromRecord(source, jsonObject);
} else if (BeanUtil.isReadableBean(source.getClass())) {
// 普通Bean
mapFromBean(source, jsonObject);
} else {
if (!jsonObject.config().isIgnoreError()) {
// 不支持对象类型转换为JSONObject
@ -142,36 +119,4 @@ class JSONObjectMapper {
private void mapFromTokener(final JSONTokener x, final JSONConfig config, final JSONObject jsonObject) {
JSONParser.of(x, config).setPredicate(this.predicate).parseTo(jsonObject);
}
/**
* 从Record转换
*
* @param record Record对象
* @param jsonObject {@link JSONObject}
*/
private void mapFromRecord(final Object record, final JSONObject jsonObject) {
final Map.Entry<String, Type>[] components = RecordUtil.getRecordComponents(record.getClass());
String key;
for (final Map.Entry<String, Type> entry : components) {
key = entry.getKey();
jsonObject.set(key, MethodUtil.invoke(record, key));
}
}
/**
* 从Bean转换
*
* @param bean Bean对象
* @param jsonObject {@link JSONObject}
*/
private void mapFromBean(final Object bean, final JSONObject jsonObject) {
final CopyOptions copyOptions = InternalJSONUtil.toCopyOptions(jsonObject.config());
if (null != this.predicate) {
copyOptions.setFieldEditor((entry -> this.predicate.test(
MutableEntry.of(StrUtil.toStringOrNull(entry.getKey()), entry.getValue())) ?
entry : null));
}
BeanUtil.beanToMap(bean, jsonObject, copyOptions);
}
}

View File

@ -17,10 +17,11 @@
package org.dromara.hutool.json.serializer;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.collection.set.ConcurrentHashSet;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.lang.tuple.Pair;
import org.dromara.hutool.core.lang.tuple.Triple;
import org.dromara.hutool.core.lang.tuple.Tuple;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap;
import org.dromara.hutool.core.reflect.ConstructorUtil;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.json.JSON;
@ -28,6 +29,8 @@ import org.dromara.hutool.json.JSONException;
import org.dromara.hutool.json.serializer.impl.*;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@ -57,11 +60,7 @@ public class TypeAdapterManager {
/**
* 静态初始化器由JVM来保证线程安全
*/
private static final TypeAdapterManager INSTANCE = new TypeAdapterManager();
static {
registerDefault(INSTANCE);
}
private static final TypeAdapterManager INSTANCE = registerDefault(new TypeAdapterManager());
}
/**
@ -79,9 +78,7 @@ public class TypeAdapterManager {
* @return SerializerManager
*/
public static TypeAdapterManager of() {
final TypeAdapterManager typeAdapterManager = new TypeAdapterManager();
registerDefault(typeAdapterManager);
return typeAdapterManager;
return registerDefault(new TypeAdapterManager());
}
/**
@ -168,20 +165,31 @@ public class TypeAdapterManager {
*/
@SuppressWarnings({"unchecked"})
public JSONSerializer<Object> getSerializer(final Object bean, final Type type) {
JSONSerializer<Object> result = null;
if (null != type && MapUtil.isNotEmpty(this.serializerMap)) {
result = (JSONSerializer<Object>) this.serializerMap.get(type);
final Class<?> rawType = TypeUtil.getClass(type);
if (null == rawType) {
return null;
}
if (JSONSerializer.class.isAssignableFrom(rawType)) {
return (JSONSerializer<Object>) ConstructorUtil.newInstanceIfPossible(rawType);
}
if (null == result && CollUtil.isNotEmpty(this.serializerSet)) {
if (MapUtil.isNotEmpty(this.serializerMap)) {
final JSONSerializer<?> result = this.serializerMap.get(rawType);
if(null != result){
return (JSONSerializer<Object>) result;
}
}
// Matcher
if (CollUtil.isNotEmpty(this.serializerSet)) {
for (final MatcherJSONSerializer<?> serializer : this.serializerSet) {
if (serializer.match(bean, null)) {
result = (MatcherJSONSerializer<Object>) serializer;
break;
return (MatcherJSONSerializer<Object>) serializer;
}
}
}
return result;
throw new JSONException("No serializer for type: " + type);
}
/**
@ -201,8 +209,8 @@ public class TypeAdapterManager {
return (JSONDeserializer<Object>) ConstructorUtil.newInstanceIfPossible(rawType);
}
if (CollUtil.isNotEmpty(this.deserializerMap)) {
final JSONDeserializer<?> jsonDeserializer = this.deserializerMap.get(type);
if (MapUtil.isNotEmpty(this.deserializerMap)) {
final JSONDeserializer<?> jsonDeserializer = this.deserializerMap.get(rawType);
if (null != jsonDeserializer) {
return (JSONDeserializer<Object>) jsonDeserializer;
}
@ -226,7 +234,7 @@ public class TypeAdapterManager {
if (null == this.serializerSet) {
synchronized (this) {
if (null == this.serializerSet) {
this.serializerSet = new ConcurrentHashSet<>();
this.serializerSet = new LinkedHashSet<>();
}
}
}
@ -237,7 +245,7 @@ public class TypeAdapterManager {
if (null == this.serializerMap) {
synchronized (this) {
if (null == this.serializerMap) {
this.serializerMap = new SafeConcurrentHashMap<>();
this.serializerMap = new HashMap<>();
}
}
}
@ -248,7 +256,7 @@ public class TypeAdapterManager {
if (null == this.deserializerSet) {
synchronized (this) {
if (null == this.deserializerSet) {
this.deserializerSet = new ConcurrentHashSet<>();
this.deserializerSet = new LinkedHashSet<>();
}
}
}
@ -259,7 +267,7 @@ public class TypeAdapterManager {
if (null == this.deserializerMap) {
synchronized (this) {
if (null == this.deserializerMap) {
this.deserializerMap = new SafeConcurrentHashMap<>();
this.deserializerMap = new HashMap<>();
}
}
}
@ -271,24 +279,35 @@ public class TypeAdapterManager {
* 注册默认的序列化器和反序列化器
*
* @param manager {@code SerializerManager}
* @return TypeAdapterManager
*/
private static void registerDefault(final TypeAdapterManager manager) {
// issue#I5WDP0 对于Kotlin对象由于参数可能非空限制导致无法创建一个默认的对象再赋值
private static TypeAdapterManager registerDefault(final TypeAdapterManager manager) {
// 自定义序列化器
// 自定义反序列化器
manager.register(KBeanDeserializer.INSTANCE);
manager.register(CollectionDeserializer.INSTANCE);
manager.register(ArrayDeserializer.INSTANCE);
manager.register(MapDeserializer.INSTANCE);
manager.register(EntryDeserializer.INSTANCE);
manager.register(RecordDeserializer.INSTANCE);
manager.register(Triple.class, TripleDeserializer.INSTANCE);
manager.register(Pair.class, PairDeserializer.INSTANCE);
manager.register(Tuple.class, TupleDeserializer.INSTANCE);
// 自定义类型适配器
manager.register(JSONPrimitiveTypeAdapter.INSTANCE);
manager.register(CharSequenceTypeAdapter.INSTANCE);
manager.register(DateTypeAdapter.INSTANCE);
manager.register(CalendarTypeAdapter.INSTANCE);
manager.register(TemporalTypeAdapter.INSTANCE);
manager.register(TimeZoneTypeAdapter.INSTANCE);
manager.register(EnumTypeAdapter.INSTANCE);
manager.register(ThrowableTypeAdapter.INSTANCE);
manager.register(EntryTypeAdapter.INSTANCE);
manager.register(MapTypeAdapter.INSTANCE);
manager.register(IterTypeAdapter.INSTANCE);
manager.register(ArrayTypeAdapter.INSTANCE);
// 最低优先级
manager.register(BeanTypeAdapter.INSTANCE);
return manager;
}
}

View File

@ -16,11 +16,15 @@
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.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.serializer.JSONContext;
import org.dromara.hutool.json.serializer.MatcherJSONDeserializer;
import org.dromara.hutool.json.serializer.MatcherJSONSerializer;
import java.lang.reflect.Array;
import java.lang.reflect.Type;
@ -32,12 +36,17 @@ import java.util.Map;
* @author looly
* @since 6.0.0
*/
public class ArrayDeserializer implements MatcherJSONDeserializer<Object> {
public class ArrayTypeAdapter implements MatcherJSONSerializer<Object>, MatcherJSONDeserializer<Object> {
/**
* 单例
*/
public static final ArrayDeserializer INSTANCE = new ArrayDeserializer();
public static final ArrayTypeAdapter INSTANCE = new ArrayTypeAdapter();
@Override
public boolean match(final Object bean, final JSONContext context) {
return ArrayUtil.isArray(bean);
}
@Override
public boolean match(final JSON json, final Type deserializeType) {
@ -47,6 +56,11 @@ public class ArrayDeserializer implements MatcherJSONDeserializer<Object> {
return false;
}
@Override
public JSON serialize(final Object bean, final JSONContext context) {
return IterTypeAdapter.INSTANCE.serialize(new ArrayIter<>(bean), context);
}
@Override
public Object deserialize(final JSON json, final Type deserializeType) {
final int size = json.size();

View File

@ -0,0 +1,88 @@
/*
* 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.reflect.TypeUtil;
import org.dromara.hutool.core.text.StrUtil;
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;
import org.dromara.hutool.json.xml.JSONXMLParser;
import org.dromara.hutool.json.xml.ParseConfig;
import java.lang.reflect.Type;
/**
* CharSequence类型适配器用于处理未匹配的JSON类型
*
* @author looly
* @since 6.0.0
*/
public class CharSequenceTypeAdapter implements MatcherJSONSerializer<CharSequence>, MatcherJSONDeserializer<CharSequence> {
/**
* 单例
*/
public static final CharSequenceTypeAdapter INSTANCE = new CharSequenceTypeAdapter();
@Override
public boolean match(final Object bean, final JSONContext context) {
return bean instanceof CharSequence;
}
@Override
public boolean match(final JSON json, final Type deserializeType) {
return CharSequence.class.isAssignableFrom(TypeUtil.getClass(deserializeType));
}
@Override
public JSON serialize(final CharSequence bean, final JSONContext context) {
final String jsonStr = StrUtil.trim(bean);
if (StrUtil.startWith(jsonStr, '<')) {
// 可能为XML
JSONObject jsonObject = (JSONObject) context.getContextJson();
if(null == jsonObject){
jsonObject = JSONUtil.ofObj(context.config());
}
JSONXMLParser.of(ParseConfig.of(), null).parseJSONObject(jsonStr, jsonObject);
return jsonObject;
}
return mapFromTokener(new JSONTokener(jsonStr), context.config());
}
@Override
public CharSequence deserialize(final JSON json, final Type deserializeType) {
if(json instanceof JSONPrimitive){
return ((JSONPrimitive) json).getValue().toString();
}
return json.toString();
}
/**
* {@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

@ -18,6 +18,7 @@ package org.dromara.hutool.json.serializer.impl;
import org.dromara.hutool.core.convert.impl.DateConverter;
import org.dromara.hutool.core.date.DateUtil;
import org.dromara.hutool.core.date.format.GlobalCustomFormat;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.JSON;
@ -55,12 +56,20 @@ public class DateTypeAdapter implements MatcherJSONSerializer<Date>, MatcherJSON
@Override
public JSON serialize(final Date bean, final JSONContext context) {
final JSONConfig config = ObjUtil.apply(context, JSONContext::config);
final JSONConfig config = context.config();
final String format = ObjUtil.apply(config, JSONConfig::getDateFormat);
return new JSONPrimitive(
null == format
? bean.getTime()
: DateUtil.format(bean, format), config);
final Object value;
// 默认为时间戳
if(null == format || GlobalCustomFormat.FORMAT_MILLISECONDS.equals(format)){
value = bean.getTime();
} else if(GlobalCustomFormat.FORMAT_SECONDS.equals(format)){
value = Math.floorDiv(bean.getTime(), 1000L);
} else {
value = DateUtil.format(bean, format);
}
return new JSONPrimitive(value, config);
}
@Override

View File

@ -16,7 +16,6 @@
package org.dromara.hutool.json.serializer.impl;
import org.dromara.hutool.core.convert.CompositeConverter;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.json.*;
import org.dromara.hutool.json.serializer.JSONDeserializer;
@ -40,58 +39,21 @@ public class DefaultDeserializer implements JSONDeserializer<Object> {
public Object deserialize(final JSON json, final Type deserializeType) {
// 当目标类型不确定时返回原JSON
final Class<?> rawType = TypeUtil.getClass(deserializeType);
if (null == rawType || rawType == json.getClass()) {
if (null == rawType || Object.class == rawType || rawType == json.getClass()) {
return json;
}
if (json instanceof JSONObject) {
return fromJSONObject((JSONObject) json, deserializeType, rawType);
} else if (json instanceof JSONArray) {
return fromJSONArray((JSONArray) json, deserializeType, rawType);
} else if (json instanceof JSONPrimitive) {
return fromJSONPrimitive((JSONPrimitive) json, deserializeType, rawType);
// JSON类型之间互转
if(json instanceof JSONPrimitive && JSON.class.isAssignableFrom(rawType)){
final Object value = json.asJSONPrimitive().getValue();
if(value instanceof CharSequence){
// JSON字符串转JSON
return JSONUtil.parse(value, json.config());
}
throw new JSONException("Unsupported JSON type: {}", json.getClass());
} else if(json instanceof JSONObject && JSONArray.class == rawType){
return JSONUtil.parseArray(json, json.config());
}
/**
* 从JSONObject反序列化
*
* @param json JSONObject
* @param deserializeType 目标类型
* @param rawType 目标类型
* @return 反序列化后的对象
*/
private Object fromJSONObject(final JSONObject json, final Type deserializeType, final Class<?> rawType) {
throw new JSONException("Unsupported JSONObject to {}", rawType);
}
/**
* 从JSONArray反序列化
*
* @param json JSONArray
* @param deserializeType 目标类型
* @param rawType 目标类型
* @return 反序列化后的对象
*/
private Object fromJSONArray(final JSONArray json, final Type deserializeType, final Class<?> rawType) {
throw new JSONException("Unsupported JSONArray to {}", rawType);
}
/**
* 从JSONPrimitive反序列化
*
* @param json JSONPrimitive
* @param deserializeType 目标类型
* @param rawType 目标类型
* @return 反序列化后的对象
*/
private Object fromJSONPrimitive(final JSONPrimitive json, final Type deserializeType, final Class<?> rawType) {
final Object value = json.getValue();
if (null != value && rawType.isAssignableFrom(value.getClass())) {
return value;
}
return CompositeConverter.getInstance().convert(deserializeType, value);
throw new JSONException("Unsupported type {} to {}", json.getClass(), deserializeType.getTypeName());
}
}

View File

@ -17,27 +17,36 @@
package org.dromara.hutool.json.serializer.impl;
import org.dromara.hutool.core.convert.CompositeConverter;
import org.dromara.hutool.core.convert.ConvertUtil;
import org.dromara.hutool.core.reflect.ConstructorUtil;
import org.dromara.hutool.core.reflect.TypeUtil;
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.MatcherJSONDeserializer;
import org.dromara.hutool.json.serializer.MatcherJSONSerializer;
import java.lang.reflect.Type;
import java.util.Map;
/**
* Map.Entry反序列化器用于将JSON对象转换为Map.Entry对象
* Map.Entry序列化和反序列化器用于将JSON对象和Map.Entry对象互转
*
* @author looly
* @since 6.0.0
*/
public class EntryDeserializer implements MatcherJSONDeserializer<Map.Entry<?, ?>> {
public class EntryTypeAdapter implements MatcherJSONSerializer<Map.Entry<?, ?>>, MatcherJSONDeserializer<Map.Entry<?, ?>> {
/**
* 单例
*/
public static final EntryDeserializer INSTANCE = new EntryDeserializer();
public static final EntryTypeAdapter INSTANCE = new EntryTypeAdapter();
@Override
public boolean match(final Object bean, final JSONContext context) {
return bean instanceof Map.Entry;
}
@Override
public boolean match(final JSON json, final Type deserializeType) {
@ -48,6 +57,19 @@ public class EntryDeserializer implements MatcherJSONDeserializer<Map.Entry<?, ?
return false;
}
@Override
public JSON serialize(final Map.Entry<?, ?> bean, final JSONContext context) {
final JSONObject result;
final JSON contextJson = context.getContextJson();
if(contextJson instanceof JSONObject){
result = contextJson.asJSONObject();
}else{
result = JSONUtil.ofObj(context.config());
}
result.set(ConvertUtil.toStr(bean.getKey()), bean.getValue());
return result;
}
@Override
public Map.Entry<?, ?> deserialize(final JSON json, final Type deserializeType) {
final Type keyType = TypeUtil.getTypeArgument(deserializeType, 0);

View File

@ -18,39 +18,66 @@ package org.dromara.hutool.json.serializer.impl;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONArray;
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.MatcherJSONDeserializer;
import org.dromara.hutool.json.serializer.MatcherJSONSerializer;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
/**
* 集合类型反序列化器
* Iterator序列化器{@link Iterable}{@link Iterator}转换为JSONArray
*
* @author looly
* @since 6.0.0
* @author Looly
*/
public class CollectionDeserializer implements MatcherJSONDeserializer<Collection<?>> {
public class IterTypeAdapter implements MatcherJSONSerializer<Object>, MatcherJSONDeserializer<Object> {
/**
* 单例
*/
public static final CollectionDeserializer INSTANCE = new CollectionDeserializer();
public static final IterTypeAdapter INSTANCE = new IterTypeAdapter();
@Override
public boolean match(final Object bean, final JSONContext context) {
return bean instanceof Iterable || bean instanceof Iterator;
}
@Override
public boolean match(final JSON json, final Type deserializeType) {
if (json instanceof JSONArray || json instanceof JSONObject) {
final Class<?> rawType = TypeUtil.getClass(deserializeType);
// 反序列化只支持到集合
return Collection.class.isAssignableFrom(rawType);
}
return false;
}
@Override
public Collection<?> deserialize(final JSON json, final Type deserializeType) {
public JSON serialize(final Object bean, final JSONContext context) {
final Iterator<?> iter;
if (bean instanceof Iterator<?>) {// Iterator
iter = ((Iterator<?>) bean);
} else {// Iterable
iter = ((Iterable<?>) bean).iterator();
}
JSONArray json = (JSONArray) context.getContextJson();
if(null == json){
json = JSONUtil.ofArray(ObjUtil.apply(context, JSONContext::config));
}
mapFromIterator(bean, iter, json);
return json;
}
@Override
public Object deserialize(final JSON json, final Type deserializeType) {
final Class<?> rawType = TypeUtil.getClass(deserializeType);
final Type elementType = TypeUtil.getTypeArgument(deserializeType);
final Collection<?> result = CollUtil.create(rawType, TypeUtil.getClass(elementType));
@ -65,6 +92,23 @@ public class CollectionDeserializer implements MatcherJSONDeserializer<Collectio
return result;
}
/**
* 从Iterator中读取数据并添加到JSONArray中
*
* @param iter {@link Iterator}
* @param jsonArray {@link JSONArray}
*/
private void mapFromIterator(final Object source, final Iterator<?> iter, final JSONArray jsonArray) {
Object next;
while (iter.hasNext()) {
next = iter.next();
// 检查循环引用
if (next != source) {
jsonArray.set(next);
}
}
}
/**
* 将JSONObject转换为集合
*

View File

@ -0,0 +1,82 @@
/*
* 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.reflect.TypeUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONConfig;
import org.dromara.hutool.json.JSONPrimitive;
import org.dromara.hutool.json.serializer.JSONContext;
import org.dromara.hutool.json.serializer.MatcherJSONDeserializer;
import org.dromara.hutool.json.serializer.MatcherJSONSerializer;
import java.lang.reflect.Type;
/**
* {@link JSONPrimitive}相关类型适配器用于处理数字类型的序列化和反序列化
*
* @author Looly
* @since 6.0.0
*/
public class JSONPrimitiveTypeAdapter implements MatcherJSONSerializer<Object>, MatcherJSONDeserializer<Object> {
/**
* 单例
*/
public static final JSONPrimitiveTypeAdapter INSTANCE = new JSONPrimitiveTypeAdapter();
@Override
public boolean match(final Object bean, final JSONContext context) {
return JSONPrimitive.isTypeForJSONPrimitive(bean);
}
@Override
public boolean match(final JSON json, final Type deserializeType) {
return json instanceof JSONPrimitive && JSONPrimitive.isTypeForJSONPrimitive(TypeUtil.getClass(deserializeType));
}
@Override
public JSON serialize(Object bean, final JSONContext context) {
if(bean instanceof Character){
// 字符按照字符串存储
bean = bean.toString();
}
final JSONPrimitive json = (JSONPrimitive) context.getContextJson();
if (null != json) {
json.setValue(bean);
return json;
}
return new JSONPrimitive(bean, context.config());
}
@Override
public Object deserialize(final JSON json, final Type deserializeType) {
final Object value = json.asJSONPrimitive().getValue();
if (null != value && TypeUtil.getClass(deserializeType).isAssignableFrom(value.getClass())) {
return value;
}
return ConvertUtil.convertWithCheck(deserializeType, value, null,
ObjUtil.apply(json.config(), JSONConfig::isIgnoreError));
}
}

View File

@ -20,7 +20,8 @@ import org.dromara.hutool.core.reflect.TypeUtil;
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.convert.JSONGetterValueProvider;
import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.convert.JSONObjectValueProvider;
import org.dromara.hutool.json.serializer.MatcherJSONDeserializer;
import java.lang.reflect.Type;
@ -46,10 +47,9 @@ public class KBeanDeserializer implements MatcherJSONDeserializer<Object> {
&& KClassUtil.isKotlinClass(rawType);
}
@SuppressWarnings("unchecked")
@Override
public Object deserialize(final JSON json, final Type deserializeType) {
final Class<?> rawType = TypeUtil.getClass(deserializeType);
return KClassUtil.newInstance(rawType, new JSONGetterValueProvider<>((JSONGetter<String>) json));
return KClassUtil.newInstance(rawType, new JSONObjectValueProvider((JSONObject) json));
}
}

View File

@ -17,28 +17,38 @@
package org.dromara.hutool.json.serializer.impl;
import org.dromara.hutool.core.convert.CompositeConverter;
import org.dromara.hutool.core.convert.ConvertUtil;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.util.ObjUtil;
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.MatcherJSONDeserializer;
import org.dromara.hutool.json.serializer.MatcherJSONSerializer;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Map反序列化器用于将JSON对象转换为Map对象
* Map类型适配器器用于将JSON对象和Map对象互转
*
* @author looly
* @since 6.0.0
*/
public class MapDeserializer implements MatcherJSONDeserializer<Map<?, ?>> {
public class MapTypeAdapter implements MatcherJSONSerializer<Map<?, ?>>, MatcherJSONDeserializer<Map<?, ?>> {
/**
* 单例
*/
public static final MapDeserializer INSTANCE = new MapDeserializer();
public static final MapTypeAdapter INSTANCE = new MapTypeAdapter();
@Override
public boolean match(final Object bean, final JSONContext context) {
return bean instanceof Map;
}
@Override
public boolean match(final JSON json, final Type deserializeType) {
@ -49,6 +59,19 @@ public class MapDeserializer implements MatcherJSONDeserializer<Map<?, ?>> {
return false;
}
@Override
public JSON serialize(final Map<?, ?> bean, final JSONContext context) {
final JSON contextJson = context.getContextJson();
final JSONObject result = contextJson instanceof JSONObject ?
(JSONObject) contextJson : JSONUtil.ofObj(context.config());
// 注入键值对
for (final Map.Entry<?, ?> e : bean.entrySet()) {
result.set(ConvertUtil.toStr(e.getKey()), e.getValue());
}
return result;
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public Map<?, ?> deserialize(final JSON json, final Type deserializeType) {
@ -60,7 +83,7 @@ public class MapDeserializer implements MatcherJSONDeserializer<Map<?, ?>> {
map.put(
// key类型为String转目标类型使用标准转换器
CompositeConverter.getInstance().convert(keyType, entry.getKey()),
entry.getValue().toBean(valueType)
ObjUtil.apply(entry.getValue(), (value)-> value.toBean(valueType))
);
}
return map;

View File

@ -0,0 +1,58 @@
/*
* 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.lang.tuple.Pair;
import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.serializer.JSONDeserializer;
import java.lang.reflect.Type;
/**
* 二元组反序列化器
*
* @author Looly
* @since 6.0.0
*/
public class PairDeserializer implements JSONDeserializer<Pair<?, ?>> {
/**
* 单例
*/
public static final PairDeserializer INSTANCE = new PairDeserializer();
@Override
public Pair<?, ?> deserialize(final JSON json, Type deserializeType) {
if (deserializeType instanceof TypeReference) {
deserializeType = ((TypeReference<?>) deserializeType).getType();
}
final Type leftType = TypeUtil.getTypeArgument(deserializeType, 0);
final Type rightType = TypeUtil.getTypeArgument(deserializeType, 2);
final JSONObject jsonObject = json.asJSONObject();
final JSON left = jsonObject.get("left");
final JSON right = jsonObject.get("right");
return Pair.of(
left.toBean(leftType),
right.toBean(rightType)
);
}
}

View File

@ -17,20 +17,20 @@
package org.dromara.hutool.json.serializer.impl;
import org.dromara.hutool.core.convert.impl.TemporalAccessorConverter;
import org.dromara.hutool.core.date.TimeUtil;
import org.dromara.hutool.core.date.format.GlobalCustomFormat;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.lang.Opt;
import org.dromara.hutool.core.math.NumberUtil;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.*;
import org.dromara.hutool.json.serializer.JSONContext;
import org.dromara.hutool.json.serializer.MatcherJSONDeserializer;
import org.dromara.hutool.json.serializer.MatcherJSONSerializer;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.*;
import java.time.temporal.TemporalAccessor;
/**
@ -70,13 +70,70 @@ public class TemporalTypeAdapter implements MatcherJSONSerializer<TemporalAccess
@Override
public JSON serialize(final TemporalAccessor bean, final JSONContext context) {
final JSONObject json;
final JSONConfig config = context.config();
final JSON contextJson = context.getContextJson();
if(contextJson instanceof JSONObject){
json = (JSONObject) contextJson;
}else {
json = new JSONObject((int) (7F / 0.75F + 1F), context.config());
toJSONObject(bean, contextJson.asJSONObject());
return contextJson;
}
if (bean instanceof Month) {
return new JSONPrimitive(((Month) bean).getValue(), config);
} else if (bean instanceof DayOfWeek) {
return new JSONPrimitive(((DayOfWeek) bean).getValue(), config);
} else if (bean instanceof MonthDay) {
return new JSONPrimitive(((MonthDay) bean).toString(), config);
}
final String format = ObjUtil.apply(config, JSONConfig::getDateFormat);
final Object value;
// 默认为时间戳
if (null == format || GlobalCustomFormat.FORMAT_MILLISECONDS.equals(format)) {
value = TimeUtil.toEpochMilli(bean);
} else if (GlobalCustomFormat.FORMAT_SECONDS.equals(format)) {
value = Math.floorDiv(TimeUtil.toEpochMilli(bean), 1000L);
} else {
value = TimeUtil.format(bean, format);
}
return new JSONPrimitive(value, config);
}
@Override
public TemporalAccessor deserialize(final JSON json, final Type deserializeType) {
// JSONPrimitive
if (json instanceof JSONPrimitive) {
final Object value = ((JSONPrimitive) json).getValue();
final TemporalAccessorConverter converter = new TemporalAccessorConverter(
Opt.ofNullable(json.config()).map(JSONConfig::getDateFormat).getOrNull());
return (TemporalAccessor) converter.convert(deserializeType, value);
}
final Class<?> temporalAccessorClass = TypeUtil.getClass(deserializeType);
if (json instanceof JSONObject) {
return fromJSONObject(temporalAccessorClass, json.asJSONObject());
}
if (Month.class.equals(temporalAccessorClass)) {
return Month.of((Integer) json.asJSONPrimitive().getValue());
} else if (DayOfWeek.class.equals(temporalAccessorClass)) {
return DayOfWeek.of((Integer) json.asJSONPrimitive().getValue());
} else if (MonthDay.class.equals(temporalAccessorClass)) {
return MonthDay.parse((CharSequence) json.asJSONPrimitive().getValue());
}
throw new JSONException("Unsupported type from JSON {} to {}", json, deserializeType);
}
/**
* {@link TemporalAccessor}转换为JSONObject
*
* @param bean {@link TemporalAccessor}
* @param json JSONObject
*/
private static void toJSONObject(final TemporalAccessor bean, final JSONObject json) {
if (bean instanceof LocalDate) {
final LocalDate localDate = (LocalDate) bean;
json.set(YEAR_KEY, localDate.getYear());
@ -97,27 +154,18 @@ public class TemporalTypeAdapter implements MatcherJSONSerializer<TemporalAccess
json.set(MINUTE_KEY, localTime.getMinute());
json.set(SECOND_KEY, localTime.getSecond());
json.set(NANO_KEY, localTime.getNano());
} else {
throw new JSONException("Unsupported TemporalAccessor type to JSON: {}", bean.getClass().getName());
}
return json;
throw new JSONException("Unsupported type {}.", bean.getClass().getName());
}
@Override
public TemporalAccessor deserialize(final JSON json, final Type deserializeType) {
// JSONPrimitive
if(json instanceof JSONPrimitive){
final Object value = ((JSONPrimitive) json).getValue();
final TemporalAccessorConverter converter = new TemporalAccessorConverter(
Opt.ofNullable(json.config()).map(JSONConfig::getDateFormat).getOrNull());
return (TemporalAccessor) converter.convert(deserializeType, value);
}
// TODO JSONArray
final Class<?> temporalAccessorClass = TypeUtil.getClass(deserializeType);
// JSONObject
final JSONObject jsonObject = (JSONObject) json;
/**
* 从JSONObject中获取时间信息转换为{@link TemporalAccessor}
*
* @param temporalAccessorClass 目标时间类型
* @param jsonObject JSONObject
* @return {@link TemporalAccessor}
*/
private static TemporalAccessor fromJSONObject(final Class<?> temporalAccessorClass, final JSONObject jsonObject) {
if (LocalDate.class.equals(temporalAccessorClass) || LocalDateTime.class.equals(temporalAccessorClass)) {
//
final Integer year = jsonObject.getInt(YEAR_KEY);
@ -160,6 +208,6 @@ public class TemporalTypeAdapter implements MatcherJSONSerializer<TemporalAccess
jsonObject.getInt(NANO_KEY));
}
throw new JSONException("Unsupported type from JSON: {}", deserializeType);
throw new JSONException("Unsupported type from JSON {} to {}", jsonObject, temporalAccessorClass);
}
}

View File

@ -0,0 +1,61 @@
/*
* 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.lang.tuple.Triple;
import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.serializer.JSONDeserializer;
import java.lang.reflect.Type;
/**
* 三元组反序列化器
*
* @author Looly
* @since 6.0.0
*/
public class TripleDeserializer implements JSONDeserializer<Triple<?, ?, ?>> {
/**
* 单例
*/
public static final TripleDeserializer INSTANCE = new TripleDeserializer();
@Override
public Triple<?, ?, ?> deserialize(final JSON json, Type deserializeType) {
if (deserializeType instanceof TypeReference) {
deserializeType = ((TypeReference<?>) deserializeType).getType();
}
final Type leftType = TypeUtil.getTypeArgument(deserializeType, 0);
final Type middileType = TypeUtil.getTypeArgument(deserializeType, 1);
final Type rightType = TypeUtil.getTypeArgument(deserializeType, 2);
final JSONObject jsonObject = json.asJSONObject();
final JSON left = jsonObject.get("left");
final JSON middle = jsonObject.get("middle");
final JSON right = jsonObject.get("right");
return Triple.of(
left.toBean(leftType),
middle.toBean(middileType),
right.toBean(rightType)
);
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.lang.tuple.Tuple;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.serializer.JSONDeserializer;
import java.lang.reflect.Type;
/**
* 多元组Tuple反序列化器
*
* @author Looly
* @since 6.0.0
*/
public class TupleDeserializer implements JSONDeserializer<Tuple> {
/**
* 单例
*/
public static final TupleDeserializer INSTANCE = new TupleDeserializer();
@Override
public Tuple deserialize(final JSON json, final Type deserializeType) {
return Tuple.of(json.toBean(Object[].class));
}
}

View File

@ -20,10 +20,10 @@ import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.text.CharUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.escape.EscapeUtil;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONArray;
import org.dromara.hutool.json.JSONException;
import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.JSONUtil;
/**
* JSON转XML字符串工具
@ -36,65 +36,61 @@ public class JSONXMLSerializer {
* 转换JSONObject为XML
* Convert a JSONObject into a well-formed, element-normal XML string.
*
* @param object A JSONObject.
* @param json A JSONObject.
* @return A string.
* @throws JSONException Thrown if there is an error parsing the string
*/
public static String toXml(final Object object) throws JSONException {
return toXml(object, null);
public static String toXml(final JSON json) throws JSONException {
return toXml(json, null);
}
/**
* 转换JSONObject为XML
*
* @param object JSON对象或数组
* @param json JSON对象或数组
* @param tagName 可选标签名称名称为空时忽略标签
* @return A string.
* @throws JSONException JSON解析异常
*/
public static String toXml(final Object object, final String tagName) throws JSONException {
return toXml(object, tagName, "content");
public static String toXml(final JSON json, final String tagName) throws JSONException {
return toXml(json, tagName, "content");
}
/**
* 转换JSONObject为XML
*
* @param object JSON对象或数组
* @param json JSON对象或数组
* @param tagName 可选标签名称名称为空时忽略标签
* @param contentKeys 标识为内容的key,遇到此key直接解析内容而不增加对应名称标签
* @return A string.
* @throws JSONException JSON解析异常
*/
public static String toXml(Object object, final String tagName, final String... contentKeys) throws JSONException {
if (null == object) {
public static String toXml(JSON json, final String tagName, final String... contentKeys) throws JSONException {
if (null == json) {
return null;
}
final StringBuilder sb = new StringBuilder();
if (object instanceof JSONObject) {
if (json instanceof JSONObject) {
// Emit <tagName>
appendTag(sb, tagName, false);
// Loop thru the keys.
((JSONObject) object).forEach((key, value) -> {
if (ArrayUtil.isArray(value)) {
value = JSONUtil.parseArray(value);
}
json.asJSONObject().forEach((key, value) -> {
// Emit content in body
if (ArrayUtil.contains(contentKeys, key)) {
if (value instanceof JSONArray) {
int i = 0;
for (final Object val : (JSONArray) value) {
for (final JSON val : (JSONArray) value) {
if (i > 0) {
sb.append(CharUtil.LF);
}
sb.append(EscapeUtil.escapeXml(val.toString()));
sb.append(EscapeUtil.escapeXml(val.toBean(String.class)));
i++;
}
} else {
sb.append(EscapeUtil.escapeXml(value.toString()));
sb.append(EscapeUtil.escapeXml(value.toBean(String.class)));
}
// Emit an array of similar keys
@ -102,7 +98,7 @@ public class JSONXMLSerializer {
} else if (StrUtil.isEmptyIfStr(value)) {
sb.append(wrapWithTag(null, key));
} else if (value instanceof JSONArray) {
for (final Object val : (JSONArray) value) {
for (final JSON val : (JSONArray) value) {
if (val instanceof JSONArray) {
sb.append(wrapWithTag(toXml(val, null, contentKeys), key));
} else {
@ -119,12 +115,8 @@ public class JSONXMLSerializer {
return sb.toString();
}
if (ArrayUtil.isArray(object)) {
object = JSONUtil.parseArray(object);
}
if (object instanceof JSONArray) {
for (final Object val : (JSONArray) object) {
if (json instanceof JSONArray) {
for (final JSON val : (JSONArray) json) {
// XML does not have good support for arrays. If an array
// appears in a place where XML is lacking, synthesize an
// <array> element.
@ -133,7 +125,7 @@ public class JSONXMLSerializer {
return sb.toString();
}
return wrapWithTag(EscapeUtil.escapeXml(object.toString()), tagName);
return wrapWithTag(EscapeUtil.escapeXml(json.toBean(String.class)), tagName);
}
/**

View File

@ -16,6 +16,7 @@
package org.dromara.hutool.json.xml;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONException;
import org.dromara.hutool.json.JSONObject;
@ -75,36 +76,36 @@ public class JSONXMLUtil {
/**
* 转换JSONObject为XML
*
* @param object JSON对象或数组
* @param json JSON对象或数组
* @return XML字符串
* @throws JSONException JSON解析异常
*/
public static String toXml(final Object object) throws JSONException {
return toXml(object, null);
public static String toXml(final JSON json) throws JSONException {
return toXml(json, null);
}
/**
* 转换JSONObject为XML
*
* @param object JSON对象或数组
* @param json JSON对象或数组
* @param tagName 可选标签名称名称为空时忽略标签
* @return A string.
* @throws JSONException JSON解析异常
*/
public static String toXml(final Object object, final String tagName) throws JSONException {
return toXml(object, tagName, "content");
public static String toXml(final JSON json, final String tagName) throws JSONException {
return toXml(json, tagName, "content");
}
/**
* 转换JSONObject为XML
*
* @param object JSON对象或数组
* @param json JSON对象或数组
* @param tagName 可选标签名称名称为空时忽略标签
* @param contentKeys 标识为内容的key,遇到此key直接解析内容而不增加对应名称标签
* @return A string.
* @throws JSONException JSON解析异常
*/
public static String toXml(final Object object, final String tagName, final String... contentKeys) throws JSONException {
return JSONXMLSerializer.toXml(object, tagName, contentKeys);
public static String toXml(final JSON json, final String tagName, final String... contentKeys) throws JSONException {
return JSONXMLSerializer.toXml(json, tagName, contentKeys);
}
}

View File

@ -17,6 +17,7 @@
package org.dromara.hutool.json;
import lombok.ToString;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.json.serializer.JSONDeserializer;
import org.dromara.hutool.json.serializer.JSONSerializer;
import org.dromara.hutool.json.serializer.TypeAdapterManager;
@ -31,8 +32,13 @@ public class CustomSerializeTest {
@BeforeEach
public void init() {
TypeAdapterManager.getInstance().register(CustomBean.class,
(JSONSerializer<CustomBean>) (bean, context) ->
((JSONObject)context.getContextJson()).set("customName", bean.name));
(JSONSerializer<CustomBean>) (bean, context) ->{
JSONObject contextJson = (JSONObject) context.getContextJson();
if(null == contextJson){
contextJson = JSONUtil.ofObj(context.config());
}
return contextJson.set("customName", bean.name);
});
}
@Test
@ -41,6 +47,7 @@ public class CustomSerializeTest {
customBean.name = "testName";
final JSONObject obj = JSONUtil.parseObj(customBean);
Console.log(obj);
Assertions.assertEquals("testName", obj.getStr("customName"));
}

View File

@ -20,10 +20,7 @@ import lombok.Data;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.*;
/**
* <a href="https://github.com/dromara/hutool/issues/2090">https://github.com/dromara/hutool/issues/2090</a>
@ -76,6 +73,23 @@ public class Issue2090Test {
final JSONObject jsonObject = new JSONObject();
jsonObject.set("month", Month.JANUARY);
Assertions.assertEquals("{\"month\":1}", jsonObject.toString());
final JSON parse = JSONUtil.parse(Month.JANUARY);
Assertions.assertInstanceOf(JSONPrimitive.class, parse);
Assertions.assertTrue(((JSONPrimitive) parse).isNumber());
Assertions.assertEquals("1", parse.toString());
}
@Test
public void weekTest(){
final JSONObject jsonObject = new JSONObject();
jsonObject.set("week", DayOfWeek.SUNDAY);
Assertions.assertEquals("{\"week\":7}", jsonObject.toString());
final JSON parse = JSONUtil.parse(DayOfWeek.SUNDAY);
Assertions.assertInstanceOf(JSONPrimitive.class, parse);
Assertions.assertTrue(((JSONPrimitive) parse).isNumber());
Assertions.assertEquals("7", parse.toString());
}
@Data

View File

@ -26,10 +26,10 @@ public class Issue2447Test {
@Test
public void addIntegerTest() {
Time time = new Time();
final Time time = new Time();
time.setTime(LocalDateTime.of(1970, 1, 2, 10, 0, 1, 0));
String timeStr = JSONUtil.toJsonStr(time);
Assertions.assertEquals(timeStr, "{\"time\":93601000}");
final String timeStr = JSONUtil.toJsonStr(time);
Assertions.assertEquals("{\"time\":93601000}", timeStr);
Assertions.assertEquals(JSONUtil.toBean(timeStr, Time.class).getTime(), time.getTime());
}

View File

@ -30,7 +30,7 @@ public class Issue3681Test {
Assertions.assertEquals("\"abc\"", abc);
abc = JSONUtil.toJsonStr(Optional.of("123"));
Assertions.assertEquals("123", abc);
Assertions.assertEquals("\"123\"", abc);
}
@Test
@ -45,6 +45,6 @@ public class Issue3681Test {
Assertions.assertEquals("\"abc\"", abc);
abc = JSONUtil.toJsonStr(Opt.of("123"));
Assertions.assertEquals("123", abc);
Assertions.assertEquals("\"123\"", abc);
}
}

View File

@ -16,7 +16,6 @@
package org.dromara.hutool.json;
import org.dromara.hutool.core.bean.BeanUtil;
import lombok.Data;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@ -31,8 +30,7 @@ public class IssueI3BS4STest {
@Test
public void toBeanTest(){
final String jsonStr = "{date: '2021-03-17T06:31:33.99'}";
final Bean1 bean1 = new Bean1();
BeanUtil.copyProperties(JSONUtil.parseObj(jsonStr), bean1);
final Bean1 bean1 = JSONUtil.parseObj(jsonStr).toBean(Bean1.class);
Assertions.assertEquals("2021-03-17T06:31:33.990", bean1.getDate().toString());
}

View File

@ -281,7 +281,7 @@ public class JSONArrayTest {
.set("value3")
.set(true);
final String s = json1.toJSONString(0, (pair) -> pair.getValue().equals("value2"));
final String s = json1.toJSONString(0, (pair) -> ((JSONPrimitive)pair.getValue()).getValue().equals("value2"));
assertEquals("[\"value2\"]", s);
}
@ -293,7 +293,7 @@ public class JSONArrayTest {
.set("value3")
.set(true);
final String s = json1.toJSONString(0, (pair) -> !pair.getValue().equals("value2"));
final String s = json1.toJSONString(0, (pair) -> !((JSONPrimitive)pair.getValue()).getValue().equals("value2"));
assertEquals("[\"value1\",\"value3\",true]", s);
}

View File

@ -22,6 +22,7 @@ import org.dromara.hutool.core.annotation.PropIgnore;
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.date.DatePattern;
import org.dromara.hutool.core.date.DateUtil;
import org.dromara.hutool.core.date.format.GlobalCustomFormat;
import org.dromara.hutool.core.io.resource.ResourceUtil;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.text.StrUtil;
@ -113,7 +114,7 @@ public class JSONObjectTest {
@Test
public void parseStringTest() {
final String jsonStr = "{\"b\":\"value2\",\"c\":\"value3\",\"a\":\"value1\", \"d\": true, \"e\": null}";
final JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
final JSONObject jsonObject = JSONUtil.parseObj(jsonStr, JSONConfig.of().setIgnoreNullValue(false));
assertEquals(jsonObject.getObj("a"), "value1");
assertEquals(jsonObject.getObj("b"), "value2");
assertEquals(jsonObject.getObj("c"), "value3");
@ -226,6 +227,13 @@ public class JSONObjectTest {
assertNull(bean.getBeanValue());
}
@Test
void addListTest(){
final JSONObject json = JSONUtil.ofObj();
json.set("list", ListUtil.of(1, 2, 3));
Assertions.assertEquals("{\"list\":[1,2,3]}", json.toString());
}
@Test
public void toBeanTest2() {
final UserA userA = new UserA();
@ -245,7 +253,7 @@ public class JSONObjectTest {
@Test
public void toBeanWithNullTest() {
final String jsonStr = "{'data':{'userName':'ak','password': null}}";
final UserWithMap user = JSONUtil.toBean(JSONUtil.parseObj(jsonStr), UserWithMap.class);
final UserWithMap user = JSONUtil.toBean(JSONUtil.parseObj(jsonStr, JSONConfig.of().setIgnoreNullValue(false)), UserWithMap.class);
Assertions.assertTrue(user.getData().containsKey("password"));
}
@ -323,7 +331,7 @@ public class JSONObjectTest {
}
@Test
public void parseBeanTest2() {
public void parseBeanWithNumberListEnumTest() {
final TestBean bean = new TestBean();
bean.setDoubleValue(111.1);
bean.setIntValue(123);
@ -333,8 +341,10 @@ public class JSONObjectTest {
final JSONObject json = JSONUtil.parseObj(bean,
JSONConfig.of().setIgnoreNullValue(false));
// 枚举转换检查更新枚举原样保存在writer时调用toString
assertEquals(TestEnum.TYPE_B, json.getObj("testEnum"));
assertEquals(111.1, json.getObj("doubleValue"));
// 枚举转换检查默认序列化枚举为其name
assertEquals(TestEnum.TYPE_B.name(), json.getObj("testEnum"));
final TestBean bean2 = json.toBean(TestBean.class);
assertEquals(bean.toString(), bean2.toString());
@ -479,9 +489,9 @@ public class JSONObjectTest {
}
@Test
public void setDateFormatTest3() {
public void setDateFormatSecondsTest() {
// 自定义格式为只有秒的时间戳一般用于JWT
final JSONConfig jsonConfig = JSONConfig.of().setDateFormat("#sss");
final JSONConfig jsonConfig = JSONConfig.of().setDateFormat(GlobalCustomFormat.FORMAT_SECONDS);
final Date date = DateUtil.parse("2020-06-05 11:16:11");
final JSONObject json = new JSONObject(jsonConfig);
@ -497,7 +507,7 @@ public class JSONObjectTest {
@Test
public void setCustomDateFormatTest() {
final JSONConfig jsonConfig = JSONConfig.of();
jsonConfig.setDateFormat("#sss");
jsonConfig.setDateFormat(GlobalCustomFormat.FORMAT_SECONDS);
final Date date = DateUtil.parse("2020-06-05 11:16:11");
final JSONObject json = new JSONObject(jsonConfig);
@ -609,7 +619,7 @@ public class JSONObjectTest {
@Test
public void createJSONObjectTest() {
Assertions.assertThrows(JSONException.class, ()->{
Assertions.assertThrows(ClassCastException.class, ()->{
// 集合类不支持转为JSONObject
JSONUtil.parseObj(new JSONArray(), JSONConfig.of());
});
@ -692,7 +702,8 @@ public class JSONObjectTest {
final String s = json1.toJSONString(0, (pair) -> {
if ("b".equals(pair.getKey())) {
// 修改值为新值
pair.setValue(pair.getValue() + "_edit");
final JSONPrimitive value = (JSONPrimitive) pair.getValue();
pair.setValue(value.getValue() + "_edit");
return true;
}
// 除了"b"其他都去掉

View File

@ -102,7 +102,7 @@ public class JSONUtilTest {
*/
@Test
public void parseNumberToJSONArrayTest() {
assertThrows(JSONException.class, () -> {
assertThrows(ClassCastException.class, () -> {
final JSONArray json = JSONUtil.parseArray(123L);
Assertions.assertNotNull(json);
});

View File

@ -31,7 +31,7 @@ public class JSONWriterTest {
// 日期原样写入
final Date date = jsonObject.getDate("date");
Assertions.assertEquals("2022-09-30 00:00:00", date.toString());
Assertions.assertEquals("2022-09-30 00:00:00", DateUtil.date(date).toString());
// 自定义日期格式生效
Assertions.assertEquals("{\"date\":\"2022-09-30\"}", jsonObject.toString());

View File

@ -161,7 +161,7 @@ public class JWTTest {
Assertions.assertEquals(bean, beanRes);
Assertions.assertEquals(numRes, num);
Assertions.assertEquals(username, strRes);
Assertions.assertEquals(list, listRes);
Assertions.assertEquals(list.toString(), listRes.toString());
final String formattedDate = DateUtil.format(date, "yyyy-MM-dd HH:mm:ss");
final String formattedRes = DateUtil.format(dateRes, "yyyy-MM-dd HH:mm:ss");

View File

@ -39,7 +39,7 @@ public class XMLTest {
Assertions.assertEquals("{\"a\":\"\"}", jsonObject.toString());
final String xml2 = JSONXMLUtil.toXml(JSONUtil.parseObj(jsonObject));
final String xml2 = JSONXMLUtil.toXml(jsonObject);
Assertions.assertEquals(xml, xml2);
}
@ -53,4 +53,11 @@ public class XMLTest {
xml = JSONXMLUtil.toXml(jsonObject, null, new String[0]);
Assertions.assertEquals("<content>123456</content>", xml);
}
@Test
public void xmlContentTest2(){
final JSONObject jsonObject = JSONUtil.ofObj().set("content","123456");
final String xml = JSONXMLUtil.toXml(jsonObject, null, new String[0]);
Assertions.assertEquals("<content>123456</content>", xml);
}
}