change Serializer style

This commit is contained in:
Looly 2024-09-12 02:10:19 +08:00
parent 27ff80d71f
commit 5409001e34
32 changed files with 626 additions and 561 deletions

View File

@ -25,20 +25,15 @@ import org.dromara.hutool.core.lang.mutable.MutableEntry;
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.reflect.ConstructorUtil;
import org.dromara.hutool.core.reflect.TypeUtil;
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.json.reader.JSONTokener;
import org.dromara.hutool.json.serialize.GlobalSerializeMapping;
import org.dromara.hutool.json.serialize.JSONDeserializer;
import org.dromara.hutool.json.serialize.JSONStringer;
import org.dromara.hutool.json.serializer.JSONStringer;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Predicate;
@ -309,25 +304,6 @@ public final class InternalJSONUtil {
return rawHashMap;
}
/**
* 根据目标类型获取对应的{@link JSONDeserializer}首先判断是否实现了{@link JSONDeserializer}接口<br>
* 如果未实现从{@link GlobalSerializeMapping}中查找全局的{@link JSONDeserializer}否则返回null
*
* @param targetType 目标类型
* @param <T> 目标类型
* @return {@link JSONDeserializer}
*/
@SuppressWarnings("unchecked")
public static <T> JSONDeserializer<T> getDeserializer(final Type targetType) {
final Class<T> rawType = (Class<T>) TypeUtil.getClass(targetType);
if (null != rawType && JSONDeserializer.class.isAssignableFrom(rawType)) {
return (JSONDeserializer<T>) ConstructorUtil.newInstanceIfPossible(rawType);
}
// 全局自定义反序列化优先级低于实现JSONDeserializer接口
return (JSONDeserializer<T>) GlobalSerializeMapping.getDeserializer(targetType);
}
// --------------------------------------------------------------------------------------------- Private method start
/**

View File

@ -303,9 +303,26 @@ public class JSONArray implements JSON, JSONGetter<Integer>, List<Object>, Rando
*
* @return Iterable
* @since 4.0.12
* @param <T> JSON类型
* @param type JSON类型
*/
public Iterable<JSONObject> jsonIter() {
return new JSONObjectIter(iterator());
public <T extends JSON> Iterable<T> jsonIter(final Class<T> type) {
final Iterator<Object> iterator = iterator();
return () -> new Iterator<T>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public T next() {
return type.cast(iterator.next());
}
@Override
public void remove() {
iterator.remove();
}
};
}
@Override

View File

@ -1,61 +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;
import java.util.Iterator;
/**
* 此类用于在JSONAray中便于遍历JSONObject而封装的Iterable可以借助foreach语法遍历
*
* @author looly
* @since 4.0.12
*/
public class JSONObjectIter implements Iterable<JSONObject> {
Iterator<Object> iterator;
/**
* 构造
*
* @param iterator 迭代器
*/
public JSONObjectIter(final Iterator<Object> iterator) {
this.iterator = iterator;
}
@Override
public Iterator<JSONObject> iterator() {
return new Iterator<JSONObject>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public JSONObject next() {
return (JSONObject) iterator.next();
}
@Override
public void remove() {
iterator.remove();
}
};
}
}

View File

@ -1,78 +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;
import org.dromara.hutool.core.bean.copier.BeanCopier;
import org.dromara.hutool.json.serialize.JSONDeserializer;
import org.dromara.hutool.json.serialize.JSONStringer;
/**
* JSON支持<br>
* 继承此类实现实体类与JSON的相互转换
*
* @author Looly
*/
public class JSONSupport implements JSONStringer, JSONDeserializer<Object> {
/**
* JSON String转Bean
*
* @param jsonString JSON String
*/
public void deserialize(final String jsonString) {
deserialize(new JSONObject(jsonString));
}
/**
* JSON转Bean
*
* @param json JSON
*/
@Override
public Object deserialize(final JSON json) {
BeanCopier.of(json,
this, this.getClass(),
InternalJSONUtil.toCopyOptions(json.config())).copy();
return this;
}
/**
* @return JSON对象
*/
public JSONObject toJSON() {
return new JSONObject(this);
}
@Override
public String toJSONString() {
return toJSON().toString();
}
/**
* 美化的JSON使用回车缩进显示JSON用于打印输出debug
*
* @return 美化的JSON
*/
public String toPrettyString() {
return toJSON().toStringPretty();
}
@Override
public String toString() {
return toJSONString();
}
}

View File

