修复Pair反序列化报错问题

This commit is contained in:
Looly 2023-04-05 01:05:07 +08:00
parent 84e8456377
commit a97901f0e3
7 changed files with 283 additions and 82 deletions

View File

@ -2,7 +2,7 @@
# 🚀Changelog
-------------------------------------------------------------------------------------------------------------
# 5.8.17.M1 (2023-04-02)
# 5.8.17.M1 (2023-04-05)
### 🐣新特性
* 【core 】 SerializeUtil.deserialize增加白名单类避免RCE vulnerabilityissue#3021@Github
@ -18,6 +18,7 @@
* 【core 】 CollUtil.split优化切割列表参数判断避免OOMpr#3026@Github
* 【core 】 修复FileUtil.move传入相同目录或子目录丢失源目录的问题pr#3032@Github
* 【core 】 修复SafeConcurrentHashMap.computeIfAbsent可能存在的结果为null的情况issue#I6RVMY@Gitee
* 【json 】 修复Pair反序列化报错问题issue#I6SZYB@Gitee
-------------------------------------------------------------------------------------------------------------
# 5.8.16 (2023-03-26)

View File

@ -6,12 +6,7 @@ import cn.hutool.core.convert.impl.MapConverter;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.text.UnicodeUtil;
import cn.hutool.core.util.ByteUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.*;
import java.lang.reflect.Type;
import java.math.BigDecimal;
@ -19,14 +14,7 @@ import java.math.BigInteger;
import java.nio.charset.Charset;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**

View File

