From 2872f15e91cfcc047a092fe5fac9d5918495a39a Mon Sep 17 00:00:00 2001 From: Looly Date: Mon, 23 Sep 2024 15:08:20 +0800 Subject: [PATCH] fix code --- .../hutool/core/annotation/ReadableBean.java | 23 +- .../hutool/core/annotation/WritableBean.java | 32 +++ .../dromara/hutool/core/bean/BeanUtil.java | 12 + .../core/bean/copier/ValueProvider.java | 2 +- .../hutool/core/bean/path/BeanPath.java | 31 +-- .../bean/path/DefaultNodeBeanCreator.java | 69 +++++ .../core/bean/path/NodeBeanCreator.java | 38 +++ .../hutool/core/bean/path/node/NameNode.java | 9 + .../dromara/hutool/core/codec/Hashids.java | 2 +- .../hutool/core/codec/PercentCodec.java | 2 +- .../hutool/core/codec/binary/Z85Codec.java | 2 +- .../core/convert/AbstractConverter.java | 2 +- .../core/convert/CompositeConverter.java | 2 +- .../core/convert/RegisterConverter.java | 2 +- .../core/convert/impl/DateConverter.java | 2 +- .../core/convert/impl/EnumConverter.java | 2 +- .../core/convert/impl/NumberConverter.java | 2 +- .../core/convert/impl/PrimitiveConverter.java | 2 +- .../core/date/format/FastDateFormat.java | 2 +- .../org/dromara/hutool/core/lang/Version.java | 2 +- .../hutool/core/convert/ConvertTest.java | 2 +- .../hutool/core/util/ByteUtilTest.java | 4 +- .../dromara/hutool/json/InternalJSONUtil.java | 72 +---- .../java/org/dromara/hutool/json/JSON.java | 32 +-- .../org/dromara/hutool/json/JSONArray.java | 6 +- .../org/dromara/hutool/json/JSONObject.java | 6 +- .../org/dromara/hutool/json/JSONUtil.java | 13 +- .../hutool/json/mapper/JSONArrayMapper.java | 104 -------- .../hutool/json/mapper/JSONObjectMapper.java | 122 --------- .../hutool/json/mapper/JSONValueMapper.java | 199 -------------- .../org/dromara/hutool/json/package-info.java | 6 +- .../hutool/json/serializer/JSONMapper.java | 248 ++++++++++++++++++ .../json/serializer/TypeAdapterManager.java | 2 + .../serializer/impl/ArrayTypeAdapter.java | 40 ++- .../json/serializer/impl/BeanTypeAdapter.java | 2 +- .../json/serializer/impl/IterTypeAdapter.java | 4 + .../serializer/impl/KBeanDeserializer.java | 2 +- .../serializer/impl/RecordDeserializer.java | 2 +- .../impl/ResourceBundleSerializer.java | 104 ++++++++ .../serializer/impl/TokenerSerializer.java | 73 ++++++ .../json/support/JSONNodeBeanCreator.java | 60 +++++ .../JSONObjectValueProvider.java | 25 +- .../json/{ => support}/JSONStrFormatter.java | 2 +- .../{convert => support}/package-info.java | 4 +- .../dromara/hutool/json/Issue1200Test.java | 7 +- .../org/dromara/hutool/json/Issue867Test.java | 8 +- .../dromara/hutool/json/IssueI9DX5HTest.java | 4 +- .../hutool/json/JSONStrFormatterTest.java | 1 + .../org/dromara/hutool/swing/img/ImgUtil.java | 2 +- 49 files changed, 796 insertions(+), 600 deletions(-) rename hutool-json/src/main/java/org/dromara/hutool/json/mapper/package-info.java => hutool-core/src/main/java/org/dromara/hutool/core/annotation/ReadableBean.java (59%) create mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/annotation/WritableBean.java create mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/bean/path/DefaultNodeBeanCreator.java create mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/bean/path/NodeBeanCreator.java delete mode 100644 hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONArrayMapper.java delete mode 100644 hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONObjectMapper.java delete mode 100644 hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONValueMapper.java create mode 100644 hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONMapper.java create mode 100644 hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/ResourceBundleSerializer.java create mode 100644 hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TokenerSerializer.java create mode 100644 hutool-json/src/main/java/org/dromara/hutool/json/support/JSONNodeBeanCreator.java rename hutool-json/src/main/java/org/dromara/hutool/json/{convert => support}/JSONObjectValueProvider.java (78%) rename hutool-json/src/main/java/org/dromara/hutool/json/{ => support}/JSONStrFormatter.java (99%) rename hutool-json/src/main/java/org/dromara/hutool/json/{convert => support}/package-info.java (82%) diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/mapper/package-info.java b/hutool-core/src/main/java/org/dromara/hutool/core/annotation/ReadableBean.java similarity index 59% rename from hutool-json/src/main/java/org/dromara/hutool/json/mapper/package-info.java rename to hutool-core/src/main/java/org/dromara/hutool/core/annotation/ReadableBean.java index 31138738a..5bef065fb 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/mapper/package-info.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/annotation/ReadableBean.java @@ -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,12 +14,19 @@ * limitations under the License. */ +package org.dromara.hutool.core.annotation; + +import java.lang.annotation.*; + /** - * Bean和JSON之间的映射封装,包括: - * + * 标记可读Bean
+ * 即包含有可读字段的Bean使用此注解标记,如含有public的字段或getter方法 + * + * @author looly + * @since 6.0.0 */ -package org.dromara.hutool.json.mapper; +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface ReadableBean { +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/annotation/WritableBean.java b/hutool-core/src/main/java/org/dromara/hutool/core/annotation/WritableBean.java new file mode 100644 index 000000000..e25cfa425 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/annotation/WritableBean.java @@ -0,0 +1,32 @@ +/* + * 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.core.annotation; + +import java.lang.annotation.*; + +/** + * 标记可写Bean
+ * 即包含有可写字段的Bean使用此注解标记,如含有public的字段或setter方法 + * + * @author looly + * @since 6.0.0 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface WritableBean { +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java index 0840bc112..266c8dc98 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java @@ -16,6 +16,9 @@ package org.dromara.hutool.core.bean; +import org.dromara.hutool.core.annotation.AnnotationUtil; +import org.dromara.hutool.core.annotation.ReadableBean; +import org.dromara.hutool.core.annotation.WritableBean; import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.bean.copier.BeanCopier; import org.dromara.hutool.core.bean.copier.CopyOptions; @@ -687,6 +690,11 @@ public class BeanUtil { // String中有getter方法,但为字符串,不是Bean return false; } + + if(AnnotationUtil.hasAnnotation(clazz, ReadableBean.class)){ + return true; + } + return hasGetter(clazz) || hasPublicField(clazz); } @@ -712,6 +720,10 @@ public class BeanUtil { return false; } + if(AnnotationUtil.hasAnnotation(clazz, WritableBean.class)){ + return true; + } + return hasSetter(clazz) || hasPublicField(clazz); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/ValueProvider.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/ValueProvider.java index 6cedd7aab..a08fb95e5 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/ValueProvider.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/ValueProvider.java @@ -31,7 +31,7 @@ public interface ValueProvider{ /** * 获取值
- * 返回值一般需要匹配被注入类型,如果不匹配会调用默认转换 Convert#convert(Type, Object)实现转换 + * 返回值一般需要匹配被注入类型,如果不匹配会调用默认转换 Convert#support(Type, Object)实现转换 * * @param key Bean对象中参数名 * @param valueType 被注入的值的类型 diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/BeanPath.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/BeanPath.java index 97bb1242a..16cfacce6 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/BeanPath.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/BeanPath.java @@ -17,14 +17,11 @@ package org.dromara.hutool.core.bean.path; import org.dromara.hutool.core.array.ArrayUtil; -import org.dromara.hutool.core.bean.path.node.NameNode; import org.dromara.hutool.core.bean.path.node.Node; import org.dromara.hutool.core.bean.path.node.NodeFactory; import org.dromara.hutool.core.text.CharUtil; import org.dromara.hutool.core.text.StrUtil; -import java.util.ArrayList; -import java.util.HashMap; import java.util.Iterator; /** @@ -67,6 +64,7 @@ public class BeanPath implements Iterator { private final Node node; private final String child; + private NodeBeanCreator beanCreator; /** * 构造 @@ -74,6 +72,7 @@ public class BeanPath implements Iterator { * @param expression 表达式 */ public BeanPath(final String expression) { + this.beanCreator = DefaultNodeBeanCreator.INSTANCE; final int length = expression.length(); final StringBuilder builder = new StringBuilder(); @@ -128,6 +127,17 @@ public class BeanPath implements Iterator { } } + /** + * 设置Bean创建器,用于创建Bean对象,默认为{@link DefaultNodeBeanCreator} + * + * @param beanCreator Bean创建器 + * @return this + */ + public BeanPath setBeanCreator(final NodeBeanCreator beanCreator) { + this.beanCreator = beanCreator; + return this; + } + /** * 获取节点 * @@ -189,7 +199,7 @@ public class BeanPath implements Iterator { final BeanPath childBeanPath = next(); Object subBean = this.node.getValue(bean); if (null == subBean) { - subBean = isListNode(childBeanPath.node) ? new ArrayList<>() : new HashMap<>(); + subBean = beanCreator.create(bean, this); this.node.setValue(bean, subBean); // 如果自定义put方法修改了value,返回修改后的value,避免值丢失 subBean = this.node.getValue(bean); @@ -210,17 +220,4 @@ public class BeanPath implements Iterator { ", child='" + child + '\'' + '}'; } - - /** - * 子节点值是否为列表 - * - * @param node 节点 - * @return 是否为列表 - */ - private static boolean isListNode(final Node node) { - if (node instanceof NameNode) { - return ((NameNode) node).isNumber(); - } - return false; - } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/DefaultNodeBeanCreator.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/DefaultNodeBeanCreator.java new file mode 100644 index 000000000..7640257a1 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/DefaultNodeBeanCreator.java @@ -0,0 +1,69 @@ +/* + * 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.core.bean.path; + +import org.dromara.hutool.core.array.ArrayUtil; +import org.dromara.hutool.core.bean.path.node.NameNode; +import org.dromara.hutool.core.bean.path.node.Node; +import org.dromara.hutool.core.reflect.ConstructorUtil; +import org.dromara.hutool.core.reflect.FieldUtil; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 默认的Bean创建器 + * + * @author looly + * @since 6.0.0 + */ +public class DefaultNodeBeanCreator implements NodeBeanCreator { + + /** + * 单例 + */ + public static final NodeBeanCreator INSTANCE = new DefaultNodeBeanCreator(); + + @Override + public Object create(final Object parent, final BeanPath beanPath) { + if(parent instanceof Map || parent instanceof List || ArrayUtil.isArray(parent)){ + // 根据下一个节点类型,判断当前节点名称对应类型 + final Node node = beanPath.next().getNode(); + if (node instanceof NameNode) { + return ((NameNode) node).isNumber() ? new ArrayList<>() : new HashMap<>(); + } + return new HashMap<>(); + } + + // 普通Bean + final Node node = beanPath.getNode(); + if(node instanceof NameNode){ + final String name = ((NameNode) node).getName(); + + final Field field = FieldUtil.getField(parent.getClass(), name); + if(null == field){ + throw new IllegalArgumentException("No field found for name: " + name); + } + return ConstructorUtil.newInstanceIfPossible(field.getType()); + } + + throw new UnsupportedOperationException("Unsupported node type: " + node.getClass()); + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/NodeBeanCreator.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/NodeBeanCreator.java new file mode 100644 index 000000000..ac1151ace --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/NodeBeanCreator.java @@ -0,0 +1,38 @@ +/* + * 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.core.bean.path; + +/** + * BeanPath节点对应的Bean创建器
+ * 用于创建Bean路径节点对应的Bean + * + * @author looly + * @since 6.0.0 + */ +public interface NodeBeanCreator { + + /** + * 创建Bean
+ * beanPath对应当前的路径,即如果父对象为:a,则beanPath为:a.b,则创建的Bean为:a.b.c对应的Bean对象
+ * 给定的a一定存在,但是本路径中b对应的Bean不存在,则创建的对象是b的值,这个值用c表示 + * + * @param parent 父Bean + * @param beanPath 当前路径 + * @return Bean + */ + Object create(final Object parent, final BeanPath beanPath); +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/node/NameNode.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/node/NameNode.java index f86e02ace..e4e0c93d2 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/node/NameNode.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/node/NameNode.java @@ -34,6 +34,15 @@ public class NameNode implements Node { private final String name; + /** + * 获取节点名 + * + * @return 节点名 + */ + public String getName() { + return name; + } + /** * 构造 * diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/codec/Hashids.java b/hutool-core/src/main/java/org/dromara/hutool/core/codec/Hashids.java index e85ff47a3..b2d150468 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/codec/Hashids.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/codec/Hashids.java @@ -505,7 +505,7 @@ public class Hashids implements Encoder, Decoder return IntStream.range(0, separators.length) .mapToObj(idx -> (separators[idx])) .filter(valid::contains) - // ugly way to convert back to char[] + // ugly way to support back to char[] .map(c -> Character.toString(c)) .collect(Collectors.joining()) .toCharArray(); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/codec/PercentCodec.java b/hutool-core/src/main/java/org/dromara/hutool/core/codec/PercentCodec.java index 5a77b5414..d3c226130 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/codec/PercentCodec.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/codec/PercentCodec.java @@ -135,7 +135,7 @@ public class PercentCodec implements Encoder, Serializable { // 对于空格单独处理 rewrittenPath.append('+'); } else { - // convert to external encoding before hex conversion + // support to external encoding before hex conversion try { writer.write(c); writer.flush(); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/codec/binary/Z85Codec.java b/hutool-core/src/main/java/org/dromara/hutool/core/codec/binary/Z85Codec.java index f577bcf6f..29763e108 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/codec/binary/Z85Codec.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/codec/binary/Z85Codec.java @@ -67,7 +67,7 @@ public class Z85Codec implements Encoder, Decoder>> 2) + (remainder == 0 ? 0 : 1)]; int idx = 0; diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/AbstractConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/AbstractConverter.java index 02731dc50..b86d787fb 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/AbstractConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/AbstractConverter.java @@ -45,7 +45,7 @@ public abstract class AbstractConverter implements Converter, Serializable { return null; } if (TypeUtil.isUnknown(targetType)) { - throw new ConvertException("Unsupported convert to unKnown type: {}", targetType); + throw new ConvertException("Unsupported support to unKnown type: {}", targetType); } final Class targetClass = TypeUtil.getClass(targetType); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java index c57192b8c..fb16e2565 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java @@ -201,6 +201,6 @@ public class CompositeConverter implements Converter, Serializable { } // 无法转换 - throw new ConvertException("Can not convert from {}: [{}] to [{}]", value.getClass().getName(), value, type.getTypeName()); + throw new ConvertException("Can not support from {}: [{}] to [{}]", value.getClass().getName(), value, type.getTypeName()); } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java index e614606d2..97707d225 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java @@ -90,7 +90,7 @@ public class RegisterConverter extends ConverterWithRoot implements Serializable } // 无法转换 - throw new ConvertException("Can not convert from {}: [{}] to [{}]", value.getClass().getName(), value, targetType.getTypeName()); + throw new ConvertException("Can not support from {}: [{}] to [{}]", value.getClass().getName(), value, targetType.getTypeName()); } /** diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java index 87f02c392..a1236f4a6 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java @@ -108,7 +108,7 @@ public class DateConverter extends AbstractConverter implements MatcherConverter } } - throw new ConvertException("Can not convert {}:[{}] to {}", value.getClass().getName(), value, targetClass.getName()); + throw new ConvertException("Can not support {}:[{}] to {}", value.getClass().getName(), value, targetClass.getName()); } /** diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EnumConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EnumConverter.java index f4aee2887..fb7025c62 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EnumConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EnumConverter.java @@ -67,7 +67,7 @@ public class EnumConverter extends AbstractConverter implements MatcherConverter return enumValue; } - throw new ConvertException("Can not convert {} to {}", value, targetClass); + throw new ConvertException("Can not support {} to {}", value, targetClass); } /** diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/NumberConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/NumberConverter.java index be925da88..0068ccc05 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/NumberConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/NumberConverter.java @@ -78,7 +78,7 @@ public class NumberConverter extends AbstractConverter implements MatcherConvert protected String convertToStr(final Object value) { final String result = StrUtil.trim(super.convertToStr(value)); if(StrUtil.isEmpty(result)){ - throw new ConvertException("Can not convert empty value to Number!"); + throw new ConvertException("Can not support empty value to Number!"); } if (result.length() > 1) { diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PrimitiveConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PrimitiveConverter.java index 884cfcbb4..2d6227f2c 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PrimitiveConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PrimitiveConverter.java @@ -76,7 +76,7 @@ public class PrimitiveConverter extends AbstractConverter implements MatcherConv } if(null == result){ - throw new ConvertException("Can not convert {} to {}", value, primitiveClass); + throw new ConvertException("Can not support {} to {}", value, primitiveClass); } return result; diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/FastDateFormat.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/FastDateFormat.java index ce0ffe233..91feb8442 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/FastDateFormat.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/FastDateFormat.java @@ -408,7 +408,7 @@ public class FastDateFormat extends Format implements PositionDateParser, DatePr return printer.getMaxLengthEstimate(); } - // convert DateTimeFormatter + // support DateTimeFormatter // ----------------------------------------------------------------------- /** diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/lang/Version.java b/hutool-core/src/main/java/org/dromara/hutool/core/lang/Version.java index 4fe04c317..9515249ed 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/lang/Version.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/lang/Version.java @@ -266,7 +266,7 @@ public class Version implements Comparable, Serializable { } return c; } - // Types differ, so convert number to string form + // Types differ, so support number to string form final int c = o1.toString().compareTo(o2.toString()); if (c == 0){ continue; diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/convert/ConvertTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/convert/ConvertTest.java index f02b07c3a..99dc2f86f 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/convert/ConvertTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/convert/ConvertTest.java @@ -329,7 +329,7 @@ public class ConvertTest { @Test public void toClassTest(){ - final Class convert = ConvertUtil.convert(Class.class, "org.dromara.hutool.core.convert.ConvertTest.Product"); + final Class convert = ConvertUtil.convert(Class.class, "org.dromara.hutool.core.support.ConvertTest.Product"); Assertions.assertSame(Product.class, convert); } diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/util/ByteUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/util/ByteUtilTest.java index 04ff90634..cb99b9534 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/util/ByteUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/util/ByteUtilTest.java @@ -219,7 +219,7 @@ public class ByteUtilTest { final byte[] bytes = new byte[]{(byte) 0xFF, (byte) 0xFE, (byte) 0x01, (byte) 0x00}; final int expectedValue = 130815; final int actualValue = ByteUtil.toInt(bytes, 0, ByteOrder.LITTLE_ENDIAN); - assertEquals(expectedValue, actualValue, "Failed to convert bytes to int using LITTLE_ENDIAN"); + assertEquals(expectedValue, actualValue, "Failed to support bytes to int using LITTLE_ENDIAN"); } @Test @@ -228,6 +228,6 @@ public class ByteUtilTest { final byte[] bytes = new byte[]{(byte) 0xFF, (byte) 0xFE, (byte) 0x01, (byte) 0x00}; final int expectedValue = -130816; final int actualValue = ByteUtil.toInt(bytes, 0, ByteOrder.BIG_ENDIAN); - assertEquals(expectedValue, actualValue, "Failed to convert bytes to int using BIG_ENDIAN"); + assertEquals(expectedValue, actualValue, "Failed to support bytes to int using BIG_ENDIAN"); } } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/InternalJSONUtil.java b/hutool-json/src/main/java/org/dromara/hutool/json/InternalJSONUtil.java index a698cfdc5..fccfef873 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/InternalJSONUtil.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/InternalJSONUtil.java @@ -16,24 +16,23 @@ package org.dromara.hutool.json; -import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.bean.copier.CopyOptions; import org.dromara.hutool.core.codec.binary.HexUtil; -import org.dromara.hutool.core.convert.ConvertUtil; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.map.CaseInsensitiveLinkedMap; import org.dromara.hutool.core.map.CaseInsensitiveTreeMap; -import org.dromara.hutool.core.math.NumberUtil; import org.dromara.hutool.core.text.CharUtil; import org.dromara.hutool.core.text.StrUtil; -import org.dromara.hutool.core.text.split.SplitUtil; import org.dromara.hutool.core.util.ObjUtil; -import org.dromara.hutool.json.mapper.JSONValueMapper; +import org.dromara.hutool.json.serializer.JSONMapper; import org.dromara.hutool.json.reader.JSONTokener; import java.io.IOException; import java.math.BigDecimal; -import java.util.*; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeMap; /** * 内部JSON工具类,仅用于JSON内部使用 @@ -88,65 +87,6 @@ public final class InternalJSONUtil { return string; } - /** - * 值转为String,用于JSON中。规则为: - *
    - *
  • 对象如果是数组或Collection,包装为{@link JSONArray}
  • - *
  • 对象如果是Map,包装为{@link JSONObject}
  • - *
  • 对象如果是数字,使用{@link NumberUtil#toStr(Number)}转换为字符串
  • - *
  • 其他情况调用toString并使用双引号包装
  • - *
- * - * @param value 需要转为字符串的对象 - * @return 字符串 - * @throws JSONException If the value is or contains an invalid number. - */ - static String valueToString(final Object value) throws JSONException { - if (value == null) { - return StrUtil.NULL; - } - if (value instanceof Number) { - return NumberUtil.toStr((Number) value); - } else if (value instanceof Boolean || value instanceof JSONObject || value instanceof JSONArray) { - return value.toString(); - } else if (value instanceof Map) { - final Map map = (Map) value; - return JSONUtil.parseObj(map).toString(); - } else if (value instanceof Collection) { - final Collection coll = (Collection) value; - return JSONUtil.parseArray(coll).toString(); - } else if (ArrayUtil.isArray(value)) { - return JSONUtil.parseArray(value).toString(); - } else { - return quote(value.toString()); - } - } - - /** - * 将Property的键转化为JSON形式
- * 用于识别类似于:org.dromara.hutool.json这类用点隔开的键
- * 注意:不允许重复键 - * - * @param jsonObject JSONObject - * @param key 键 - * @param value 值 - */ - public static void propertyPut(final JSONObject jsonObject, final Object key, final Object value) { - final String[] path = SplitUtil.splitToArray(ConvertUtil.toStr(key), StrUtil.DOT); - final int last = path.length - 1; - JSONObject target = jsonObject; - for (int i = 0; i < last; i += 1) { - final String segment = path[i]; - JSONObject nextTarget = target.getJSONObject(segment); - if (nextTarget == null) { - nextTarget = new JSONObject(target.config()); - target.set(segment, nextTarget); - } - target = nextTarget; - } - target.set(path[last], value); - } - /** * 默认情况下是否忽略null值的策略选择,以下对象不忽略null值,其它对象忽略: * @@ -179,7 +119,7 @@ public final class InternalJSONUtil { .setIgnoreError(config.isIgnoreError()) .setIgnoreNullValue(config.isIgnoreNullValue()) .setTransientSupport(config.isTransientSupport()) - .setConverter((targetType, value) -> JSONValueMapper.of(config, null).map(value)); + .setConverter((targetType, value) -> JSONMapper.of(config, null).map(value)); } /** diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSON.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSON.java index 19af217c4..c10719c36 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/JSON.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSON.java @@ -18,9 +18,10 @@ package org.dromara.hutool.json; import org.dromara.hutool.core.bean.path.BeanPath; import org.dromara.hutool.core.lang.mutable.MutableEntry; -import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.json.serializer.JSONDeserializer; +import org.dromara.hutool.json.serializer.JSONMapper; import org.dromara.hutool.json.serializer.TypeAdapterManager; +import org.dromara.hutool.json.support.JSONNodeBeanCreator; import org.dromara.hutool.json.writer.JSONWriter; import java.io.Serializable; @@ -138,7 +139,7 @@ public interface JSON extends Serializable { * @param value 值 */ default void putByPath(final String expression, final Object value) { - BeanPath.of(expression).setValue(this, value); + BeanPath.of(expression).setBeanCreator(new JSONNodeBeanCreator(config())).setValue(this, value); } /** @@ -228,33 +229,8 @@ public interface JSON extends Serializable { * @param Bean类型 * @param type {@link Type} * @return 实体类对象 - * @since 4.3.2 */ - @SuppressWarnings("unchecked") default 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 deserializer = TypeAdapterManager.getInstance().getDeserializer(this, type); - final boolean ignoreError = ObjUtil.defaultIfNull(config(), JSONConfig::isIgnoreError, false); - if(null == deserializer){ - if(ignoreError){ - return null; - } - throw new JSONException("No deserializer for type: " + type); - } - - try{ - return (T) deserializer.deserialize(this, type); - } catch (final Exception e){ - if(ignoreError){ - return null; - } - throw e; - } + return JSONMapper.of(config(), null).toBean(this, type); } } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONArray.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONArray.java index c8e46f35a..4460613d2 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONArray.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONArray.java @@ -23,7 +23,7 @@ import org.dromara.hutool.core.convert.impl.ArrayConverter; import org.dromara.hutool.core.lang.Validator; import org.dromara.hutool.core.lang.mutable.MutableEntry; import org.dromara.hutool.core.util.ObjUtil; -import org.dromara.hutool.json.mapper.JSONValueMapper; +import org.dromara.hutool.json.serializer.JSONMapper; import org.dromara.hutool.json.writer.JSONWriter; import java.util.*; @@ -52,7 +52,7 @@ public class JSONArray extends ListWrapper implements JSON, JSONGetter implements JSON, JSONGetter(initialCapacity)); this.config = ObjUtil.defaultIfNull(config, JSONConfig::of); - this.mapper = JSONValueMapper.of(config, null); + this.mapper = JSONMapper.of(config, null); } // endregion diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONObject.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONObject.java index 954cf1952..1161583a1 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONObject.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONObject.java @@ -25,7 +25,7 @@ import org.dromara.hutool.core.map.MapUtil; import org.dromara.hutool.core.map.MapWrapper; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.util.ObjUtil; -import org.dromara.hutool.json.mapper.JSONValueMapper; +import org.dromara.hutool.json.serializer.JSONMapper; import org.dromara.hutool.json.writer.JSONWriter; import java.util.Arrays; @@ -53,7 +53,7 @@ public class JSONObject extends MapWrapper implements JSON, JSONGe * 配置项 */ private final JSONConfig config; - private final JSONValueMapper mapper; + private final JSONMapper mapper; /** * 构造,初始容量为 {@link #DEFAULT_CAPACITY},KEY有序 @@ -81,7 +81,7 @@ public class JSONObject extends MapWrapper implements JSON, JSONGe public JSONObject(final int capacity, final JSONConfig config) { super(InternalJSONUtil.createRawMap(capacity, config)); this.config = ObjUtil.defaultIfNull(config, JSONConfig::of); - this.mapper = JSONValueMapper.of(config, null); + this.mapper = JSONMapper.of(config, null); } @Override diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONUtil.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONUtil.java index 5a3c45f6d..30d94d466 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONUtil.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONUtil.java @@ -23,7 +23,8 @@ import org.dromara.hutool.core.lang.mutable.MutableEntry; import org.dromara.hutool.core.reflect.TypeReference; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.util.ObjUtil; -import org.dromara.hutool.json.mapper.JSONValueMapper; +import org.dromara.hutool.json.serializer.JSONMapper; +import org.dromara.hutool.json.support.JSONStrFormatter; import org.dromara.hutool.json.writer.JSONWriter; import org.dromara.hutool.json.xml.JSONXMLUtil; @@ -160,8 +161,8 @@ public class JSONUtil { */ public static JSONArray parseArray(final Object arrayOrCollection, final JSONConfig config, final Predicate> predicate) { if(arrayOrCollection instanceof JSONObject){ - final JSONValueMapper jsonValueMapper = JSONValueMapper.of(config, predicate); - return jsonValueMapper.mapFromJSONObject((JSONObject) arrayOrCollection); + final JSONMapper jsonMapper = JSONMapper.of(config, predicate); + return jsonMapper.mapFromJSONObject((JSONObject) arrayOrCollection); } return (JSONArray) parse(arrayOrCollection, config, predicate); } @@ -214,11 +215,11 @@ public class JSONUtil { * @return JSON(JSONObject or JSONArray) */ public static JSON parse(final Object obj, final JSONConfig config, final Predicate> predicate) { - final JSONValueMapper jsonValueMapper = JSONValueMapper.of(config, predicate); + final JSONMapper jsonMapper = JSONMapper.of(config, predicate); if (obj instanceof CharSequence) { - return jsonValueMapper.map((CharSequence) obj); + return jsonMapper.map((CharSequence) obj); } - return jsonValueMapper.map(obj); + return jsonMapper.map(obj); } /** diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONArrayMapper.java b/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONArrayMapper.java deleted file mode 100644 index be429f64b..000000000 --- a/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONArrayMapper.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2013-2024 Hutool Team and hutool.cn - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.dromara.hutool.json.mapper; - -import org.dromara.hutool.core.io.IoUtil; -import org.dromara.hutool.core.lang.mutable.MutableEntry; -import org.dromara.hutool.json.JSONArray; -import org.dromara.hutool.json.JSONConfig; -import org.dromara.hutool.json.JSONException; -import org.dromara.hutool.json.reader.JSONParser; -import org.dromara.hutool.json.reader.JSONTokener; - -import java.util.function.Predicate; - -/** - * 对象和JSONArray映射器,用于转换对象为JSONArray,支持: - *
    - *
  • JSONTokener 转 JSONArray,直接解析
  • - *
  • Iterable 转 JSONArray
  • - *
  • Iterator 转 JSONArray
  • - *
  • 数组 转 JSONArray
  • - *
- * - * @author looly - * @since 6.0.0 - */ -class JSONArrayMapper { - /** - * 创建ArrayMapper - * - * @param source 来源对象 - * @param predicate 值过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤,,{@link Predicate#test(Object)}为{@code true}保留 - * @return ObjectMapper - */ - public static JSONArrayMapper of(final Object source, final Predicate> predicate) { - return new JSONArrayMapper(source, predicate); - } - - private final Object source; - private final Predicate> predicate; - - /** - * 构造 - * - * @param source 来源对象 - * @param predicate 值过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤,,{@link Predicate#test(Object)}为{@code true}保留 - */ - public JSONArrayMapper(final Object source, final Predicate> predicate) { - this.source = source; - this.predicate = predicate; - } - - /** - * 将给定对象转换为{@link JSONArray} - * - * @param jsonArray 目标{@link JSONArray} - * @throws JSONException 非数组或集合 - */ - public void mapTo(final JSONArray jsonArray) throws JSONException { - final Object source = this.source; - if (null == source) { - return; - } - - if (source instanceof byte[]) { - final byte[] bytesSource = (byte[]) source; - if ('[' == bytesSource[0] && ']' == bytesSource[bytesSource.length - 1]) { - mapFromTokener(new JSONTokener(IoUtil.toStream(bytesSource)), jsonArray.config(), jsonArray); - } else { - // https://github.com/dromara/hutool/issues/2369 - // 非标准的二进制流,则按照普通数组对待 - for (final byte b : bytesSource) { - jsonArray.set(b); - } - } - } - - throw new JSONException("Unsupported [{}] to JSONArray", source.getClass()); - } - - /** - * 从JSONTokener中读取数据,并添加到JSONArray中 - * - * @param x {@link JSONTokener} - * @param jsonArray {@link JSONArray} - */ - private void mapFromTokener(final JSONTokener x, final JSONConfig config, final JSONArray jsonArray) { - JSONParser.of(x, config).setPredicate(this.predicate).parseTo(jsonArray); - } -} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONObjectMapper.java b/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONObjectMapper.java deleted file mode 100644 index 2a193fc9e..000000000 --- a/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONObjectMapper.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2013-2024 Hutool Team and hutool.cn - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.dromara.hutool.json.mapper; - -import org.dromara.hutool.core.io.IoUtil; -import org.dromara.hutool.core.lang.mutable.MutableEntry; -import org.dromara.hutool.json.InternalJSONUtil; -import org.dromara.hutool.json.JSONConfig; -import org.dromara.hutool.json.JSONException; -import org.dromara.hutool.json.JSONObject; -import org.dromara.hutool.json.reader.JSONParser; -import org.dromara.hutool.json.reader.JSONTokener; - -import java.util.Enumeration; -import java.util.ResourceBundle; -import java.util.function.Predicate; - -/** - * 对象和JSONObject映射器,用于转换对象为JSONObject,支持: - *
    - *
  • Map 转 JSONObject,将键值对加入JSON对象
  • - *
  • Map.Entry 转 JSONObject
  • - *
  • JSONTokener 转 JSONObject,直接解析
  • - *
  • ResourceBundle 转 JSONObject
  • - *
  • Bean 转 JSONObject,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象。例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"
  • - *
- * - * @author looly - */ -class JSONObjectMapper { - - /** - * 创建ObjectMapper - * - * @param source 来源对象 - * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 - * @return ObjectMapper - */ - public static JSONObjectMapper of(final Object source, final Predicate> predicate) { - return new JSONObjectMapper(source, predicate); - } - - private final Object source; - private final Predicate> predicate; - - /** - * 构造 - * - * @param source 来源对象 - * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 - */ - public JSONObjectMapper(final Object source, final Predicate> predicate) { - this.source = source; - this.predicate = predicate; - } - - /** - * 将给定对象转换为{@link JSONObject} - * - * @param jsonObject 目标{@link JSONObject} - */ - public void mapTo(final JSONObject jsonObject) { - final Object source = this.source; - if (null == source) { - return; - } - - if (source instanceof byte[]) { - mapFromTokener(new JSONTokener(IoUtil.toStream((byte[]) source)), jsonObject.config(), jsonObject); - } else if (source instanceof ResourceBundle) { - // ResourceBundle - mapFromResourceBundle((ResourceBundle) source, jsonObject); - } else { - if (!jsonObject.config().isIgnoreError()) { - // 不支持对象类型转换为JSONObject - throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass()); - } - } - // 如果用户选择跳过异常,则跳过此值转换 - } - - /** - * 从{@link ResourceBundle}转换 - * - * @param bundle ResourceBundle - * @param jsonObject {@link JSONObject} - */ - private void mapFromResourceBundle(final ResourceBundle bundle, final JSONObject jsonObject) { - final Enumeration keys = bundle.getKeys(); - while (keys.hasMoreElements()) { - final String key = keys.nextElement(); - if (key != null) { - InternalJSONUtil.propertyPut(jsonObject, key, bundle.getString(key)); - } - } - } - - /** - * 从{@link JSONTokener}转换 - * - * @param x JSONTokener - * @param config JSON配置 - * @param jsonObject {@link JSONObject} - */ - private void mapFromTokener(final JSONTokener x, final JSONConfig config, final JSONObject jsonObject) { - JSONParser.of(x, config).setPredicate(this.predicate).parseTo(jsonObject); - } -} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONValueMapper.java b/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONValueMapper.java deleted file mode 100644 index 40fccf753..000000000 --- a/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONValueMapper.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2013-2024 Hutool Team and hutool.cn - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.dromara.hutool.json.mapper; - -import org.dromara.hutool.core.array.ArrayUtil; -import org.dromara.hutool.core.lang.Opt; -import org.dromara.hutool.core.lang.mutable.MutableEntry; -import org.dromara.hutool.core.map.MapWrapper; -import org.dromara.hutool.core.text.StrUtil; -import org.dromara.hutool.core.util.ObjUtil; -import org.dromara.hutool.json.*; -import org.dromara.hutool.json.reader.JSONParser; -import org.dromara.hutool.json.reader.JSONTokener; -import org.dromara.hutool.json.serializer.JSONSerializer; -import org.dromara.hutool.json.serializer.SimpleJSONContext; -import org.dromara.hutool.json.serializer.TypeAdapterManager; -import org.dromara.hutool.json.xml.JSONXMLParser; -import org.dromara.hutool.json.xml.ParseConfig; - -import java.io.InputStream; -import java.io.Reader; -import java.io.Serializable; -import java.util.Optional; -import java.util.function.Predicate; - -/** - * 对象和JSON值映射器,用于转换对象为JSON对象中的值
- * 有效的JSON值包括: - *
    - *
  • JSONObject
  • - *
  • JSONArray
  • - *
  • String
  • - *
  • 数字(int、long等)
  • - *
  • Boolean值,如true或false
  • - *
  • {@code null}
  • - *
- * - * @author looly - * @since 6.0.0 - */ -public class JSONValueMapper implements Serializable { - - private static final long serialVersionUID = -6714488573738940582L; - - /** - * 创建ObjectMapper - * - * @param jsonConfig 来源对象 - * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 - * @return ObjectMapper - */ - public static JSONValueMapper of(final JSONConfig jsonConfig, final Predicate> predicate) { - return new JSONValueMapper(jsonConfig, predicate); - } - - private final JSONConfig jsonConfig; - private final Predicate> predicate; - - /** - * 构造 - * - * @param jsonConfig JSON配置 - * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 - */ - public JSONValueMapper(final JSONConfig jsonConfig, final Predicate> predicate) { - this.jsonConfig = ObjUtil.defaultIfNull(jsonConfig, JSONConfig::of); - this.predicate = predicate; - } - - /** - * 将JSONObject转换为JSONArray - * - * @param jsonObject JSONObject - * @return JSONArray - */ - public JSONArray mapFromJSONObject(final JSONObject jsonObject){ - final JSONArray array = new JSONArray(jsonConfig); - JSONArrayMapper.of(jsonObject, this.predicate) - .mapTo(array); - return array; - } - - /** - * 解析JSON字符串或XML字符串为JSON结构 - * - * @param source JSON字符串或XML字符串 - * @return JSON对象 - */ - public JSON map(final CharSequence source) { - final String jsonStr = StrUtil.trim(source); - if (StrUtil.startWith(jsonStr, '<')) { - // 可能为XML - final JSONObject jsonObject = new JSONObject(jsonConfig); - JSONXMLParser.of(ParseConfig.of(), this.predicate).parseJSONObject(jsonStr, jsonObject); - return jsonObject; - } - - return mapFromTokener(new JSONTokener(source)); - } - - /** - * 在需要的时候转换映射对象
- * 包装包括: - *
    - *
  • array or collection =》 JSONArray
  • - *
  • map =》 JSONObject
  • - *
  • standard property (Double, String, et al) =》 原对象
  • - *
  • 来自于java包 =》 字符串
  • - *
  • 其它 =》 尝试包装为JSONObject,否则返回{@code null}
  • - *
- * - * @param obj 被映射的对象 - * @return 映射后的值,null表示此值需被忽略 - */ - public JSON map(Object obj) { - if (null == obj) { - return null; - } - - if (obj instanceof Optional) { - obj = ((Optional) obj).orElse(null); - } else if (obj instanceof Opt) { - obj = ((Opt) obj).getOrNull(); - } - - if (obj instanceof JSON) { - return (JSON) obj; - } - - // 读取JSON流 - if(obj instanceof JSONTokener){ - return mapFromTokener((JSONTokener) obj); - }else if(obj instanceof JSONParser){ - return ((JSONParser)obj).parse(); - } else if (obj instanceof Reader) { - return mapFromTokener(new JSONTokener((Reader) obj)); - } else if (obj instanceof InputStream) { - return mapFromTokener(new JSONTokener((InputStream) obj)); - } - - // 自定义序列化 - final JSONSerializer serializer = TypeAdapterManager.getInstance() - .getSerializer(obj, obj.getClass()); - if (null != serializer) { - return serializer.serialize(obj, new SimpleJSONContext(null, this.jsonConfig)); - } - - // 原始类型 - if (JSONPrimitive.isTypeForJSONPrimitive(obj)) { - return new JSONPrimitive(obj, jsonConfig); - } - - // 特定对象转换 - try { - // JSONArray - if (// MapWrapper实现了Iterable会被当作JSONArray,此处做修正 - !(obj instanceof MapWrapper) && - (obj instanceof Iterable || ArrayUtil.isArray(obj))) { - final JSONArray jsonArray = new JSONArray(jsonConfig); - JSONArrayMapper.of(obj, predicate).mapTo(jsonArray); - return jsonArray; - } - - // 默认按照JSONObject对待 - final JSONObject jsonObject = new JSONObject(jsonConfig); - JSONObjectMapper.of(obj, predicate).mapTo(jsonObject); - return jsonObject; - } catch (final Exception exception) { - if (jsonConfig.isIgnoreError()) { - return null; - } - throw exception instanceof JSONException ? (JSONException) exception : new JSONException(exception); - } - } - - /** - * 从{@link JSONTokener} 中读取JSON字符串,并转换为JSON - * - * @param tokener {@link JSONTokener} - * @return JSON - */ - private JSON mapFromTokener(final JSONTokener tokener) { - return JSONParser.of(tokener, jsonConfig).setPredicate(this.predicate).parse(); - } -} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/package-info.java b/hutool-json/src/main/java/org/dromara/hutool/json/package-info.java index 862ee7ffe..5f871bc5f 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/package-info.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/package-info.java @@ -26,9 +26,9 @@ * JSON封装主要包括JSON表示和JSON转换: * *
{@code
- *               <--JSONConverter--              <---JSONParser----
- *     Java对象  <=================>   JSON对象   <=================>    JSON字符串
- *               ------mapper----->              ---JSONWriter---->
+ *               <-----JSONMapper-----              <---JSONParser----
+ *     Java对象  <====================>   JSON对象   <=================>    JSON字符串
+ *               -----JSONMapper----->              ---JSONWriter---->
  * }
* * 当然,为了高效转换,如果没有自定义需求,Java对象可以不通过JSON对象与JSON字符串转换: diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONMapper.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONMapper.java new file mode 100644 index 000000000..624091b5d --- /dev/null +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/JSONMapper.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2013-2024 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hutool.json.serializer; + +import org.dromara.hutool.core.lang.Opt; +import org.dromara.hutool.core.lang.mutable.MutableEntry; +import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.json.*; +import org.dromara.hutool.json.reader.JSONParser; +import org.dromara.hutool.json.reader.JSONTokener; +import org.dromara.hutool.json.xml.JSONXMLParser; +import org.dromara.hutool.json.xml.ParseConfig; + +import java.io.Serializable; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.Optional; +import java.util.function.Predicate; + +/** + * 对象和JSON值映射器,用于Java对象和JSON对象互转
+ *
    + *
  • Java对象转JSON:{@link #map(Object)}
  • + *
  • JSON转Java对象:{@link #toBean(JSON, Type)}
  • + *
+ *

+ * 转换依赖于{@link JSONSerializer}和{@link JSONDeserializer}的实现,通过{@link TypeAdapterManager}统一管理
+ * 序列化和反序列化定义于两个作用域,首先查找本类中定义的,如果没有,使用{@link TypeAdapterManager#getInstance()} 查找全局定义的。 + * + * @author looly + * @since 6.0.0 + */ +public class JSONMapper implements Serializable { + + private static final long serialVersionUID = -6714488573738940582L; + + /** + * 创建ObjectMapper + * + * @param jsonConfig 来源对象 + * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 + * @return ObjectMapper + */ + public static JSONMapper of(final JSONConfig jsonConfig, final Predicate> predicate) { + return new JSONMapper(jsonConfig, predicate); + } + + private final JSONConfig jsonConfig; + private final Predicate> predicate; + private TypeAdapterManager typeAdapterManager; + + /** + * 构造 + * + * @param jsonConfig JSON配置 + * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 + */ + public JSONMapper(final JSONConfig jsonConfig, final Predicate> predicate) { + this.jsonConfig = ObjUtil.defaultIfNull(jsonConfig, JSONConfig::of); + this.predicate = predicate; + } + + /** + * 获取自定义类型转换器,用于将自定义类型转换为JSONObject + * + * @return 类型转换器管理器 + */ + public TypeAdapterManager getTypeAdapterManager() { + return this.typeAdapterManager; + } + + /** + * 设置自定义类型转换器,用于将自定义类型转换为JSONObject + * + * @param typeAdapterManager 类型转换器管理器 + * @return this + */ + public JSONMapper setTypeAdapterManager(final TypeAdapterManager typeAdapterManager) { + this.typeAdapterManager = typeAdapterManager; + return this; + } + + /** + * 转为实体类对象 + * + * @param Bean类型 + * @param json JSON + * @param type {@link Type} + * @return 实体类对象 + */ + @SuppressWarnings("unchecked") + public T toBean(final JSON json, final Type type) { + if (null == type || Object.class == type) { + if (json instanceof JSONPrimitive) { + return (T) ((JSONPrimitive) json).getValue(); + } + return (T) this; + } + + JSONDeserializer deserializer = null; + // 自定义反序列化 + if (null != this.typeAdapterManager) { + deserializer = this.typeAdapterManager.getDeserializer(json, type); + } + // 全局自定义反序列化 + if (null == deserializer) { + deserializer = TypeAdapterManager.getInstance().getDeserializer(json, type); + } + final boolean ignoreError = ObjUtil.defaultIfNull(this.jsonConfig, JSONConfig::isIgnoreError, false); + if (null == deserializer) { + if (ignoreError) { + return null; + } + throw new JSONException("No deserializer for type: " + type); + } + + try { + return (T) deserializer.deserialize(json, type); + } catch (final Exception e) { + if (ignoreError) { + return null; + } + throw e; + } + } + + /** + * 将JSONObject转换为JSONArrayM
+ * 在普通的Serializer中,JSONObject会被直接返回,如果想转为JSONArray, + * + * @param jsonObject JSONObject + * @return JSONArray + */ + public JSONArray mapFromJSONObject(final JSONObject jsonObject) { + final JSONArray array = JSONUtil.ofArray(jsonConfig); + for (final Map.Entry entry : jsonObject) { + array.add(entry.getValue()); + } + return array; + } + + /** + * 解析JSON字符串或XML字符串为JSON结构
+ * 在普通的Serializer中,字符串会被作为普通字符串转为{@link JSONPrimitive},此处做区分 + * + * @param source JSON字符串或XML字符串 + * @return JSON对象 + */ + public JSON map(final CharSequence source) { + final String jsonStr = StrUtil.trim(source); + if (StrUtil.startWith(jsonStr, '<')) { + // 可能为XML + final JSONObject jsonObject = JSONUtil.ofObj(jsonConfig); + JSONXMLParser.of(ParseConfig.of(), this.predicate).parseJSONObject(jsonStr, jsonObject); + return jsonObject; + } + + return mapFromTokener(new JSONTokener(source)); + } + + /** + * 在需要的时候转换映射对象
+ * 包装包括: + *
    + *
  • array or collection =》 JSONArray
  • + *
  • map =》 JSONObject
  • + *
  • standard property (Double, String, et al) =》 原对象
  • + *
  • 来自于java包 =》 字符串
  • + *
  • 其它 =》 尝试包装为JSONObject,否则返回{@code null}
  • + *
+ * + * @param obj 被映射的对象 + * @return 映射后的值,null表示此值需被忽略 + */ + public JSON map(Object obj) { + if (null == obj) { + return null; + } + + if (obj instanceof Optional) { + obj = ((Optional) obj).orElse(null); + if (null == obj) { + return null; + } + } else if (obj instanceof Opt) { + obj = ((Opt) obj).getOrNull(); + if (null == obj) { + return null; + } + } + + if (obj instanceof JSON) { + return (JSON) obj; + } + + final Class clazz = obj.getClass(); + JSONSerializer serializer = null; + // 自定义序列化 + if (null != this.typeAdapterManager) { + serializer = this.typeAdapterManager.getSerializer(obj, clazz); + } + // 全局自定义序列化 + if (null == serializer) { + serializer = TypeAdapterManager.getInstance().getSerializer(obj, clazz); + } + final boolean ignoreError = ObjUtil.defaultIfNull(this.jsonConfig, JSONConfig::isIgnoreError, false); + if (null == serializer) { + if (ignoreError) { + return null; + } + throw new JSONException("No deserializer for type: " + obj.getClass()); + } + + try { + return serializer.serialize(obj, new SimpleJSONContext(null, this.jsonConfig)); + } catch (final Exception e) { + if (ignoreError) { + return null; + } + throw e; + } + } + + /** + * 从{@link JSONTokener} 中读取JSON字符串,并转换为JSON + * + * @param tokener {@link JSONTokener} + * @return JSON + */ + private JSON mapFromTokener(final JSONTokener tokener) { + return JSONParser.of(tokener, jsonConfig).setPredicate(this.predicate).parse(); + } +} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/TypeAdapterManager.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/TypeAdapterManager.java index a245e7524..ec4caf41f 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/TypeAdapterManager.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/TypeAdapterManager.java @@ -284,6 +284,8 @@ public class TypeAdapterManager { private static TypeAdapterManager registerDefault(final TypeAdapterManager manager) { // 自定义序列化器 + manager.register(ResourceBundleSerializer.INSTANCE); + manager.register(TokenerSerializer.INSTANCE); // 自定义反序列化器 manager.register(KBeanDeserializer.INSTANCE); diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/ArrayTypeAdapter.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/ArrayTypeAdapter.java index 50f7df5ec..178085562 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/ArrayTypeAdapter.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/ArrayTypeAdapter.java @@ -18,10 +18,11 @@ package org.dromara.hutool.json.serializer.impl; import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.collection.iter.ArrayIter; +import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.reflect.TypeUtil; -import org.dromara.hutool.json.JSON; -import org.dromara.hutool.json.JSONArray; -import org.dromara.hutool.json.JSONObject; +import org.dromara.hutool.json.*; +import org.dromara.hutool.json.reader.JSONParser; +import org.dromara.hutool.json.reader.JSONTokener; import org.dromara.hutool.json.serializer.JSONContext; import org.dromara.hutool.json.serializer.MatcherJSONDeserializer; import org.dromara.hutool.json.serializer.MatcherJSONSerializer; @@ -58,6 +59,9 @@ public class ArrayTypeAdapter implements MatcherJSONSerializer, MatcherJ @Override public JSON serialize(final Object bean, final JSONContext context) { + if (bean instanceof byte[]) { + return serializeBytes((byte[]) bean, context); + } return IterTypeAdapter.INSTANCE.serialize(new ArrayIter<>(bean), context); } @@ -74,6 +78,36 @@ public class ArrayTypeAdapter implements MatcherJSONSerializer, MatcherJ return result; } + /** + * 序列化二进制流为JSON + * + * @param bytes 二进制流 + * @param context JSON上下文 + * @return JSONArray + */ + private JSON serializeBytes(final byte[] bytes, final JSONContext context) { + final JSONConfig config = context.config(); + switch (bytes[0]) { + case '{': + case '[': + return JSONParser.of(new JSONTokener(IoUtil.toStream(bytes)), config).parse(); + } + + // https://github.com/dromara/hutool/issues/2369 + // 非标准的二进制流,则按照普通数组对待 + final JSONArray result; + final JSON contextJson = context.getContextJson(); + if (contextJson instanceof JSONArray) { + result = (JSONArray) contextJson; + } else { + result = JSONUtil.ofArray(config); + } + for (final byte b : bytes) { + result.set(b); + } + return result; + } + /** * 将JSONObject填充到数组 * diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/BeanTypeAdapter.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/BeanTypeAdapter.java index 17791de91..9d5351b43 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/BeanTypeAdapter.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/BeanTypeAdapter.java @@ -26,7 +26,7 @@ import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.json.InternalJSONUtil; import org.dromara.hutool.json.JSON; import org.dromara.hutool.json.JSONObject; -import org.dromara.hutool.json.convert.JSONObjectValueProvider; +import org.dromara.hutool.json.support.JSONObjectValueProvider; import org.dromara.hutool.json.serializer.JSONContext; import org.dromara.hutool.json.serializer.MatcherJSONDeserializer; import org.dromara.hutool.json.serializer.MatcherJSONSerializer; diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/IterTypeAdapter.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/IterTypeAdapter.java index 67c1b8add..6cd35f825 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/IterTypeAdapter.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/IterTypeAdapter.java @@ -17,6 +17,7 @@ package org.dromara.hutool.json.serializer.impl; import org.dromara.hutool.core.collection.CollUtil; +import org.dromara.hutool.core.map.MapWrapper; import org.dromara.hutool.core.reflect.TypeUtil; import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.json.JSON; @@ -46,6 +47,9 @@ public class IterTypeAdapter implements MatcherJSONSerializer, MatcherJS @Override public boolean match(final Object bean, final JSONContext context) { + if(bean instanceof MapWrapper){ + return false; + } return bean instanceof Iterable || bean instanceof Iterator; } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/KBeanDeserializer.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/KBeanDeserializer.java index c1774ca92..ca8a42a44 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/KBeanDeserializer.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/KBeanDeserializer.java @@ -21,7 +21,7 @@ import org.dromara.hutool.core.reflect.kotlin.KClassUtil; import org.dromara.hutool.json.JSON; import org.dromara.hutool.json.JSONGetter; import org.dromara.hutool.json.JSONObject; -import org.dromara.hutool.json.convert.JSONObjectValueProvider; +import org.dromara.hutool.json.support.JSONObjectValueProvider; import org.dromara.hutool.json.serializer.MatcherJSONDeserializer; import java.lang.reflect.Type; diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/RecordDeserializer.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/RecordDeserializer.java index 98681dca6..8e8068647 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/RecordDeserializer.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/RecordDeserializer.java @@ -20,7 +20,7 @@ import org.dromara.hutool.core.bean.RecordUtil; import org.dromara.hutool.core.reflect.TypeUtil; import org.dromara.hutool.json.JSON; import org.dromara.hutool.json.JSONObject; -import org.dromara.hutool.json.convert.JSONObjectValueProvider; +import org.dromara.hutool.json.support.JSONObjectValueProvider; import org.dromara.hutool.json.serializer.MatcherJSONDeserializer; import java.lang.reflect.Type; diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/ResourceBundleSerializer.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/ResourceBundleSerializer.java new file mode 100644 index 000000000..b0a6b2909 --- /dev/null +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/ResourceBundleSerializer.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2024 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hutool.json.serializer.impl; + +import org.dromara.hutool.core.convert.ConvertUtil; +import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.core.text.split.SplitUtil; +import org.dromara.hutool.json.JSON; +import org.dromara.hutool.json.JSONObject; +import org.dromara.hutool.json.JSONUtil; +import org.dromara.hutool.json.serializer.JSONContext; +import org.dromara.hutool.json.serializer.MatcherJSONSerializer; + +import java.util.Enumeration; +import java.util.ResourceBundle; + +/** + * {@link ResourceBundle}序列化器 + * + * @author looly + * @since 6.0.0 + */ +public class ResourceBundleSerializer implements MatcherJSONSerializer { + + /** + * 单例 + */ + public static final ResourceBundleSerializer INSTANCE = new ResourceBundleSerializer(); + + @Override + public boolean match(final Object bean, final JSONContext context) { + return bean instanceof ResourceBundle; + } + + @Override + public JSON serialize(final ResourceBundle bean, final JSONContext context) { + final JSONObject result; + + final JSON json = context.getContextJson(); + if (json instanceof JSONObject) { + result = (JSONObject) json; + } else { + result = JSONUtil.ofObj(context.config()); + } + + mapFromResourceBundle(bean, result); + return result; + } + + /** + * 从{@link ResourceBundle}转换 + * + * @param bundle ResourceBundle + * @param jsonObject {@link JSONObject} + */ + private void mapFromResourceBundle(final ResourceBundle bundle, final JSONObject jsonObject) { + final Enumeration keys = bundle.getKeys(); + while (keys.hasMoreElements()) { + final String key = keys.nextElement(); + if (key != null) { + propertyPut(jsonObject, key, bundle.getString(key)); + } + } + } + + /** + * 将Property的键转化为JSON形式
+ * 用于识别类似于:org.dromara.hutool.json这类用点隔开的键
+ * 注意:不允许重复键 + * + * @param jsonObject JSONObject + * @param key 键 + * @param value 值 + */ + private static void propertyPut(final JSONObject jsonObject, final Object key, final Object value) { + final String[] path = SplitUtil.splitToArray(ConvertUtil.toStr(key), StrUtil.DOT); + final int last = path.length - 1; + JSONObject target = jsonObject; + for (int i = 0; i < last; i += 1) { + final String segment = path[i]; + JSONObject nextTarget = target.getJSONObject(segment); + if (nextTarget == null) { + nextTarget = JSONUtil.ofObj(target.config()); + target.set(segment, nextTarget); + } + target = nextTarget; + } + target.set(path[last], value); + } +} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TokenerSerializer.java b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TokenerSerializer.java new file mode 100644 index 000000000..a95ac1854 --- /dev/null +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serializer/impl/TokenerSerializer.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hutool.json.serializer.impl; + +import org.dromara.hutool.json.JSON; +import org.dromara.hutool.json.JSONConfig; +import org.dromara.hutool.json.reader.JSONParser; +import org.dromara.hutool.json.reader.JSONTokener; +import org.dromara.hutool.json.serializer.JSONContext; +import org.dromara.hutool.json.serializer.MatcherJSONSerializer; + +import java.io.InputStream; +import java.io.Reader; + +/** + * JSONTokener及其读取流的JSON序列化器实现 + * + * @author looly + * @since 6.0.0 + */ +public class TokenerSerializer implements MatcherJSONSerializer { + + /** + * 单例 + */ + public static final TokenerSerializer INSTANCE = new TokenerSerializer(); + + @Override + public boolean match(final Object bean, final JSONContext context) { + return bean instanceof Reader + || bean instanceof InputStream; + } + + @Override + public JSON serialize(final Object bean, final JSONContext context) { + // 读取JSON流 + if (bean instanceof JSONTokener) { + return mapFromTokener((JSONTokener) bean, context.config()); + } else if (bean instanceof JSONParser) { + return ((JSONParser) bean).parse(); + } else if (bean instanceof Reader) { + return mapFromTokener(new JSONTokener((Reader) bean), context.config()); + } else if (bean instanceof InputStream) { + return mapFromTokener(new JSONTokener((InputStream) bean), context.config()); + } + + throw new IllegalArgumentException("Unsupported source: " + bean); + } + + /** + * 从{@link JSONTokener} 中读取JSON字符串,并转换为JSON + * + * @param tokener {@link JSONTokener} + * @return JSON + */ + private JSON mapFromTokener(final JSONTokener tokener, final JSONConfig config) { + return JSONParser.of(tokener, config).setPredicate(null).parse(); + } +} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/support/JSONNodeBeanCreator.java b/hutool-json/src/main/java/org/dromara/hutool/json/support/JSONNodeBeanCreator.java new file mode 100644 index 000000000..eef9591b4 --- /dev/null +++ b/hutool-json/src/main/java/org/dromara/hutool/json/support/JSONNodeBeanCreator.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hutool.json.support; + +import org.dromara.hutool.core.bean.path.BeanPath; +import org.dromara.hutool.core.bean.path.NodeBeanCreator; +import org.dromara.hutool.core.bean.path.node.NameNode; +import org.dromara.hutool.core.bean.path.node.Node; +import org.dromara.hutool.json.JSONConfig; +import org.dromara.hutool.json.JSONUtil; + +/** + * JSON节点Bean创建器 + * + * @author looly + * @since 6.0.0 + */ +public class JSONNodeBeanCreator implements NodeBeanCreator { + + private final JSONConfig config; + + /** + * 构造 + * + * @param config JSON配置 + */ + public JSONNodeBeanCreator(final JSONConfig config) { + this.config = config; + } + + @Override + public Object create(final Object parent, final BeanPath beanPath) { + final BeanPath next = beanPath.next(); + if (null != next) { + final Node node = next.getNode(); + if (node instanceof NameNode) { + final NameNode nameNode = (NameNode) node; + if (nameNode.isNumber()) { + return JSONUtil.ofArray(config); + } + return JSONUtil.ofObj(config); + } + } + return JSONUtil.ofObj(config); + } +} diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONObjectValueProvider.java b/hutool-json/src/main/java/org/dromara/hutool/json/support/JSONObjectValueProvider.java similarity index 78% rename from hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONObjectValueProvider.java rename to hutool-json/src/main/java/org/dromara/hutool/json/support/JSONObjectValueProvider.java index 302d4dfbd..f33d8b4e3 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONObjectValueProvider.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/support/JSONObjectValueProvider.java @@ -14,16 +14,18 @@ * limitations under the License. */ -package org.dromara.hutool.json.convert; +package org.dromara.hutool.json.support; import org.dromara.hutool.core.bean.copier.ValueProvider; +import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.json.JSON; import org.dromara.hutool.json.JSONObject; import java.lang.reflect.Type; /** - * JSONObject值提供者,用于将JSONObject中的值注入Bean + * JSONObject值提供者,用于将JSONObject中的值注入Bean
+ * 兼容下划线模式的JSON转换为驼峰模式 * * @author Looly * @since 6.0.0 @@ -42,16 +44,19 @@ public class JSONObjectValueProvider implements ValueProvider { } @Override - public Object value(final String key, final Type valueType) { - final JSON value = jsonObject.get(key); - if (null == value) { - return null; - } - return value.toBean(valueType); + public boolean containsKey(final String key) { + return jsonObject.containsKey(key) || jsonObject.containsKey(StrUtil.toUnderlineCase(key)); } @Override - public boolean containsKey(final String key) { - return jsonObject.containsKey(key); + public Object value(final String key, final Type valueType) { + JSON value = jsonObject.get(key); + if (null == value) { + value = jsonObject.get(StrUtil.toUnderlineCase(key)); + if(null == value){ + return null; + } + } + return value.toBean(valueType); } } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONStrFormatter.java b/hutool-json/src/main/java/org/dromara/hutool/json/support/JSONStrFormatter.java similarity index 99% rename from hutool-json/src/main/java/org/dromara/hutool/json/JSONStrFormatter.java rename to hutool-json/src/main/java/org/dromara/hutool/json/support/JSONStrFormatter.java index d1266cf0c..0dd02dfe7 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONStrFormatter.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/support/JSONStrFormatter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.dromara.hutool.json; +package org.dromara.hutool.json.support; import org.dromara.hutool.core.text.CharUtil; import org.dromara.hutool.core.text.StrUtil; diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/convert/package-info.java b/hutool-json/src/main/java/org/dromara/hutool/json/support/package-info.java similarity index 82% rename from hutool-json/src/main/java/org/dromara/hutool/json/convert/package-info.java rename to hutool-json/src/main/java/org/dromara/hutool/json/support/package-info.java index d0c3246d6..2f69d23a9 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/convert/package-info.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/support/package-info.java @@ -15,9 +15,9 @@ */ /** - * JSON中对象、值类型转换封装 + * JSON的支持类,如用于转换和BeanPath操作的对象,还有用于格式化的对象等 * * @author Looly * @since 6.0.0 */ -package org.dromara.hutool.json.convert; +package org.dromara.hutool.json.support; diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/Issue1200Test.java b/hutool-json/src/test/java/org/dromara/hutool/json/Issue1200Test.java index 99b624cc6..afe8c3810 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/Issue1200Test.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/Issue1200Test.java @@ -35,7 +35,12 @@ public class Issue1200Test { JSONConfig.of().setIgnoreError(true)); final ResultBean resultBean = jsonObject.toBean(ResultBean.class); - Assertions.assertNull(resultBean.getItems().get(0).get(0)); + // json对象转ItemsBean,但是字段对应不上,保留空对象 + Assertions.assertNull(resultBean.getItems().get(0).get(0).get(0).getDetail()); + + // 字符串转对象,失败,为null + Assertions.assertNull(resultBean.getItems().get(0).get(0).get(1)); + Assertions.assertNull(resultBean.getItems().get(0).get(0).get(2)); } @Data diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/Issue867Test.java b/hutool-json/src/test/java/org/dromara/hutool/json/Issue867Test.java index af7972f76..9485047a1 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/Issue867Test.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/Issue867Test.java @@ -21,14 +21,18 @@ import org.dromara.hutool.core.annotation.Alias; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +/** + * 兼容下划线模式的JSON转换为驼峰模式
+ * 逻辑见:JSONObjectValueProvider + */ public class Issue867Test { @Test - public void toBeanTest(){ + public void toBeanTest() { final String jsonStr = "{\"abc_1d\":\"123\",\"abc_d\":\"456\",\"abc_de\":\"789\"}"; final JSONObject json = JSONUtil.parseObj(jsonStr); //Console.log(json); - final Test02 bean = JSONUtil.toBean(json,Test02.class); + final Test02 bean = JSONUtil.toBean(json, Test02.class); Assertions.assertEquals("123", bean.getAbc1d()); Assertions.assertEquals("456", bean.getAbcD()); Assertions.assertEquals("789", bean.getAbcDe()); diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/IssueI9DX5HTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/IssueI9DX5HTest.java index 7c0a89bb0..facc913cd 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/IssueI9DX5HTest.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/IssueI9DX5HTest.java @@ -17,7 +17,7 @@ package org.dromara.hutool.json; import org.dromara.hutool.core.text.StrUtil; -import org.dromara.hutool.json.mapper.JSONValueMapper; +import org.dromara.hutool.json.serializer.JSONMapper; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -27,7 +27,7 @@ public class IssueI9DX5HTest { @Test void xmlToJSONTest() { final String xml = "你好"; - final JSONValueMapper mapper = JSONValueMapper.of(JSONConfig.of(), entry -> { + final JSONMapper mapper = JSONMapper.of(JSONConfig.of(), entry -> { entry.setKey(StrUtil.toUnderlineCase((CharSequence) entry.getKey())); return true; }); diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/JSONStrFormatterTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/JSONStrFormatterTest.java index c027e94b7..99628b6fe 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/JSONStrFormatterTest.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/JSONStrFormatterTest.java @@ -17,6 +17,7 @@ package org.dromara.hutool.json; import org.dromara.hutool.core.lang.Console; +import org.dromara.hutool.json.support.JSONStrFormatter; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/hutool-swing/src/main/java/org/dromara/hutool/swing/img/ImgUtil.java b/hutool-swing/src/main/java/org/dromara/hutool/swing/img/ImgUtil.java index 29e54a3eb..a5aab30ef 100644 --- a/hutool-swing/src/main/java/org/dromara/hutool/swing/img/ImgUtil.java +++ b/hutool-swing/src/main/java/org/dromara/hutool/swing/img/ImgUtil.java @@ -564,7 +564,7 @@ public class ImgUtil { } // endregion - // region ----- convert + // region ----- support /** * 图像类型转换:GIF=》JPG、GIF=》PNG、PNG=》JPG、PNG=》GIF(X)、BMP=》PNG