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

View File

@ -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之间的映射封装包括
* <ul>
* <li>JSONObjectMapper: 转换对象为JSONObject</li>
* <li>JSONArrayMapper: 转换对象为JSONArray</li>
* <li>JSONValueMapper: 转换对象为JSON值</li>
* </ul>
* 标记可读Bean<br>
* 即包含有可读字段的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 {
}

View File

@ -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<br>
* 即包含有可写字段的Bean使用此注解标记如含有public的字段或setter方法
*
* @author looly
* @since 6.0.0
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface WritableBean {
}

View File

@ -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);
}

View File

@ -31,7 +31,7 @@ public interface ValueProvider<K>{
/**
* 获取值<br>
* 返回值一般需要匹配被注入类型如果不匹配会调用默认转换 Convert#convert(Type, Object)实现转换
* 返回值一般需要匹配被注入类型如果不匹配会调用默认转换 Convert#support(Type, Object)实现转换
*
* @param key Bean对象中参数名
* @param valueType 被注入的值的类型

View File

@ -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<BeanPath> {
private final Node node;
private final String child;
private NodeBeanCreator beanCreator;
/**
* 构造
@ -74,6 +72,7 @@ public class BeanPath implements Iterator<BeanPath> {
* @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<BeanPath> {
}
}
/**
* 设置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<BeanPath> {
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<BeanPath> {
", child='" + child + '\'' +
'}';
}
/**
* 子节点值是否为列表
*
* @param node 节点
* @return 是否为列表
*/
private static boolean isListNode(final Node node) {
if (node instanceof NameNode) {
return ((NameNode) node).isNumber();
}
return false;
}
}

View File

@ -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());
}
}

View File

@ -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创建器<br>
* 用于创建Bean路径节点对应的Bean
*
* @author looly
* @since 6.0.0
*/
public interface NodeBeanCreator {
/**
* 创建Bean<br>
* beanPath对应当前的路径即如果父对象为a则beanPath为a.b则创建的Bean为a.b.c对应的Bean对象<br>
* 给定的a一定存在但是本路径中b对应的Bean不存在则创建的对象是b的值这个值用c表示
*
* @param parent 父Bean
* @param beanPath 当前路径
* @return Bean
*/
Object create(final Object parent, final BeanPath beanPath);
}

View File