@ -1,48 +1,13 @@
package cn.hutool.core.convert;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.impl.ArrayConverter;
import cn.hutool.core.convert.impl.AtomicBooleanConverter;
import cn.hutool.core.convert.impl.AtomicIntegerArrayConverter;
import cn.hutool.core.convert.impl.AtomicLongArrayConverter;
import cn.hutool.core.convert.impl.AtomicReferenceConverter;
import cn.hutool.core.convert.impl.BeanConverter;
import cn.hutool.core.convert.impl.BooleanConverter;
import cn.hutool.core.convert.impl.CalendarConverter;
import cn.hutool.core.convert.impl.CharacterConverter;
import cn.hutool.core.convert.impl.CharsetConverter;
import cn.hutool.core.convert.impl.ClassConverter;
import cn.hutool.core.convert.impl.CollectionConverter;
import cn.hutool.core.convert.impl.CurrencyConverter;
import cn.hutool.core.convert.impl.DateConverter;
import cn.hutool.core.convert.impl.DurationConverter;
import cn.hutool.core.convert.impl.EnumConverter;
import cn.hutool.core.convert.impl.LocaleConverter;
import cn.hutool.core.convert.impl.MapConverter;
import cn.hutool.core.convert.impl.NumberConverter;
import cn.hutool.core.convert.impl.OptConverter;
import cn.hutool.core.convert.impl.OptionalConverter;
import cn.hutool.core.convert.impl.PathConverter;
import cn.hutool.core.convert.impl.PeriodConverter;
import cn.hutool.core.convert.impl.PrimitiveConverter;
import cn.hutool.core.convert.impl.ReferenceConverter;
import cn.hutool.core.convert.impl.StackTraceElementConverter;
import cn.hutool.core.convert.impl.StringConverter;
import cn.hutool.core.convert.impl.TemporalAccessorConverter;
import cn.hutool.core.convert.impl.TimeZoneConverter;
import cn.hutool.core.convert.impl.URIConverter;
import cn.hutool.core.convert.impl.URLConverter;
import cn.hutool.core.convert.impl.UUIDConverter;
import cn.hutool.core.convert.impl.*;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.map.SafeConcurrentHashMap;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.ServiceLoaderUtil;
import cn.hutool.core.util.TypeUtil;
import cn.hutool.core.util.*;
import java.io.Serializable;
import java.lang.ref.SoftReference;
@ -54,35 +19,10 @@ import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.MonthDay;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Period;
import java.time.ZonedDateTime;
import java.time.*;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Collection;
import java.util.Currency;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicLongArray;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.DoubleAdder;
import java.util.concurrent.atomic.LongAdder;
import java.util.*;
import java.util.concurrent.atomic.*;
/**
* 转换器登记中心
@ -357,6 +297,12 @@ public class ConverterRegistry implements Serializable {
return (T) mapConverter.convert(value, (Map<?, ?>) defaultValue);
}
// Map类型不可以默认强转
if (Map.Entry.class.isAssignableFrom(rowType)) {
final EntryConverter mapConverter = new EntryConverter(type);
return (T) mapConverter.convert(value, (Map.Entry<?, ?>) defaultValue);
}
// 默认强转
if (rowType.isInstance(value)) {
return (T) value;
@ -462,6 +408,7 @@ public class ConverterRegistry implements Serializable {
defaultConverterMap.put(StackTraceElement.class, new StackTraceElementConverter());// since 4.5.2
defaultConverterMap.put(Optional.class, new OptionalConverter());// since 5.0.0
defaultConverterMap.put(Opt.class, new OptConverter());// since 5.7.16
defaultConverterMap.put(Pair.class, new PairConverter(Pair.class));// since 5.8.17
return this;
}

View File

@ -0,0 +1,115 @@
package cn.hutool.core.convert.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.AbstractConverter;
import cn.hutool.core.convert.ConvertException;
import cn.hutool.core.convert.ConverterRegistry;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.TypeUtil;
import java.lang.reflect.Type;
import java.util.Map;
public class EntryConverter extends AbstractConverter<Map.Entry<?, ?>> {
/** Pair类型 */
private final Type pairType;
/** 键类型 */
private final Type keyType;
/** 值类型 */
private final Type valueType;
/**
* 构造Pair的key和value泛型类型自动获取
*
* @param entryType Map类型
*/
public EntryConverter(Type entryType) {
this(entryType, TypeUtil.getTypeArgument(entryType, 0), TypeUtil.getTypeArgument(entryType, 1));
}
/**
* 构造
*
* @param entryType Pair类型
* @param keyType 键类型
* @param valueType 值类型
*/
public EntryConverter(Type entryType, Type keyType, Type valueType) {
this.pairType = entryType;
this.keyType = keyType;
this.valueType = valueType;
}
@SuppressWarnings("rawtypes")
@Override
protected Map.Entry<?, ?> convertInternal(Object value) {
Map map = null;
if (value instanceof Pair) {
final Pair pair = (Pair) value;
map = MapUtil.of(pair.getKey(), pair.getValue());
}else if (value instanceof Map) {
map = (Map) value;
} else if (value instanceof CharSequence) {
final CharSequence str = (CharSequence) value;
map = strToMap(str);
} else if (BeanUtil.isReadableBean(value.getClass())) {
map = BeanUtil.beanToMap(value);
}
if (null != map) {
return mapToEntry(pairType, keyType, valueType, map);
}
throw new ConvertException("Unsupported to map from [{}] of type: {}", value, value.getClass().getName());
}
/**
* 字符串转单个键值对的Map支持分隔符{@code :}{@code =}{@code ,}
*
* @param str 字符串
* @return map or null
*/
private static Map<CharSequence, CharSequence> strToMap(final CharSequence str) {
// key:value key=value key,value
final int index = StrUtil.indexOf(str, '=', 0, str.length());
if (index > -1) {
return MapUtil.of(str.subSequence(0, index + 1), str.subSequence(index, str.length()));
}
return null;
}
/**
* Map转Entry
*
* @param targetType 目标的Map类型
* @param keyType 键类型
* @param valueType 值类型
* @param map 被转换的map
* @return Entry
*/
@SuppressWarnings("rawtypes")
private static Map.Entry<?, ?> mapToEntry(final Type targetType, final Type keyType, final Type valueType, final Map map) {
Object key = null;
Object value = null;
if (1 == map.size()) {
final Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
key = entry.getKey();
value = entry.getValue();
} else if (2 == map.size()) {
key = map.get("key");
value = map.get("value");
}
final ConverterRegistry convert = ConverterRegistry.getInstance();
return (Map.Entry<?, ?>) ReflectUtil.newInstance(TypeUtil.getClass(targetType),
TypeUtil.isUnknown(keyType) ? key : convert.convert(keyType, key),
TypeUtil.isUnknown(valueType) ? value : convert.convert(valueType, value)
);
}
}

View File