@ -23,17 +23,12 @@ 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.convert.JSONConverter;
import org.dromara.hutool.json.serialize.GlobalSerializeMapping;
import org.dromara.hutool.json.serialize.JSONArraySerializer;
import org.dromara.hutool.json.serialize.JSONDeserializer;
import org.dromara.hutool.json.serialize.JSONObjectSerializer;
import org.dromara.hutool.json.writer.GlobalValueWriters;
import org.dromara.hutool.json.writer.JSONValueWriter;
import org.dromara.hutool.json.writer.JSONWriter;
import org.dromara.hutool.json.xml.JSONXMLUtil;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Type;
@ -242,12 +237,8 @@ public class JSONUtil {
* @param obj Bean对象
* @return JSON字符串
*/
public static String toJsonPrettyStr(Object obj) {
obj = parse(obj);
if (obj instanceof JSON) {
return ((JSON) obj).toStringPretty();
}
return StrUtil.toStringOrNull(obj);
public static String toJsonPrettyStr(final Object obj) {
return parse(obj).toStringPretty();
}
/**
@ -292,19 +283,9 @@ public class JSONUtil {
* @param writer Writer
* @since 5.3.3
*/
public static void toJsonStr(Object obj, final Writer writer) {
public static void toJsonStr(final Object obj, final Writer writer) {
if (null != obj) {
obj = parse(obj);
if (obj instanceof JSON) {
((JSON) obj).write(writer);
}
// 普通值
try {
writer.write(obj.toString());
} catch (final IOException e) {
throw new IORuntimeException(e);
}
parse(obj).write(writer);
}
}
@ -555,40 +536,4 @@ public class JSONUtil {
}
return StrUtil.isWrap(StrUtil.trim(str), '[', ']');
}
/**
* 加入自定义的序列化器
*
* @param type 对象类型
* @param serializer 序列化器实现
* @see GlobalSerializeMapping#putSerializer(Type, JSONObjectSerializer)
* @since 6.0.0
*/
public static void putSerializer(final Type type, final JSONObjectSerializer<?> serializer) {
GlobalSerializeMapping.putSerializer(type, serializer);
}
/**
* 加入自定义的序列化器
*
* @param type 对象类型
* @param serializer 序列化器实现
* @see GlobalSerializeMapping#putSerializer(Type, JSONArraySerializer)
* @since 6.0.0
*/
public static void putSerializer(final Type type, final JSONArraySerializer<?> serializer) {
GlobalSerializeMapping.putSerializer(type, serializer);
}
/**
* 加入自定义的反序列化器
*
* @param type 对象类型
* @param deserializer 反序列化器实现
* @see GlobalSerializeMapping#putDeserializer(Type, JSONDeserializer)
* @since 4.6.5
*/
public static void putDeserializer(final Type type, final JSONDeserializer<?> deserializer) {
GlobalSerializeMapping.putDeserializer(type, deserializer);
}
}

View File

@ -32,8 +32,7 @@ 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.serialize.JSONDeserializer;
import org.dromara.hutool.json.serialize.JSONStringer;
import org.dromara.hutool.json.serializer.*;
import java.io.Serializable;
import java.lang.reflect.Type;
@ -139,6 +138,7 @@ public class JSONConverter implements Converter, Serializable {
* @return 转换后的对象
* @throws JSONException 转换异常
*/
@SuppressWarnings("unchecked")
public JSON toJSON(Object obj) throws JSONException {
if (null == obj) {
return null;
@ -154,6 +154,22 @@ public class JSONConverter implements Converter, Serializable {
return (JSON) obj;
}
// 自定义序列化
final JSONSerializer<Object> serializer =
(JSONSerializer<Object>) SerializerManager.getInstance().getSerializer(obj);
if (null != serializer) {
return serializer.serialize(obj, new JSONContext() {
@Override
public JSON getContextJson() {
return null;
}
@Override
public JSONConfig config() {
return JSONConverter.this.config;
}
});
}
if (obj instanceof Number || obj instanceof Boolean) {
// RFC8259规范的原始类型数据
return new JSONPrimitive(obj, config);
@ -223,10 +239,10 @@ public class JSONConverter implements Converter, Serializable {
@SuppressWarnings("unchecked")
private <T> T toBean(final Type targetType, final JSON json) {
// 自定义对象反序列化
final JSONDeserializer<Object> deserializer = InternalJSONUtil.getDeserializer(targetType);
final JSONDeserializer<?> deserializer = SerializerManager.getInstance().getDeserializer(targetType);
if (null != deserializer) {
return (T) deserializer.deserialize(json);
return (T) deserializer.deserialize(json, targetType);
}
// 当目标类型不确定时返回原JSON

View File

@ -16,16 +16,18 @@
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.Mutable;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.json.*;
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 org.dromara.hutool.json.serialize.GlobalSerializeMapping;
import org.dromara.hutool.json.serialize.JSONSerializer;
import org.dromara.hutool.json.serializer.JSONSerializer;
import org.dromara.hutool.json.serializer.SerializerManager;
import java.io.InputStream;
import java.io.Reader;
@ -79,7 +81,7 @@ public class JSONArrayMapper {
* @param jsonArray 目标{@link JSONArray}
* @throws JSONException 非数组或集合
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@SuppressWarnings("unchecked")
public void mapTo(final JSONArray jsonArray) throws JSONException {
final Object source = this.source;
if (null == source) {
@ -87,9 +89,9 @@ public class JSONArrayMapper {
}
// 自定义序列化
final JSONSerializer serializer = GlobalSerializeMapping.getSerializer(source.getClass());
final JSONSerializer<Object> serializer = SerializerManager.getInstance().getSerializer(source.getClass());
if (null != serializer) {
serializer.serialize(jsonArray, source);
serializer.serialize(source, ()-> jsonArray);
return;
}

View File

@ -27,8 +27,8 @@ 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.serialize.GlobalSerializeMapping;
import org.dromara.hutool.json.serialize.JSONSerializer;
import org.dromara.hutool.json.serializer.JSONSerializer;
import org.dromara.hutool.json.serializer.SerializerManager;
import org.dromara.hutool.json.xml.JSONXMLParser;
import org.dromara.hutool.json.xml.ParseConfig;
@ -88,7 +88,7 @@ public class JSONObjectMapper {
*
* @param jsonObject 目标{@link JSONObject}
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@SuppressWarnings("rawtypes")
public void mapTo(final JSONObject jsonObject) {
final Object source = this.source;
if (null == source) {
@ -96,9 +96,9 @@ public class JSONObjectMapper {
}
// 自定义序列化
final JSONSerializer serializer = GlobalSerializeMapping.getSerializer(source.getClass());
final JSONSerializer<Object> serializer = SerializerManager.getInstance().getSerializer(source.getClass());
if (null != serializer) {
serializer.serialize(jsonObject, source);
serializer.serialize(source, () -> jsonObject);
return;
}

View File

@ -22,7 +22,7 @@ import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONArray;
import org.dromara.hutool.json.JSONConfig;
import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.serialize.JSONStringer;
import org.dromara.hutool.json.serializer.JSONStringer;
import org.dromara.hutool.json.writer.GlobalValueWriters;
import java.io.Serializable;

View File

@ -1,131 +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.serialize;
import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap;
import org.dromara.hutool.core.reflect.NullType;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.JSON;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 全局的序列化和反序列化器映射<br>
* 在JSON和Java对象转换过程中优先使用注册于此处的自定义转换<br>
* 分别定义{@link JSONObjectSerializer}{@link JSONArraySerializer}的原因是实际加入对象到JSON中时无法区分是JSONObject还是JSONArray
*
* @author Looly
*/
public class GlobalSerializeMapping {
private static Map<Type, JSONSerializer<? extends JSON, ?>> serializerMap;
private static Map<Type, JSONDeserializer<?>> deserializerMap;
static {
serializerMap = new SafeConcurrentHashMap<>();
deserializerMap = new SafeConcurrentHashMap<>();
final TemporalAccessorSerializer localDateSerializer = new TemporalAccessorSerializer(LocalDate.class);
serializerMap.put(LocalDate.class, localDateSerializer);
deserializerMap.put(LocalDate.class, localDateSerializer);
final TemporalAccessorSerializer localDateTimeSerializer = new TemporalAccessorSerializer(LocalDateTime.class);
serializerMap.put(LocalDateTime.class, localDateTimeSerializer);
deserializerMap.put(LocalDateTime.class, localDateTimeSerializer);
final TemporalAccessorSerializer localTimeSerializer = new TemporalAccessorSerializer(LocalTime.class);
serializerMap.put(LocalTime.class, localTimeSerializer);
deserializerMap.put(LocalTime.class, localTimeSerializer);
}
/**
* 加入自定义的JSONArray序列化器
*
* @param type 对象类型
* @param serializer 序列化器实现
*/
public static void putSerializer(final Type type, final JSONArraySerializer<?> serializer) {
putInternal(type, serializer);
}
/**
* 加入自定义的JSONObject序列化器
*
* @param type 对象类型
* @param serializer 序列化器实现
*/
public static void putSerializer(final Type type, final JSONObjectSerializer<?> serializer) {
putInternal(type, serializer);
}
/**
* 加入自定义的反序列化器
*
* @param type 对象类型
* @param deserializer 反序列化器实现
*/
synchronized public static void putDeserializer(final Type type, final JSONDeserializer<?> deserializer) {
if (null == deserializerMap) {
deserializerMap = new ConcurrentHashMap<>();
}
deserializerMap.put(ObjUtil.defaultIfNull(type, NullType.INSTANCE), deserializer);
}
/**
* 获取自定义的序列化器如果未定义返回{@code null}
*
* @param type 类型
* @return 自定义的序列化器或者{@code null}
*/
public static JSONSerializer<? extends JSON, ?> getSerializer(final Type type) {
if (null == serializerMap || null == type) {
return null;
}
return serializerMap.get(ObjUtil.defaultIfNull(type, NullType.INSTANCE));
}
/**
* 获取自定义的反序列化器如果未定义返回{@code null}
*
* @param type 类型
* @return 自定义的反序列化器或者{@code null}
*/
public static JSONDeserializer<?> getDeserializer(final Type type) {
if (null == deserializerMap || null == type) {
return null;
}
return deserializerMap.get(ObjUtil.defaultIfNull(type, NullType.INSTANCE));
}
/**
* 加入自定义的序列化器
*
* @param type 对象类型
* @param serializer 序列化器实现
*/
synchronized private static void putInternal(final Type type, final JSONSerializer<? extends JSON, ?> serializer) {
if (null == serializerMap) {
serializerMap = new ConcurrentHashMap<>();
}
serializerMap.put(ObjUtil.defaultIfNull(type, NullType.INSTANCE), serializer);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
* 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.
@ -14,16 +14,32 @@
* limitations under the License.
*/
package org.dromara.hutool.json.serialize;
package org.dromara.hutool.json.serializer;
import org.dromara.hutool.json.JSONArray;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONConfig;
/**
* JSON列表的序列化接口用于将特定对象序列化为{@link JSONArray}
* JSON序列化上下文用于获取当前JSON对象以便在序列化过程中获取配置信息
*
* @param <V> 对象类型
*
* @author Looly
* @author looly
* @since 6.0.0
*/
@FunctionalInterface
public interface JSONArraySerializer<V> extends JSONSerializer<JSONArray, V>{}
public interface JSONContext {
/**
* 获取当前JSON对象
*
* @return JSON对象
*/
JSON getContextJson();
/**
* 获取JSON配置
*
* @return JSON配置
*/
default JSONConfig config() {
return getContextJson().config();
}
}

View File

@ -14,30 +14,32 @@
* limitations under the License.
*/
package org.dromara.hutool.json.serialize;
package org.dromara.hutool.json.serializer;
import org.dromara.hutool.json.JSON;
import java.lang.reflect.Type;
/**
* JSON自定义反序列化接口实现JSON to Bean使用方式为
* JSON自定义反序列化接口实现{@link JSON} to Bean使用方式为
* <ul>
* <li>定义好反序列化规则使用{@link GlobalSerializeMapping#putDeserializer(Type, JSONDeserializer)}关联指定类型与转换器实现反序列化</li>
* <li>使Bean实现此接口调用{@link #deserialize(JSON)}解析字段返回this即可</li>
* <li>定义好反序列化规则关联指定类型与转换器实现反序列化</li>
* <li>使Bean实现此接口调用{@link #deserialize(JSON, Type)}解析字段返回this即可</li>
* </ul>
*
* @param <T> 反序列化后的类型
* @param <V> 反序列化后的类型
* @author Looly
*/
@FunctionalInterface
public interface JSONDeserializer<T> {
public interface JSONDeserializer<V> {
/**
* 反序列化通过实现此方法自定义实现JSON转换为指定类型的逻辑
* 反序列化通过实现此方法自定义实现JSON转换为指定类型的逻辑<br>
* deserializeType用于指明当结果对象存在泛型时可以获取泛型对应的实际类型
*
* @param json {@link JSON}
* @param deserializeType 反序列化类型
* @return 目标对象
*/
T deserialize(JSON json);
V deserialize(JSON json, Type deserializeType);
}

View File

@ -14,26 +14,30 @@
* limitations under the License.
*/
package org.dromara.hutool.json.serialize;
package org.dromara.hutool.json.serializer;
import org.dromara.hutool.json.JSON;
/**
* 序列化接口通过实现此接口实现自定义的对象转换为JSON的操作
*
* @param <T> JSON类型可以是JSONObject或者JSONArray
* @param <V> 对象类型
* @author Looly
*/
@FunctionalInterface
public interface JSONSerializer<T extends JSON, V> {
public interface JSONSerializer<V> {
/**
* 序列化实现通过实现此方法将指定类型的对象转换为{@link JSON}对象<br>
* 转换后的对象可以为JSONObject也可以为JSONArray首先new一个空的JSON然后将需要的数据字段put到JSON对象中去即可
* 序列化实现通过实现此方法将指定类型的对象转换为{@link JSON}对象,可以
* <ul>
* <li>如果为原始类型可以转为{@link org.dromara.hutool.json.JSONPrimitive}</li>
* <li>如果是集合或数组类可以转为{@link org.dromara.hutool.json.JSONArray}</li>
* <li>如果是Bean或键值对类型可以转为{@link org.dromara.hutool.json.JSONObject}</li>
* </ul>
*
* @param json JSON可以为JSONObject或者JSONArray
* @param bean 指定类型对象
* @param bean 指定类型对象
* @param context JSON上下文用于获取当前json节点或配置等信息
* @return JSON
*/
void serialize(T json, V bean);
JSON serialize(V bean, JSONContext context);
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.dromara.hutool.json.serialize;
package org.dromara.hutool.json.serializer;
import org.dromara.hutool.core.lang.wrapper.Wrapper;

View File

@ -0,0 +1,41 @@
/*
* 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;
import org.dromara.hutool.json.JSON;
import java.lang.reflect.Type;
/**
* 带有匹配方法的 JSON反序列化器<br>
* 匹配方法返回true表示匹配反序列化器将执行反序列化操作
*
* @param <V> 反序列化结果类型
* @author looly
* @since 6.0.0
*/
public interface MatcherJSONDeserializer<V> extends JSONDeserializer<V> {
/**
* 匹配反序列化器是否匹配
*
* @param json JSON对象
* @param deserializeType 反序列化类型
* @return 是否匹配
*/
boolean match(JSON json, Type deserializeType);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
* 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.
@ -14,16 +14,23 @@
* limitations under the License.
*/
package org.dromara.hutool.json.serialize;
import org.dromara.hutool.json.JSONPrimitive;
package org.dromara.hutool.json.serializer;
/**
* 原始对象的序列化接口用于将特定对象序列化为{@link JSONPrimitive}
* 匹配JSON序列化器用于判断是否匹配匹配则执行序列化
*
* @param <V> 对象类型
* @param <V> JSON对象类型
* @author Looly
* @since 6.0.0
*/
@FunctionalInterface
public interface JSONPrimitiveSerializer<V> extends JSONSerializer<JSONPrimitive, V> {
public interface MatcherJSONSerializer<V> extends JSONSerializer<V> {
/**
* 判断是否匹配
*
* @param bean 对象
* @param context JSON上下文
* @return 是否匹配
*/
boolean match(V bean, JSONContext context);
}

View File

@ -0,0 +1,270 @@
/*
* 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;
import org.dromara.hutool.core.collection.set.ConcurrentHashSet;
import org.dromara.hutool.core.lang.Assert;
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.serializer.impl.TemporalAccessorSerializer;
import org.dromara.hutool.json.serializer.impl.TimeZoneSerializer;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
/**
* JSON序列化和反序列化管理器用于管理JSON序列化器注册和注销自定义序列化器和反序列化器
*
* @author looly
* @since 6.0.0
*/
public class SerializerManager {
/**
* 类级的内部类也就是静态的成员式内部类该内部类的实例与外部类的实例 没有绑定关系而且只有被调用到才会装载从而实现了延迟加载
*/
private static class SingletonHolder {
/**
* 静态初始化器由JVM来保证线程安全
*/
private static final SerializerManager INSTANCE = new SerializerManager();
static {
registerDefault();
}
}
/**
* 获得单例的 SerializerManager
*
* @return SerializerManager
*/
public static SerializerManager getInstance() {
return SerializerManager.SingletonHolder.INSTANCE;
}
/**
* 用户自定义序列化器存储自定义匹配规则的一类对象的转换器
*/
private volatile Set<MatcherJSONSerializer<?>> serializerSet;
/**
* 用户自定义精确类型转换器<br>
* 主要存储类型明确无子类的转换器
*/
private volatile Map<Type, JSONSerializer<?>> serializerMap;
/**
* 用户自定义类型转换器存储自定义匹配规则的一类对象的转换器
*/
private volatile Set<MatcherJSONDeserializer<?>> deserializerSet;
/**
* 用户自定义精确类型转换器<br>
* 主要存储类型明确无子类的转换器
*/
private volatile Map<Type, JSONDeserializer<?>> deserializerMap;
/**
* 构造
*/
public SerializerManager() {
}
// region ----- register
/**
* 注册自定义序列化器用于自定义对象序列化<br>
* 当按照匹配规则匹配时使用对应的序列化器进行序列化
*
* @param serializer 自定义序列化器
* @return this
*/
public SerializerManager register(final MatcherJSONSerializer<?> serializer) {
if (null != serializer) {
getSerializerSet().add(serializer);
}
return this;
}
/**
* 注册自定义序列化器用于自定义对象序列化<br>
* 当类型精准匹配时使用对应的序列化器进行序列化
*
* @param type 类型
* @param serializer 自定义序列化器{@code null}表示移除
* @return this
*/
public SerializerManager register(final Type type, final JSONSerializer<?> serializer) {
Assert.notNull(type);
if (null == serializer) {
getSerializerMap().remove(type);
} else {
getSerializerMap().put(type, serializer);
}
return this;
}
/**
* 注册自定义反序列化器用于自定义对象反序列化<br>
* 当按照匹配规则匹配时使用对应的反序列化器进行反序列化
*
* @param deserializer 自定义反序列化器
* @return this
*/
public SerializerManager register(final MatcherJSONDeserializer<?> deserializer) {
if (null != deserializer) {
getDeserializerSet().add(deserializer);
}
return this;
}
/**
* 注册自定义反序列化器用于自定义对象反序列化<br>
* 当类型精准匹配时使用对应的反序列化器进行反序列化
*
* @param type 类型{@code null}表示
* @param deserializer 自定义反序列化器{@code null}表示移除
* @return this
*/
public SerializerManager register(final Type type, final JSONDeserializer<?> deserializer) {
Assert.notNull(type);
if (null == deserializer) {
getDeserializerMap().remove(type);
} else {
getDeserializerMap().put(type, deserializer);
}
return this;
}
// endregion
// region ----- getSerializer or Deserializer
/**
* 获取匹配器对应的序列化器
*
* @param bean 对象
* @return JSONSerializer
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public JSONSerializer<?> getSerializer(final Object bean) {
for (final MatcherJSONSerializer serializer : this.serializerSet) {
if (serializer.match(bean, null)) {
return serializer;
}
}
return null;
}
/**
* 获取匹配器对应的序列化器
*
* @param type 类型
* @return JSONSerializer
*/
@SuppressWarnings("unchecked")
public JSONSerializer<Object> getSerializer(final Type type) {
if(null == type){
return null;
}
return (JSONSerializer<Object>) getSerializerMap().get(type);
}
/**
* 获取匹配器对应的反序列化器
*
* @param type 类型
* @return JSONDeserializer
*/
@SuppressWarnings("unchecked")
public JSONDeserializer<Object> getDeserializer(final Type type) {
final Class<?> rawType = TypeUtil.getClass(type);
if(null == rawType){
return null;
}
if (JSONDeserializer.class.isAssignableFrom(rawType)) {
return (JSONDeserializer<Object>) ConstructorUtil.newInstanceIfPossible(rawType);
}
return (JSONDeserializer<Object>) getDeserializerMap().get(type);
}
// endregion
// region ----- getSet or Map
private Set<MatcherJSONSerializer<?>> getSerializerSet() {
if (null == this.serializerSet) {
synchronized (this) {
if (null == this.serializerSet) {
this.serializerSet = new ConcurrentHashSet<>();
}
}
}
return this.serializerSet;
}
private Map<Type, JSONSerializer<?>> getSerializerMap() {
if (null == this.serializerMap) {
synchronized (this) {
if (null == this.serializerMap) {
this.serializerMap = new SafeConcurrentHashMap<>();
}
}
}
return this.serializerMap;
}
private Set<MatcherJSONDeserializer<?>> getDeserializerSet() {
if (null == this.deserializerSet) {
synchronized (this) {
if (null == this.deserializerSet) {
this.deserializerSet = new ConcurrentHashSet<>();
}
}
}
return this.deserializerSet;
}
private Map<Type, JSONDeserializer<?>> getDeserializerMap() {
if (null == this.deserializerMap) {
synchronized (this) {
if (null == this.deserializerMap) {
this.deserializerMap = new SafeConcurrentHashMap<>();
}
}
}
return this.deserializerMap;
}
/**
* 注册默认的序列化器和反序列化器
*/
private static void registerDefault() {
SingletonHolder.INSTANCE.register(LocalDate.class, (JSONSerializer<?>) new TemporalAccessorSerializer(LocalDate.class));
SingletonHolder.INSTANCE.register(LocalDate.class, (JSONDeserializer<?>) new TemporalAccessorSerializer(LocalDate.class));
SingletonHolder.INSTANCE.register(LocalTime.class, (JSONSerializer<?>) new TemporalAccessorSerializer(LocalTime.class));
SingletonHolder.INSTANCE.register(LocalTime.class, (JSONDeserializer<?>) new TemporalAccessorSerializer(LocalTime.class));
SingletonHolder.INSTANCE.register(LocalDateTime.class, (JSONSerializer<?>) new TemporalAccessorSerializer(LocalDateTime.class));
SingletonHolder.INSTANCE.register(LocalDateTime.class, (JSONDeserializer<?>) new TemporalAccessorSerializer(LocalDateTime.class));
SingletonHolder.INSTANCE.register((MatcherJSONSerializer<TimeZone>) TimeZoneSerializer.INSTANCE);
SingletonHolder.INSTANCE.register((MatcherJSONDeserializer<TimeZone>) TimeZoneSerializer.INSTANCE);
}
// endregion
}

View File

@ -14,14 +14,18 @@
* limitations under the License.
*/
package org.dromara.hutool.json.serialize;
package org.dromara.hutool.json.serializer.impl;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.math.NumberUtil;
import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONException;
import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.serializer.JSONContext;
import org.dromara.hutool.json.serializer.JSONDeserializer;
import org.dromara.hutool.json.serializer.JSONSerializer;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
@ -39,7 +43,7 @@ import java.time.temporal.TemporalAccessor;
* @author looly
* @since 5.7.22
*/
public class TemporalAccessorSerializer implements JSONSerializer<JSONObject, TemporalAccessor>, JSONDeserializer<TemporalAccessor> {
public class TemporalAccessorSerializer implements JSONSerializer<TemporalAccessor>, JSONDeserializer<TemporalAccessor> {
private static final String YEAR_KEY = "year";
private static final String MONTH_KEY = "month";
@ -61,7 +65,14 @@ public class TemporalAccessorSerializer implements JSONSerializer<JSONObject, Te
}
@Override
public void serialize(final JSONObject json, final TemporalAccessor bean) {
public JSON serialize(final TemporalAccessor bean, final JSONContext context) {
final JSONObject json;
final JSON contextJson = context.getContextJson();
if(contextJson instanceof JSONObject){
json = (JSONObject) contextJson;
}else {
json = new JSONObject(7F / 0.75F + 1F, context.config());
}
if (bean instanceof LocalDate) {
final LocalDate localDate = (LocalDate) bean;
json.set(YEAR_KEY, localDate.getYear());
@ -83,12 +94,13 @@ public class TemporalAccessorSerializer implements JSONSerializer<JSONObject, Te
json.set(SECOND_KEY, localTime.getSecond());
json.set(NANO_KEY, localTime.getNano());
} else {
throw new JSONException("Unsupported type to JSON: {}", bean.getClass().getName());
throw new JSONException("Unsupported TemporalAccessor type to JSON: {}", bean.getClass().getName());
}
return json;
}
@Override
public TemporalAccessor deserialize(final JSON json) {
public TemporalAccessor deserialize(final JSON json, final Type deserializeType) {
final JSONObject jsonObject = (JSONObject) json;
if (LocalDate.class.equals(this.temporalAccessorClass) || LocalDateTime.class.equals(this.temporalAccessorClass)) {
//

View File

@ -0,0 +1,62 @@
/*
* 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.json.JSON;
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;
import java.util.TimeZone;
/**
* 时区序列化器
*
* @author looly
* @since 6.0.0
*/
public class TimeZoneSerializer implements MatcherJSONSerializer<TimeZone>, MatcherJSONDeserializer<TimeZone> {
/**
* 单例
*/
public static final TimeZoneSerializer INSTANCE = new TimeZoneSerializer();
@Override
public boolean match(final JSON json, final Type deserializeType) {
return TimeZone.class.isAssignableFrom(TypeUtil.getClass(deserializeType));
}
@SuppressWarnings("DataFlowIssue")
@Override
public boolean match(final TimeZone bean, final JSONContext context) {
return bean instanceof TimeZone;
}
@Override
public JSON serialize(final TimeZone bean, final JSONContext context) {
return new JSONPrimitive(bean.getID());
}
@Override
public TimeZone deserialize(final JSON json, final Type deserializeType) {
return TimeZone.getTimeZone(json.toString());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
* 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.
@ -14,15 +14,9 @@
* limitations under the License.
*/
package org.dromara.hutool.json.serialize;
import org.dromara.hutool.json.JSONObject;
/**
* 对象的序列化接口用于将特定对象序列化为{@link JSONObject}
* @param <V> 对象类型
* 默认实现的序列化和反序列化
*
* @author Looly
*/
@FunctionalInterface
public interface JSONObjectSerializer<V> extends JSONSerializer<JSONObject, V>{}
package org.dromara.hutool.json.serializer.impl;

View File

@ -28,4 +28,4 @@
* <li>反序列化DeserializeJSON对象 转换为 Java对象</li>
* </ul>
*/
package org.dromara.hutool.json.serialize;
package org.dromara.hutool.json.serializer;

View File

@ -17,7 +17,7 @@
package org.dromara.hutool.json.writer;
import org.dromara.hutool.json.JSONException;
import org.dromara.hutool.json.serialize.JSONStringer;
import org.dromara.hutool.json.serializer.JSONStringer;
/**
* {@link JSONStringer}的值写出器

View File

@ -16,8 +16,10 @@
package org.dromara.hutool.json;
import org.dromara.hutool.json.serialize.JSONObjectSerializer;
import lombok.ToString;
import org.dromara.hutool.json.serializer.JSONDeserializer;
import org.dromara.hutool.json.serializer.JSONSerializer;
import org.dromara.hutool.json.serializer.SerializerManager;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -27,8 +29,10 @@ import java.util.Date;
public class CustomSerializeTest {
@BeforeEach
public void init(){
JSONUtil.putSerializer(CustomBean.class, (JSONObjectSerializer<CustomBean>) (json, bean) -> json.set("customName", bean.name));
public void init() {
SerializerManager.getInstance().register(CustomBean.class,
(JSONSerializer<CustomBean>) (bean, context) ->
((JSONObject)context.getContextJson()).set("customName", bean.name));
}
@Test
@ -51,9 +55,9 @@ public class CustomSerializeTest {
@Test
public void deserializeTest() {
JSONUtil.putDeserializer(CustomBean.class, json -> {
SerializerManager.getInstance().register(CustomBean.class, (JSONDeserializer<CustomBean>) (json, deserializeType) -> {
final CustomBean customBean = new CustomBean();
customBean.name = ((JSONObject)json).getStr("customName");
customBean.name = ((JSONObject) json).getStr("customName");
return customBean;
});

View File

@ -16,17 +16,21 @@
package org.dromara.hutool.json;
import org.dromara.hutool.json.serialize.JSONDeserializer;
import org.dromara.hutool.json.serialize.JSONObjectSerializer;
import lombok.Data;
import org.dromara.hutool.json.serializer.JSONContext;
import org.dromara.hutool.json.serializer.JSONDeserializer;
import org.dromara.hutool.json.serializer.JSONSerializer;
import org.dromara.hutool.json.serializer.SerializerManager;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Type;
public class Issue2555Test {
@Test
public void serAndDeserTest(){
JSONUtil.putSerializer(MyType.class, new MySerializer());
JSONUtil.putDeserializer(MyType.class, new MyDeserializer());
SerializerManager.getInstance().register(MyType.class, new MySerializer());
SerializerManager.getInstance().register(MyType.class, new MyDeserializer());
final SimpleObj simpleObj = new SimpleObj();
final MyType child = new MyType();
@ -50,16 +54,16 @@ public class Issue2555Test {
private MyType myType;
}
public static class MySerializer implements JSONObjectSerializer<MyType> {
public static class MySerializer implements JSONSerializer<MyType> {
@Override
public void serialize(final JSONObject json, final MyType bean) {
json.set("addr", bean.getAddress());
public JSON serialize(final MyType bean, final JSONContext context) {
return ((JSONObject)context.getContextJson()).set("addr", bean.getAddress());
}
}
public static class MyDeserializer implements JSONDeserializer<MyType> {
@Override
public MyType deserialize(final JSON json) {
public MyType deserialize(final JSON json, final Type deserializeType) {
final MyType myType = new MyType();
myType.setAddress(((JSONObject)json).getStr("addr"));
return myType;

View File

@ -18,7 +18,9 @@ package org.dromara.hutool.json;
import lombok.Data;
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.json.serialize.JSONObjectSerializer;
import org.dromara.hutool.json.serializer.JSONContext;
import org.dromara.hutool.json.serializer.JSONSerializer;
import org.dromara.hutool.json.serializer.SerializerManager;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@ -31,7 +33,7 @@ public class Issue3086Test {
@Test
public void serializeTest() {
JSONUtil.putSerializer(TestBean.class, new TestBean());
SerializerManager.getInstance().register(TestBean.class, new TestBean());
final List<SimpleGrantedAuthority> strings = ListUtil.of(
new SimpleGrantedAuthority("ROLE_admin"),
@ -61,14 +63,19 @@ public class Issue3086Test {
}
@Data
static class TestBean implements JSONObjectSerializer<TestBean> {
static class TestBean implements JSONSerializer<TestBean> {
private Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
@Override
public void serialize(final JSONObject json, final TestBean testBean) {
final List<String> strings = testBean.getAuthorities()
public JSON serialize(final TestBean bean, final JSONContext context) {
final List<String> strings = bean.getAuthorities()
.stream().map(SimpleGrantedAuthority::getAuthority).collect(Collectors.toList());
json.set("authorities",strings);
JSONObject contextJson = (JSONObject) context.getContextJson();
if(null == contextJson){
contextJson = new JSONObject(context.config());
}
contextJson.set("authorities",strings);
return contextJson;
}
}
}

View File

@ -19,10 +19,11 @@ package org.dromara.hutool.json;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.json.serialize.JSONDeserializer;
import org.dromara.hutool.json.serializer.JSONDeserializer;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
@ -38,7 +39,7 @@ public class IssueI7M2GZTest {
private Integer parsed;
@Override
public JSONBeanParserImpl deserialize(final JSON json) {
public JSONBeanParserImpl deserialize(final JSON json, final Type deserializeType) {
setName("new Object");
setParsed(12);
return this;

View File

@ -16,11 +16,11 @@
package org.dromara.hutool.json;
import org.dromara.hutool.json.serialize.GlobalSerializeMapping;
import org.dromara.hutool.json.serialize.JSONDeserializer;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.dromara.hutool.json.serializer.JSONDeserializer;
import org.dromara.hutool.json.serializer.SerializerManager;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@ -31,7 +31,7 @@ public class IssuesI44E4HTest {
@Test
public void deserializerTest(){
GlobalSerializeMapping.putDeserializer(TestDto.class, (JSONDeserializer<TestDto>) json -> {
SerializerManager.getInstance().register(TestDto.class, (JSONDeserializer<TestDto>) (json, deserializeType) -> {
final TestDto testDto = new TestDto();
testDto.setMd(new AcBizModuleMd("name1", ((JSONObject)json).getStr("md")));
return testDto;

View File

@ -16,6 +16,7 @@
package org.dromara.hutool.json;
import lombok.Data;
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.io.file.FileUtil;
@ -25,7 +26,6 @@ import org.dromara.hutool.core.util.CharsetUtil;
import org.dromara.hutool.json.test.bean.Exam;
import org.dromara.hutool.json.test.bean.JsonNode;
import org.dromara.hutool.json.test.bean.KeyBean;
import lombok.Data;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@ -34,6 +34,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* JSONArray单元测试
*
@ -47,19 +50,19 @@ public class JSONArrayTest {
final JSONObject jsonObject = new JSONObject();
JSONArray jsonArray = new JSONArray(jsonObject, JSONConfig.of());
Assertions.assertEquals(new JSONArray(), jsonArray);
assertEquals(new JSONArray(), jsonArray);
jsonObject.set("key1", "value1");
jsonArray = new JSONArray(jsonObject, JSONConfig.of());
Assertions.assertEquals(1, jsonArray.size());
Assertions.assertEquals("[{\"key1\":\"value1\"}]", jsonArray.toString());
assertEquals(1, jsonArray.size());
assertEquals("[{\"key1\":\"value1\"}]", jsonArray.toString());
}
@Test
public void addNullTest() {
final List<String> aaa = ListUtil.view("aaa", null);
final String jsonStr = JSONUtil.toJsonStr(JSONUtil.parse(aaa, JSONConfig.of().setIgnoreNullValue(false)));
Assertions.assertEquals("[\"aaa\",null]", jsonStr);
assertEquals("[\"aaa\",null]", jsonStr);
}
@Test
@ -72,14 +75,14 @@ public class JSONArrayTest {
array.add("value2");
array.add("value3");
Assertions.assertEquals(array.get(0), "value1");
assertEquals(array.get(0), "value1");
}
@Test
public void parseTest() {
final String jsonStr = "[\"value1\", \"value2\", \"value3\"]";
final JSONArray array = JSONUtil.parseArray(jsonStr);
Assertions.assertEquals(array.get(0), "value1");
assertEquals(array.get(0), "value1");
}
@Test
@ -90,7 +93,7 @@ public class JSONArrayTest {
// 不忽略null则null的键值对被保留
jsonArray = JSONUtil.parseArray(jsonStr, JSONConfig.of().setIgnoreNullValue(false));
Assertions.assertTrue(jsonArray.getJSONObject(1).containsKey("result"));
assertTrue(jsonArray.getJSONObject(1).containsKey("result"));
}
@Test
@ -99,7 +102,7 @@ public class JSONArrayTest {
final JSONObject obj0 = array.getJSONObject(0);
final Exam exam = JSONUtil.toBean(obj0, Exam.class);
Assertions.assertEquals("0", exam.getAnswerArray()[0].getSeq());
assertEquals("0", exam.getAnswerArray()[0].getSeq());
}
@Test
@ -114,8 +117,8 @@ public class JSONArrayTest {
final ArrayList<KeyBean> list = ListUtil.of(b1, b2);
final JSONArray jsonArray = JSONUtil.parseArray(list);
Assertions.assertEquals("aValue1", jsonArray.getJSONObject(0).getStr("akey"));
Assertions.assertEquals("bValue2", jsonArray.getJSONObject(1).getStr("bkey"));
assertEquals("aValue1", jsonArray.getJSONObject(0).getStr("akey"));
assertEquals("bValue2", jsonArray.getJSONObject(1).getStr("bkey"));
}
@Test
@ -138,11 +141,11 @@ public class JSONArrayTest {
Assertions.assertFalse(userList.isEmpty());
Assertions.assertSame(User.class, userList.get(0).getClass());
Assertions.assertEquals(Integer.valueOf(111), userList.get(0).getId());
Assertions.assertEquals(Integer.valueOf(112), userList.get(1).getId());
assertEquals(Integer.valueOf(111), userList.get(0).getId());
assertEquals(Integer.valueOf(112), userList.get(1).getId());
Assertions.assertEquals("test1", userList.get(0).getName());
Assertions.assertEquals("test2", userList.get(1).getName());
assertEquals("test1", userList.get(0).getName());
assertEquals("test2", userList.get(1).getName());
}
@Test
@ -156,11 +159,11 @@ public class JSONArrayTest {
Assertions.assertFalse(list.isEmpty());
Assertions.assertSame(Dict.class, list.get(0).getClass());
Assertions.assertEquals(Integer.valueOf(111), list.get(0).getInt("id"));
Assertions.assertEquals(Integer.valueOf(112), list.get(1).getInt("id"));
assertEquals(Integer.valueOf(111), list.get(0).getInt("id"));
assertEquals(Integer.valueOf(112), list.get(1).getInt("id"));
Assertions.assertEquals("test1", list.get(0).getStr("name"));
Assertions.assertEquals("test2", list.get(1).getStr("name"));
assertEquals("test1", list.get(0).getStr("name"));
assertEquals("test2", list.get(1).getStr("name"));
}
@Test
@ -184,8 +187,8 @@ public class JSONArrayTest {
final List<KeyBean> list = ja.toList(KeyBean.class);
Assertions.assertNull(list.get(0));
Assertions.assertEquals("avalue", list.get(1).getAkey());
Assertions.assertEquals("bvalue", list.get(1).getBkey());
assertEquals("avalue", list.get(1).getAkey());
assertEquals("bvalue", list.get(1).getBkey());
}
@Test
@ -209,28 +212,28 @@ public class JSONArrayTest {
final JSONArray jsonArray = JSONUtil.parseArray(mapList);
final List<JsonNode> nodeList = jsonArray.toList(JsonNode.class);
Assertions.assertEquals(Long.valueOf(0L), nodeList.get(0).getId());
Assertions.assertEquals(Long.valueOf(1L), nodeList.get(1).getId());
Assertions.assertEquals(Long.valueOf(0L), nodeList.get(2).getId());
Assertions.assertEquals(Long.valueOf(0L), nodeList.get(3).getId());
assertEquals(Long.valueOf(0L), nodeList.get(0).getId());
assertEquals(Long.valueOf(1L), nodeList.get(1).getId());
assertEquals(Long.valueOf(0L), nodeList.get(2).getId());
assertEquals(Long.valueOf(0L), nodeList.get(3).getId());
Assertions.assertEquals(Integer.valueOf(0), nodeList.get(0).getParentId());
Assertions.assertEquals(Integer.valueOf(1), nodeList.get(1).getParentId());
Assertions.assertEquals(Integer.valueOf(0), nodeList.get(2).getParentId());
Assertions.assertEquals(Integer.valueOf(0), nodeList.get(3).getParentId());
assertEquals(Integer.valueOf(0), nodeList.get(0).getParentId());
assertEquals(Integer.valueOf(1), nodeList.get(1).getParentId());
assertEquals(Integer.valueOf(0), nodeList.get(2).getParentId());
assertEquals(Integer.valueOf(0), nodeList.get(3).getParentId());
Assertions.assertEquals("0", nodeList.get(0).getName());
Assertions.assertEquals("1", nodeList.get(1).getName());
Assertions.assertEquals("+0", nodeList.get(2).getName());
Assertions.assertEquals("-0", nodeList.get(3).getName());
assertEquals("0", nodeList.get(0).getName());
assertEquals("1", nodeList.get(1).getName());
assertEquals("+0", nodeList.get(2).getName());
assertEquals("-0", nodeList.get(3).getName());
}
@Test
public void getByPathTest() {
final String jsonStr = "[{\"id\": \"1\",\"name\": \"a\"},{\"id\": \"2\",\"name\": \"b\"}]";
final JSONArray jsonArray = JSONUtil.parseArray(jsonStr);
Assertions.assertEquals("b", jsonArray.getByPath("[1].name"));
Assertions.assertEquals("b", JSONUtil.getByPath(jsonArray, "[1].name"));
assertEquals("b", jsonArray.getByPath("[1].name"));
assertEquals("b", JSONUtil.getByPath(jsonArray, "[1].name"));
}
@Test
@ -238,12 +241,12 @@ public class JSONArrayTest {
JSONArray jsonArray = new JSONArray();
jsonArray.set(3, "test");
// 默认忽略null值因此空位无值只有一个值
Assertions.assertEquals(1, jsonArray.size());
assertEquals(1, jsonArray.size());
jsonArray = new JSONArray(JSONConfig.of().setIgnoreNullValue(false));
jsonArray.set(2, "test");
// 第三个位置插入值0~2都是null
Assertions.assertEquals(3, jsonArray.size());
assertEquals(3, jsonArray.size());
}
// https://github.com/dromara/hutool/issues/1858
@ -251,8 +254,8 @@ public class JSONArrayTest {
public void putTest2() {
final JSONArray jsonArray = new JSONArray();
jsonArray.put(0, 1);
Assertions.assertEquals(1, jsonArray.size());
Assertions.assertEquals(1, jsonArray.get(0));
assertEquals(1, jsonArray.size());
assertEquals(1, jsonArray.get(0));
}
private static Map<String, String> buildMap(final String id, final String parentId, final String name) {
@ -278,7 +281,7 @@ public class JSONArrayTest {
.set(true);
final String s = json1.toJSONString(0, (pair) -> pair.getValue().equals("value2"));
Assertions.assertEquals("[\"value2\"]", s);
assertEquals("[\"value2\"]", s);
}
@Test
@ -290,7 +293,7 @@ public class JSONArrayTest {
.set(true);
final String s = json1.toJSONString(0, (pair) -> !pair.getValue().equals("value2"));
Assertions.assertEquals("[\"value1\",\"value3\",true]", s);
assertEquals("[\"value1\",\"value3\",true]", s);
}
@Test
@ -298,7 +301,7 @@ public class JSONArrayTest {
final JSONArray array = JSONUtil.ofArray(JSONConfig.of().setIgnoreNullValue(false));
array.set(null);
Assertions.assertEquals("[null]", array.toString());
assertEquals("[null]", array.toString());
}
@Test
@ -306,8 +309,8 @@ public class JSONArrayTest {
final String jsonArr = "[{\"id\":111,\"name\":\"test1\"},{\"id\":112,\"name\":\"test2\"}]";
//noinspection MismatchedQueryAndUpdateOfCollection
final JSONArray array = new JSONArray(jsonArr, null, (mutable) -> mutable.get().toString().contains("111"));
Assertions.assertEquals(1, array.size());
Assertions.assertTrue(array.getJSONObject(0).containsKey("id"));
assertEquals(1, array.size());
assertTrue(array.getJSONObject(0).containsKey("id"));
}
@Test
@ -322,8 +325,20 @@ public class JSONArrayTest {
mutable.set(o);
return true;
});
Assertions.assertEquals(2, array.size());
Assertions.assertTrue(array.getJSONObject(0).containsKey("id"));
Assertions.assertEquals("test1_edit", array.getJSONObject(0).get("name"));
assertEquals(2, array.size());
assertTrue(array.getJSONObject(0).containsKey("id"));
assertEquals("test1_edit", array.getJSONObject(0).get("name"));
}
@Test
void jsonIterTest() {
final JSONArray array = JSONUtil.ofArray();
array.add(JSONUtil.ofObj().set("name", "aaa"));
array.add(JSONUtil.ofObj().set("name", "bbb"));
array.add(JSONUtil.ofObj().set("name", "ccc"));
StringBuilder result = new StringBuilder();
array.jsonIter(JSONObject.class).forEach(result::append);
assertEquals("{\"name\":\"aaa\"}{\"name\":\"bbb\"}{\"name\":\"ccc\"}", result.toString());
}
}

View File

@ -16,11 +16,13 @@
package org.dromara.hutool.json;
import org.dromara.hutool.json.serialize.JSONDeserializer;
import lombok.Data;
import org.dromara.hutool.json.serializer.JSONDeserializer;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Type;
public class JSONDeserializerTest {
@Test
@ -33,14 +35,14 @@ public class JSONDeserializerTest {
}
@Data
static class TestBean implements JSONDeserializer<Object> {
static class TestBean implements JSONDeserializer<TestBean> {
private String name;
private String address;
@Override
public Object deserialize(final JSON value) {
final JSONObject valueObj = (JSONObject) value;
public TestBean deserialize(final JSON json, final Type deserializeType) {
final JSONObject valueObj = (JSONObject) json;
this.name = valueObj.getStr("customName");
this.address = valueObj.getStr("customAddress");
return this;

View File

@ -1,61 +0,0 @@
/*
* 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;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class JSONSupportTest {
/**
* https://github.com/dromara/hutool/issues/1779
* 在JSONSupport的JSONBeanParse中如果使用json.toBean会导致JSONBeanParse.parse方法反复递归调用最终栈溢出<br>
* 因此parse方法默认实现必须避开JSONBeanParse.parse调用
*/
@Test
public void parseTest() {
final String jsonstr = "{\n" +
" \"location\": \"https://hutool.cn\",\n" +
" \"message\": \"这是一条测试消息\",\n" +
" \"requestId\": \"123456789\",\n" +
" \"traceId\": \"987654321\"\n" +
"}";
final TestBean testBean = JSONUtil.toBean(jsonstr, TestBean.class);
Assertions.assertEquals("https://hutool.cn", testBean.getLocation());
Assertions.assertEquals("这是一条测试消息", testBean.getMessage());
Assertions.assertEquals("123456789", testBean.getRequestId());
Assertions.assertEquals("987654321", testBean.getTraceId());
}
@EqualsAndHashCode(callSuper = true)
@Data
static class TestBean extends JSONSupport{
private String location;
private String message;
private String requestId;
private String traceId;
}
}

View File

@ -21,7 +21,7 @@ import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.date.DateUtil;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.math.NumberUtil;
import org.dromara.hutool.json.serialize.JSONStringer;
import org.dromara.hutool.json.serializer.JSONStringer;
import org.dromara.hutool.json.test.bean.Price;
import org.dromara.hutool.json.test.bean.UserA;
import org.dromara.hutool.json.test.bean.UserC;

View File

@ -46,7 +46,6 @@ public class JSONEngineTest {
@Test
void writeTimeZoneTest() {
// TODO Hutool无法序列化TimeZone等特殊对象
Arrays.stream(engineNames).forEach(this::assertWriteTimeZone);
}