@ -34,6 +34,15 @@ public class NameNode implements Node {
private final String name;
/**
* 获取节点名
*
* @return 节点名
*/
public String getName() {
return name;
}
/**
* 构造
*

View File

@ -505,7 +505,7 @@ public class Hashids implements Encoder<long[], String>, Decoder<String, long[]>
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();

View File

@ -135,7 +135,7 @@ public class PercentCodec implements Encoder<byte[], byte[]>, Serializable {
// 对于空格单独处理
rewrittenPath.append('+');
} else {
// convert to external encoding before hex conversion
// support to external encoding before hex conversion
try {
writer.write(c);
writer.flush();

View File

@ -67,7 +67,7 @@ public class Z85Codec implements Encoder<byte[], String>, Decoder<String, byte[]
// Support inputs that are not divisible by 4 with no remainder.
final int padding = 4 - (remainder == 0 ? 4 : remainder);
// SPEC: 4 octets convert into 5 printable characters.
// SPEC: 4 octets support into 5 printable characters.
// char.length = length + 1/4 length == 5/4 length.
final char[] chars = new char[length + (length >>> 2) + (remainder == 0 ? 0 : 1)];
int idx = 0;

View File

@ -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);

View File

@ -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());
}
}

View File

@ -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());
}
/**

View File

@ -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());
}
/**

View File

@ -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);
}
/**

View File

@ -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) {

View File

@ -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;

View File

@ -408,7 +408,7 @@ public class FastDateFormat extends Format implements PositionDateParser, DatePr
return printer.getMaxLengthEstimate();
}
// convert DateTimeFormatter
// support DateTimeFormatter
// -----------------------------------------------------------------------
/**

View File

@ -266,7 +266,7 @@ public class Version implements Comparable<Version>, 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;

View File

@ -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);
}

View File

@ -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");
}
}

View File

@ -16,24 +16,23 @@
package org.dromara.hutool.json;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.bean.copier.CopyOptions;
import org.dromara.hutool.core.codec.binary.HexUtil;
import org.dromara.hutool.core.convert.ConvertUtil;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.core.map.CaseInsensitiveLinkedMap;
import org.dromara.hutool.core.map.CaseInsensitiveTreeMap;
import org.dromara.hutool.core.math.NumberUtil;
import org.dromara.hutool.core.text.CharUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.split.SplitUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.mapper.JSONValueMapper;
import org.dromara.hutool.json.serializer.JSONMapper;
import org.dromara.hutool.json.reader.JSONTokener;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* 内部JSON工具类仅用于JSON内部使用
@ -88,65 +87,6 @@ public final class InternalJSONUtil {
return string;
}
/**
* 值转为String用于JSON中规则为
* <ul>
* <li>对象如果是数组或Collection包装为{@link JSONArray}</li>
* <li>对象如果是Map包装为{@link JSONObject}</li>
* <li>对象如果是数字使用{@link NumberUtil#toStr(Number)}转换为字符串</li>
* <li>其他情况调用toString并使用双引号包装</li>
* </ul>
*
* @param value 需要转为字符串的对象
* @return 字符串
* @throws JSONException If the value is or contains an invalid number.
*/
static String valueToString(final Object value) throws JSONException {
if (value == null) {
return StrUtil.NULL;
}
if (value instanceof Number) {
return NumberUtil.toStr((Number) value);
} else if (value instanceof Boolean || value instanceof JSONObject || value instanceof JSONArray) {
return value.toString();
} else if (value instanceof Map) {
final Map<?, ?> map = (Map<?, ?>) value;
return JSONUtil.parseObj(map).toString();
} else if (value instanceof Collection) {
final Collection<?> coll = (Collection<?>) value;
return JSONUtil.parseArray(coll).toString();
} else if (ArrayUtil.isArray(value)) {
return JSONUtil.parseArray(value).toString();
} else {
return quote(value.toString());
}
}
/**
* 将Property的键转化为JSON形式<br>
* 用于识别类似于org.dromara.hutool.json这类用点隔开的键<br>
* 注意不允许重复键
*
* @param jsonObject JSONObject
* @param key
* @param value
*/
public static void propertyPut(final JSONObject jsonObject, final Object key, final Object value) {
final String[] path = SplitUtil.splitToArray(ConvertUtil.toStr(key), StrUtil.DOT);
final int last = path.length - 1;
JSONObject target = jsonObject;
for (int i = 0; i < last; i += 1) {
final String segment = path[i];
JSONObject nextTarget = target.getJSONObject(segment);
if (nextTarget == null) {
nextTarget = new JSONObject(target.config());
target.set(segment, nextTarget);
}
target = nextTarget;
}
target.set(path[last], value);
}
/**
* 默认情况下是否忽略null值的策略选择以下对象不忽略null值其它对象忽略
*
@ -179,7 +119,7 @@ public final class InternalJSONUtil {
.setIgnoreError(config.isIgnoreError())
.setIgnoreNullValue(config.isIgnoreNullValue())
.setTransientSupport(config.isTransientSupport())
.setConverter((targetType, value) -> JSONValueMapper.of(config, null).map(value));
.setConverter((targetType, value) -> JSONMapper.of(config, null).map(value));
}
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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

View File

@ -21,6 +21,10 @@ import org.dromara.hutool.core.annotation.Alias;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
* 兼容下划线模式的JSON转换为驼峰模式<br>
* 逻辑见JSONObjectValueProvider
*/
public class Issue867Test {
@Test

View File

@ -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 = "<GoodMsg>你好</GoodMsg>";
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;
});

View File

@ -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;

View File

@ -564,7 +564,7 @@ public class ImgUtil {
}
// endregion
// region ----- convert
// region ----- support
/**
* 图像类型转换GIF=JPGGIF=PNGPNG=JPGPNG=GIF(X)BMP=PNG