@ -0,0 +1,115 @@
package cn.hutool.core.convert.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.AbstractConverter;
import cn.hutool.core.convert.ConvertException;
import cn.hutool.core.convert.ConverterRegistry;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.TypeUtil;
import java.lang.reflect.Type;
import java.util.Map;
public class PairConverter extends AbstractConverter<Pair<?, ?>> {
/** Pair类型 */
private final Type pairType;
/** 键类型 */
private final Type keyType;
/** 值类型 */
private final Type valueType;
/**
* 构造Pair的key和value泛型类型自动获取
*
* @param pairType Map类型
*/
public PairConverter(Type pairType) {
this(pairType, null, null);
}
/**
* 构造
*
* @param pairType Pair类型
* @param keyType 键类型
* @param valueType 值类型
*/
public PairConverter(Type pairType, Type keyType, Type valueType) {
this.pairType = pairType;
this.keyType = keyType;
this.valueType = valueType;
}
@SuppressWarnings("rawtypes")
@Override
protected Pair<?, ?> convertInternal(Object value) {
Map map = null;
if (value instanceof Map.Entry) {
final Map.Entry entry = (Map.Entry) value;
map = MapUtil.of(entry.getKey(), entry.getValue());
}else if (value instanceof Map) {
map = (Map) value;
} else if (value instanceof CharSequence) {
final CharSequence str = (CharSequence) value;
map = strToMap(str);
} else if (BeanUtil.isReadableBean(value.getClass())) {
map = BeanUtil.beanToMap(value);
}
if (null != map) {
return mapToPair(pairType, keyType, valueType, map);
}
throw new ConvertException("Unsupported to map from [{}] of type: {}", value, value.getClass().getName());
}
/**
* 字符串转单个键值对的Map支持分隔符{@code :}{@code =}{@code ,}
*
* @param str 字符串
* @return map or null
*/
private static Map<CharSequence, CharSequence> strToMap(final CharSequence str) {
// key:value key=value key,value
final int index = StrUtil.indexOf(str, '=', 0, str.length());
if (index > -1) {
return MapUtil.of(str.subSequence(0, index + 1), str.subSequence(index, str.length()));
}
return null;
}
/**
* Map转Entry
*
* @param targetType 目标的Map类型
* @param keyType 键类型
* @param valueType 值类型
* @param map 被转换的map
* @return Entry
*/
@SuppressWarnings("rawtypes")
private static Pair<?, ?> mapToPair(final Type targetType, final Type keyType, final Type valueType, final Map map) {
Object key = null;
Object value = null;
if (1 == map.size()) {
final Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
key = entry.getKey();
value = entry.getValue();
} else if (2 == map.size()) {
key = map.get("key");
value = map.get("value");
}
final ConverterRegistry convert = ConverterRegistry.getInstance();
return (Pair<?, ?>) ReflectUtil.newInstance(TypeUtil.getClass(targetType),
TypeUtil.isUnknown(keyType) ? key : convert.convert(keyType, key),
TypeUtil.isUnknown(valueType) ? value : convert.convert(valueType, value)
);
}
}

View File

@ -17,6 +17,7 @@ import cn.hutool.json.serialize.JSONDeserializer;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
/**
* JSON转换器
@ -122,7 +123,11 @@ public class JSONConverter implements Converter<JSON> {
// issue#2212@Github
// 在JSONObject转Bean时读取JSONObject本身的配置文件
if(value instanceof JSONGetter
&& targetType instanceof Class && BeanUtil.hasSetter((Class<?>) targetType)){
&& targetType instanceof Class
// Map.Entry特殊处理
&& (false == Map.Entry.class.isAssignableFrom((Class<?>)targetType)
&& BeanUtil.hasSetter((Class<?>) targetType))){
final JSONConfig config = ((JSONGetter<?>) value).getConfig();
final Converter<T> converter = new BeanConverter<>(targetType,
InternalJSONUtil.toCopyOptions(config).setIgnoreError(ignoreError));

View File

@ -0,0 +1,30 @@
package cn.hutool.json;
import cn.hutool.core.lang.Pair;
import org.junit.Assert;
import org.junit.Test;
import java.util.AbstractMap;
import java.util.Map;
public class IssueI6SZYBTest {
@Test
public void pairTest() {
Pair<Integer,Integer> pair = Pair.of(1, 2);
String jsonStr = JSONUtil.toJsonStr(pair);
Assert.assertEquals("{\"key\":1,\"value\":2}", jsonStr);
final Pair bean = JSONUtil.toBean(jsonStr, Pair.class);
Assert.assertEquals(pair, bean);
}
@Test
public void entryTest() {
Map.Entry<String,Integer> pair = new AbstractMap.SimpleEntry<>("1", 2);
String jsonStr = JSONUtil.toJsonStr(pair);
Assert.assertEquals("{\"1\":2}", jsonStr);
final Map.Entry bean = JSONUtil.toBean(jsonStr, AbstractMap.SimpleEntry.class);
Assert.assertEquals(pair, bean);
}
}