diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java index 808c7f1d2..ce8e3e58c 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java @@ -10,12 +10,7 @@ import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.CharUtil; import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; /** * Bean路径表达式,用于获取多层嵌套Bean中的字段值或Bean对象
@@ -119,11 +114,14 @@ public class BeanPath implements Serializable { public void set(final Object bean, final Object value) { Objects.requireNonNull(bean); - Object subBean = bean, previousBean; + Object subBean = bean; + Object previousBean = null; boolean isFirst = true; String patternPart; // 尝试找到倒数第二个子对象, 最终需要设置它的字段值 final int length = patternParts.size() - 1; + + // 填充父字段缺失的对象 for (int i = 0; i < length; i++) { patternPart = patternParts.get(i); // 保存当前操作的bean, 以便subBean不存在时, 可以用来填充缺失的子对象 @@ -145,8 +143,13 @@ public class BeanPath implements Serializable { } } } - // 设置最终的字段值 - BeanUtil.setFieldValue(subBean, patternParts.get(length), value); + + // 设置最终的(当前)字段值 + final Object newSubBean = BeanUtil.setFieldValue(subBean, patternParts.get(length), value); + if(newSubBean != subBean && null != previousBean){ + // 对象变更,重新加入 + BeanUtil.setFieldValue(previousBean, patternParts.get(length - 1), newSubBean); + } } @Override @@ -166,7 +169,7 @@ public class BeanPath implements Serializable { private Object get(final List patternParts, final Object bean) { Object subBean = bean; boolean isFirst = true; - for (String patternPart : patternParts) { + for (final String patternPart : patternParts) { subBean = getFieldValue(subBean, patternPart); if (null == subBean) { // 支持表达式的第一个对象为Bean本身(若用户定义表达式$开头,则不做此操作) diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java index 2a6dedea5..6a3b29ae7 100755 --- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java @@ -118,7 +118,7 @@ public class BeanUtil { if (method.getParameterCount() == 0) { final String name = method.getName(); if (name.startsWith("get") || name.startsWith("is")) { - if(false == "getClass".equals(name)){ + if (false == "getClass".equals(name)) { return true; } } @@ -311,24 +311,32 @@ public class BeanUtil { /** * 设置字段值,通过反射设置字段值,并不调用setXXX方法
- * 对象同样支持Map类型,fieldNameOrIndex即为key + * 对象同样支持Map类型,fieldNameOrIndex即为key,支持: + * * * @param bean Bean * @param fieldNameOrIndex 字段名或序号,序号支持负数 * @param value 值 + * @return bean,当为数组时,返回一个新的数组 */ @SuppressWarnings({"unchecked", "rawtypes"}) - public static void setFieldValue(final Object bean, final String fieldNameOrIndex, final Object value) { + public static Object setFieldValue(final Object bean, final String fieldNameOrIndex, final Object value) { if (bean instanceof Map) { ((Map) bean).put(fieldNameOrIndex, value); } else if (bean instanceof List) { ListUtil.setOrPadding((List) bean, Convert.toInt(fieldNameOrIndex), value); } else if (ArrayUtil.isArray(bean)) { - ArrayUtil.setOrAppend(bean, Convert.toInt(fieldNameOrIndex), value); + // issue#3008,追加产生新数组,此处返回新数组 + return ArrayUtil.setOrAppend(bean, Convert.toInt(fieldNameOrIndex), value); } else { // 普通Bean对象 FieldUtil.setFieldValue(bean, fieldNameOrIndex, value); } + return bean; } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java index c87f806da..26645b862 100755 --- a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java @@ -701,7 +701,7 @@ public class IoUtil extends NioUtil { * @return {@link InputStream} * @since 4.0.9 */ - public static InputStream toMarkSupportStream(final InputStream in) { + public static InputStream toMarkSupport(final InputStream in) { if (null == in) { return null; } @@ -711,6 +711,23 @@ public class IoUtil extends NioUtil { return in; } + /** + * 将{@link Reader}转换为支持mark标记的Reader
+ * 若原Reader支持mark标记,则返回原Reader,否则使用{@link BufferedReader} 包装之 + * + * @param reader {@link Reader} + * @return {@link Reader} + */ + public static Reader toMarkSupport(final Reader reader) { + if (null == reader) { + return null; + } + if (false == reader.markSupported()) { + return new BufferedReader(reader); + } + return reader; + } + /** * 获得{@link PushbackReader}
* 如果是{@link PushbackReader}强转返回,否则新建 diff --git a/hutool-core/src/main/java/cn/hutool/core/io/ReaderWrapper.java b/hutool-core/src/main/java/cn/hutool/core/io/ReaderWrapper.java index f196d41b9..4c279b4c4 100755 --- a/hutool-core/src/main/java/cn/hutool/core/io/ReaderWrapper.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/ReaderWrapper.java @@ -53,6 +53,31 @@ public class ReaderWrapper extends Reader implements Wrapper { return raw.read(buffer, off, len); } + @Override + public boolean markSupported() { + return this.raw.markSupported(); + } + + @Override + public void mark(final int readAheadLimit) throws IOException { + this.raw.mark(readAheadLimit); + } + + @Override + public long skip(final long n) throws IOException { + return this.raw.skip(n); + } + + @Override + public boolean ready() throws IOException { + return this.raw.ready(); + } + + @Override + public void reset() throws IOException { + this.raw.reset(); + } + @Override public void close() throws IOException { raw.close(); diff --git a/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java b/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java index c48be93b4..480bbc0c5 100755 --- a/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java @@ -624,7 +624,8 @@ public class NetUtil { } /** - * 获取主机名称,一次获取会缓存名称 + * 获取主机名称,一次获取会缓存名称
+ * 注意此方法会触发反向DNS解析,导致阻塞,阻塞时间取决于网络! * * @return 主机名称 * @since 5.4.4 diff --git a/hutool-core/src/main/java/cn/hutool/core/reflect/FieldUtil.java b/hutool-core/src/main/java/cn/hutool/core/reflect/FieldUtil.java index 9257c7e69..4da535792 100644 --- a/hutool-core/src/main/java/cn/hutool/core/reflect/FieldUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/reflect/FieldUtil.java @@ -266,16 +266,17 @@ public class FieldUtil { } /** - * 设置字段值 + * 设置字段值,如果值类型必须与字段类型匹配,会自动转换对象类型 * * @param obj 对象,如果是static字段,此参数为null * @param field 字段 - * @param value 值,值类型必须与字段类型匹配,不会自动转换对象类型 + * @param value 值,类型不匹配会自动转换对象类型 * @throws UtilException UtilException 包装IllegalAccessException异常 */ public static void setFieldValue(final Object obj, final Field field, Object value) throws UtilException { Assert.notNull(field, "Field in [{}] not exist !", obj); + // 值类型检查和转换 final Class fieldType = field.getType(); if (null != value) { if (false == fieldType.isAssignableFrom(value.getClass())) { @@ -290,6 +291,18 @@ public class FieldUtil { value = ClassUtil.getDefaultValue(fieldType); } + setFieldValueExact(obj, field, value); + } + + /** + * 设置字段值,传入的字段值必须和字段类型一致,否则抛出异常 + * + * @param obj 对象,如果是static字段,此参数为null + * @param field 字段 + * @param value 值,值类型必须与字段类型匹配 + * @throws UtilException UtilException 包装IllegalAccessException异常 + */ + public static void setFieldValueExact(final Object obj, final Field field, final Object value) throws UtilException { ReflectUtil.setAccessible(field); try { field.set(obj instanceof Class ? null : obj, value); diff --git a/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java b/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java index e28a14541..807069988 100644 --- a/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java @@ -3,6 +3,8 @@ package cn.hutool.core.bean; import cn.hutool.core.lang.test.bean.ExamInfoDict; import cn.hutool.core.lang.test.bean.UserInfoDict; import cn.hutool.core.map.Dict; +import cn.hutool.core.util.ArrayUtil; +import lombok.Data; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -152,4 +154,36 @@ public class BeanPathTest { BeanPath.of("aa.bb").set(dict, "BB"); Assert.assertEquals("{aa={bb=BB}}", dict.toString()); } + + @Test + public void appendArrayTest(){ + // issue#3008@Github + final MyUser myUser = new MyUser(); + BeanPath.of("hobby[0]").set(myUser, "LOL"); + BeanPath.of("hobby[1]").set(myUser, "KFC"); + BeanPath.of("hobby[2]").set(myUser, "COFFE"); + + Assert.assertEquals("[LOL, KFC, COFFE]", ArrayUtil.toString(myUser.getHobby())); + } + + @Test + public void appendArrayTest2(){ + // issue#3008@Github + final MyUser2 myUser = new MyUser2(); + BeanPath.of("myUser.hobby[0]").set(myUser, "LOL"); + BeanPath.of("myUser.hobby[1]").set(myUser, "KFC"); + BeanPath.of("myUser.hobby[2]").set(myUser, "COFFE"); + + Assert.assertEquals("[LOL, KFC, COFFE]", ArrayUtil.toString(myUser.getMyUser().getHobby())); + } + + @Data + static class MyUser { + private String[] hobby; + } + + @Data + static class MyUser2 { + private MyUser myUser; + } } diff --git a/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java index fceb10d56..f7b2bf917 100755 --- a/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java @@ -78,10 +78,17 @@ public class NetUtilTest { } @Test + @Ignore public void getLocalHostNameTest() { + // 注意此方法会触发反向DNS解析,导致阻塞,阻塞时间取决于网络! Assert.assertNotNull(NetUtil.getLocalHostName()); } + @Test + public void getLocalHostTest() { + Assert.assertNotNull(NetUtil.getLocalhost()); + } + @Test public void pingTest(){ Assert.assertTrue(NetUtil.ping("127.0.0.1")); diff --git a/hutool-extra/src/main/java/cn/hutool/extra/compress/CompressUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/compress/CompressUtil.java index 6f3e49b07..0a62e95d4 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/compress/CompressUtil.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/compress/CompressUtil.java @@ -74,7 +74,7 @@ public class CompressUtil { * @return {@link CompressorOutputStream} */ public static CompressorInputStream getIn(String compressorName, InputStream in) { - in = IoUtil.toMarkSupportStream(in); + in = IoUtil.toMarkSupport(in); try { if(StrUtil.isBlank(compressorName)){ compressorName = CompressorStreamFactory.detect(in); diff --git a/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java b/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java index 71a5b2693..5c8bb7ce2 100755 --- a/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java +++ b/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java @@ -21,9 +21,7 @@ import cn.hutool.json.serialize.JSONStringer; import cn.hutool.json.writer.GlobalValueWriterMapping; import cn.hutool.json.writer.JSONValueWriter; -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; +import java.io.*; import java.lang.reflect.Type; import java.math.BigDecimal; import java.sql.SQLException; @@ -316,7 +314,7 @@ public final class InternalJSONUtil { */ public static Writer quote(final String str, final Writer writer, final boolean isWrap) throws IORuntimeException { try { - return doQuote(str, writer, isWrap); + return _quote(str, writer, isWrap); } catch (final IOException e) { throw new IORuntimeException(e); } @@ -422,7 +420,7 @@ public final class InternalJSONUtil { * @throws IOException IO异常 * @since 3.3.1 */ - private static Writer doQuote(final String str, final Writer writer, final boolean isWrap) throws IOException { + private static Writer _quote(final String str, final Writer writer, final boolean isWrap) throws IOException { if (StrUtil.isEmpty(str)) { if (isWrap) { writer.write("\"\""); diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONArray.java b/hutool-json/src/main/java/cn/hutool/json/JSONArray.java index 5b54acf21..7ea21621d 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONArray.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONArray.java @@ -8,7 +8,7 @@ import cn.hutool.core.lang.mutable.MutableEntry; import cn.hutool.core.lang.mutable.MutableObj; import cn.hutool.core.text.StrJoiner; import cn.hutool.core.util.ObjUtil; -import cn.hutool.json.mapper.ArrayMapper; +import cn.hutool.json.mapper.JSONArrayMapper; import cn.hutool.json.writer.JSONWriter; import java.io.StringWriter; @@ -148,7 +148,7 @@ public class JSONArray implements JSON, JSONGetter, List, Rando */ public JSONArray(final Object object, final JSONConfig jsonConfig, final Predicate> predicate) throws JSONException { this(DEFAULT_CAPACITY, jsonConfig); - ArrayMapper.of(object, predicate).mapTo(this); + JSONArrayMapper.of(object, predicate).mapTo(this); } // endregion diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java index f6f97a37d..8449f8631 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java @@ -9,7 +9,7 @@ import cn.hutool.core.lang.mutable.MutableEntry; import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapWrapper; import cn.hutool.core.util.ObjUtil; -import cn.hutool.json.mapper.ObjectMapper; +import cn.hutool.json.mapper.JSONObjectMapper; import cn.hutool.json.writer.JSONWriter; import java.io.StringWriter; @@ -132,7 +132,7 @@ public class JSONObject extends MapWrapper implements JSON, JSON */ public JSONObject(final Object source, final JSONConfig config, final Predicate> predicate) { this(DEFAULT_CAPACITY, config); - ObjectMapper.of(source, predicate).mapTo(this); + JSONObjectMapper.of(source, predicate).mapTo(this); } // -------------------------------------------------------------------------------------------------------------------- Constructor end diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONTokener.java b/hutool-json/src/main/java/cn/hutool/json/JSONTokener.java index 5955f59ae..7ec7b1fe1 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONTokener.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONTokener.java @@ -1,9 +1,9 @@ package cn.hutool.json; import cn.hutool.core.io.IoUtil; +import cn.hutool.core.io.ReaderWrapper; import cn.hutool.core.text.StrUtil; -import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.Reader; @@ -14,7 +14,7 @@ import java.io.StringReader; * * @author from JSON.org */ -public class JSONTokener { +public class JSONTokener extends ReaderWrapper { private long character; /** @@ -37,10 +37,6 @@ public class JSONTokener { * 是否使用前一个字符 */ private boolean usePrevious; - /** - * 源 - */ - private final Reader reader; /** * JSON配置 @@ -48,24 +44,6 @@ public class JSONTokener { private final JSONConfig config; // ------------------------------------------------------------------------------------ Constructor start - - /** - * 从Reader中构建 - * - * @param reader Reader - * @param config JSON配置 - */ - public JSONTokener(final Reader reader, final JSONConfig config) { - this.reader = reader.markSupported() ? reader : new BufferedReader(reader); - this.eof = false; - this.usePrevious = false; - this.previous = 0; - this.index = 0; - this.character = 1; - this.line = 1; - this.config = config; - } - /** * 从InputStream中构建,使用UTF-8编码 * @@ -86,6 +64,23 @@ public class JSONTokener { public JSONTokener(final CharSequence s, final JSONConfig config) { this(new StringReader(StrUtil.str(s)), config); } + + /** + * 从Reader中构建 + * + * @param reader Reader + * @param config JSON配置 + */ + public JSONTokener(final Reader reader, final JSONConfig config) { + super(IoUtil.toMarkSupport(reader)); + this.eof = false; + this.usePrevious = false; + this.previous = 0; + this.index = 0; + this.character = 1; + this.line = 1; + this.config = config; + } // ------------------------------------------------------------------------------------ Constructor end /** @@ -138,7 +133,7 @@ public class JSONTokener { c = this.previous; } else { try { - c = this.reader.read(); + c = read(); } catch (final IOException exception) { throw new JSONException(exception); } @@ -390,11 +385,11 @@ public class JSONTokener { final long startIndex = this.index; final long startCharacter = this.character; final long startLine = this.line; - this.reader.mark(1000000); + mark(1000000); do { c = this.next(); if (c == 0) { - this.reader.reset(); + reset(); this.index = startIndex; this.character = startCharacter; this.line = startLine; diff --git a/hutool-json/src/main/java/cn/hutool/json/convert/JSONConverter.java b/hutool-json/src/main/java/cn/hutool/json/convert/JSONConverter.java index 5792475b9..74684d0dc 100644 --- a/hutool-json/src/main/java/cn/hutool/json/convert/JSONConverter.java +++ b/hutool-json/src/main/java/cn/hutool/json/convert/JSONConverter.java @@ -6,18 +6,14 @@ import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.ConvertException; import cn.hutool.core.convert.Converter; import cn.hutool.core.convert.RegisterConverter; -import cn.hutool.core.convert.impl.ArrayConverter; -import cn.hutool.core.convert.impl.CollectionConverter; -import cn.hutool.core.convert.impl.DateConverter; -import cn.hutool.core.convert.impl.MapConverter; -import cn.hutool.core.convert.impl.TemporalAccessorConverter; +import cn.hutool.core.convert.impl.*; import cn.hutool.core.map.MapWrapper; -import cn.hutool.core.math.NumberUtil; import cn.hutool.core.reflect.ConstructorUtil; import cn.hutool.core.reflect.TypeReference; import cn.hutool.core.reflect.TypeUtil; import cn.hutool.core.text.StrUtil; import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjUtil; import cn.hutool.json.*; import cn.hutool.json.serialize.JSONDeserializer; import cn.hutool.json.serialize.JSONStringer; @@ -111,36 +107,45 @@ public class JSONConverter implements Converter { /** * 实现Object对象转换为{@link JSON},支持的对象: *
    - *
  • String: 转换为相应的对象,
  • + *
  • String: 转换为相应的对象,"和'包围的字符串返回原字符串,""返回{@code null}
  • *
  • Array、Iterable、Iterator:转换为JSONArray
  • *
  • Bean对象:转为JSONObject
  • *
  • Number:返回原对象
  • + *
  • null:返回{@code null}
  • *
* * @param obj 被转换的对象 * @return 转换后的对象 * @throws JSONException 转换异常 */ + @SuppressWarnings("resource") public Object toJSON(final Object obj) throws JSONException { + if(null == obj){ + return null; + } final JSON json; if (obj instanceof JSON || obj instanceof Number) { return obj; } else if (obj instanceof CharSequence) { final String jsonStr = StrUtil.trim((CharSequence) obj); - switch (jsonStr.charAt(0)){ - case '"': - case '\'': - // JSON字符串值 - return jsonStr; + if(jsonStr.isEmpty()){ + // 空按照null值处理 + return null; + } + final char firstC = jsonStr.charAt(0); + switch (firstC){ case '[': return new JSONArray(jsonStr, config); case '{': return new JSONObject(jsonStr, config); default: - if(NumberUtil.isNumber(jsonStr)){ - return NumberUtil.toBigDecimal(jsonStr); + // RFC8259,JSON字符串值、number, boolean, or null + final Object value = new JSONTokener(jsonStr, config).nextValue(); + if(ObjUtil.equals(value, jsonStr)){ + // 原值返回,意味着非正常数字、Boolean或null + throw new JSONException("Unsupported JSON String: {}", jsonStr); } - throw new JSONException("Unsupported String to JSON: {}", jsonStr); + return value; } } else if (obj instanceof MapWrapper) { // MapWrapper实现了Iterable会被当作JSONArray,此处做修正 diff --git a/hutool-json/src/main/java/cn/hutool/json/mapper/ArrayMapper.java b/hutool-json/src/main/java/cn/hutool/json/mapper/JSONArrayMapper.java similarity index 87% rename from hutool-json/src/main/java/cn/hutool/json/mapper/ArrayMapper.java rename to hutool-json/src/main/java/cn/hutool/json/mapper/JSONArrayMapper.java index c4dd10ec0..3ae805640 100644 --- a/hutool-json/src/main/java/cn/hutool/json/mapper/ArrayMapper.java +++ b/hutool-json/src/main/java/cn/hutool/json/mapper/JSONArrayMapper.java @@ -32,7 +32,7 @@ import java.util.function.Predicate; * @author looly * @since 6.0.0 */ -public class ArrayMapper { +public class JSONArrayMapper { /** * 创建ArrayMapper * @@ -40,8 +40,8 @@ public class ArrayMapper { * @param predicate 值过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤,,{@link Predicate#test(Object)}为{@code true}保留 * @return ObjectMapper */ - public static ArrayMapper of(final Object source, final Predicate> predicate) { - return new ArrayMapper(source, predicate); + public static JSONArrayMapper of(final Object source, final Predicate> predicate) { + return new JSONArrayMapper(source, predicate); } private final Object source; @@ -53,7 +53,7 @@ public class ArrayMapper { * @param source 来源对象 * @param predicate 值过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null}表示不过滤,,{@link Predicate#test(Object)}为{@code true}保留 */ - public ArrayMapper(final Object source, final Predicate> predicate) { + public JSONArrayMapper(final Object source, final Predicate> predicate) { this.source = source; this.predicate = predicate; } @@ -78,7 +78,9 @@ public class ArrayMapper { return; } - if (source instanceof CharSequence) { + if (source instanceof JSONTokener) { + mapFromTokener((JSONTokener) source, jsonArray); + }else if (source instanceof CharSequence) { // JSON字符串 mapFromStr((CharSequence) source, jsonArray); } else if (source instanceof Reader) { @@ -96,8 +98,6 @@ public class ArrayMapper { jsonArray.add(b); } } - } else if (source instanceof JSONTokener) { - mapFromTokener((JSONTokener) source, jsonArray); } else { final Iterator iter; if (ArrayUtil.isArray(source)) {// 数组 @@ -107,7 +107,11 @@ public class ArrayMapper { } else if (source instanceof Iterable) {// Iterable iter = ((Iterable) source).iterator(); } else { - throw new JSONException("JSONArray initial value should be a string or collection or array."); + if(false == jsonArray.getConfig().isIgnoreError()){ + throw new JSONException("JSONArray initial value should be a string or collection or array."); + } + // 如果用户选择跳过异常,则跳过此值转换 + return; } Object next; diff --git a/hutool-json/src/main/java/cn/hutool/json/mapper/ObjectMapper.java b/hutool-json/src/main/java/cn/hutool/json/mapper/JSONObjectMapper.java similarity index 89% rename from hutool-json/src/main/java/cn/hutool/json/mapper/ObjectMapper.java rename to hutool-json/src/main/java/cn/hutool/json/mapper/JSONObjectMapper.java index 284f8ece5..e4aa1669b 100644 --- a/hutool-json/src/main/java/cn/hutool/json/mapper/ObjectMapper.java +++ b/hutool-json/src/main/java/cn/hutool/json/mapper/JSONObjectMapper.java @@ -39,7 +39,7 @@ import java.util.function.Predicate; * @author looly * @since 5.8.0 */ -public class ObjectMapper { +public class JSONObjectMapper { /** * 创建ObjectMapper @@ -48,8 +48,8 @@ public class ObjectMapper { * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 * @return ObjectMapper */ - public static ObjectMapper of(final Object source, final Predicate> predicate) { - return new ObjectMapper(source, predicate); + public static JSONObjectMapper of(final Object source, final Predicate> predicate) { + return new JSONObjectMapper(source, predicate); } private final Object source; @@ -61,7 +61,7 @@ public class ObjectMapper { * @param source 来源对象 * @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留 */ - public ObjectMapper(final Object source, final Predicate> predicate) { + public JSONObjectMapper(final Object source, final Predicate> predicate) { this.source = source; this.predicate = predicate; } @@ -90,7 +90,10 @@ public class ObjectMapper { throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass()); } - if (source instanceof Map) { + if (source instanceof JSONTokener) { + // JSONTokener + mapFromTokener((JSONTokener) source, jsonObject); + }else if (source instanceof Map) { // Map for (final Map.Entry e : ((Map) source).entrySet()) { jsonObject.set(Convert.toStr(e.getKey()), e.getValue(), predicate, false); @@ -107,18 +110,18 @@ public class ObjectMapper { mapFromTokener(new JSONTokener((InputStream) source, jsonObject.getConfig()), jsonObject); } else if (source instanceof byte[]) { mapFromTokener(new JSONTokener(IoUtil.toStream((byte[]) source), jsonObject.getConfig()), jsonObject); - } else if (source instanceof JSONTokener) { - // JSONTokener - mapFromTokener((JSONTokener) source, jsonObject); } else if (source instanceof ResourceBundle) { - // JSONTokener + // ResourceBundle mapFromResourceBundle((ResourceBundle) source, jsonObject); } else if (BeanUtil.isReadableBean(source.getClass())) { // 普通Bean mapFromBean(source, jsonObject); } else { - // 不支持对象类型转换为JSONObject - throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass()); + if(false == jsonObject.getConfig().isIgnoreError()){ + // 不支持对象类型转换为JSONObject + throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass()); + } + // 如果用户选择跳过异常,则跳过此值转换 } } diff --git a/hutool-json/src/test/java/cn/hutool/json/IssueI6LBZATest.java b/hutool-json/src/test/java/cn/hutool/json/IssueI6LBZATest.java index 161ec6f7f..ae343a13b 100755 --- a/hutool-json/src/test/java/cn/hutool/json/IssueI6LBZATest.java +++ b/hutool-json/src/test/java/cn/hutool/json/IssueI6LBZATest.java @@ -3,8 +3,6 @@ package cn.hutool.json; import org.junit.Assert; import org.junit.Test; -import java.math.BigDecimal; - public class IssueI6LBZATest { @Test public void parseJSONStringTest() { @@ -31,6 +29,6 @@ public class IssueI6LBZATest { public void parseJSONNumberTest() { final String a = "123"; final Object parse = JSONUtil.parse(a); - Assert.assertEquals(BigDecimal.class, parse.getClass()); + Assert.assertEquals(Integer.class, parse.getClass()); } } diff --git a/hutool-json/src/test/java/cn/hutool/json/JSONTokenerTest.java b/hutool-json/src/test/java/cn/hutool/json/JSONTokenerTest.java new file mode 100644 index 000000000..e543a11ad --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/JSONTokenerTest.java @@ -0,0 +1,13 @@ +package cn.hutool.json; + +import cn.hutool.core.io.resource.ResourceUtil; +import org.junit.Assert; +import org.junit.Test; + +public class JSONTokenerTest { + @Test + public void parseTest() { + final JSONObject jsonObject = JSONUtil.parseObj(ResourceUtil.getUtf8Reader("issue1200.json")); + Assert.assertNotNull(jsonObject); + } +} diff --git a/hutool-json/src/test/java/cn/hutool/json/JSONUtilTest.java b/hutool-json/src/test/java/cn/hutool/json/JSONUtilTest.java index d0e52df3b..758a82a2a 100644 --- a/hutool-json/src/test/java/cn/hutool/json/JSONUtilTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/JSONUtilTest.java @@ -17,6 +17,42 @@ import java.util.*; public class JSONUtilTest { + @Test(expected = JSONException.class) + public void parseInvalid() { + JSONUtil.parse("abc"); + } + + @Test(expected = JSONException.class) + public void parseInvalid2() { + JSONUtil.parse("'abc"); + } + + @Test(expected = JSONException.class) + public void parseInvalid3() { + JSONUtil.parse("\"abc"); + } + + @Test + public void parseValueTest() { + Object parse = JSONUtil.parse(123); + Assert.assertEquals(123, parse); + + parse = JSONUtil.parse("\"abc\""); + Assert.assertEquals("abc", parse); + + parse = JSONUtil.parse("true"); + Assert.assertEquals(true, parse); + + parse = JSONUtil.parse("False"); + Assert.assertEquals(false, parse); + + parse = JSONUtil.parse("null"); + Assert.assertNull(parse); + + parse = JSONUtil.parse(""); + Assert.assertNull(parse); + } + /** * 出现语法错误时报错,检查解析\x字符时是否会导致死循环异常 */ @@ -29,20 +65,39 @@ public class JSONUtilTest { * 数字解析为JSONArray报错 */ @Test(expected = JSONException.class) - public void parseNumberTest() { + public void parseNumberToJSONArrayTest() { final JSONArray json = JSONUtil.parseArray(123L); Assert.assertNotNull(json); } + /** + * 数字解析为JSONArray报错 + */ + @Test + public void parseNumberToJSONArrayTest2() { + final JSONArray json = JSONUtil.parseArray(123L, + JSONConfig.of().setIgnoreError(true)); + Assert.assertNotNull(json); + } + /** * 数字解析为JSONArray报错 */ @Test(expected = JSONException.class) - public void parseNumberTest2() { + public void parseNumberToJSONObjectTest() { final JSONObject json = JSONUtil.parseObj(123L); Assert.assertNotNull(json); } + /** + * 数字解析为JSONObject,忽略错误 + */ + @Test + public void parseNumberToJSONObjectTest2() { + final JSONObject json = JSONUtil.parseObj(123L, JSONConfig.of().setIgnoreError(true)); + Assert.assertEquals(new JSONObject(), json); + } + @Test public void toJsonStrTest() { final UserA a1 = new UserA(); diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelUtil.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelUtil.java index 22a59c968..63f88901b 100755 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelUtil.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelUtil.java @@ -93,7 +93,7 @@ public class ExcelUtil { * @since 3.2.0 */ public static void readBySax(InputStream in, final int rid, final RowHandler rowHandler) { - in = IoUtil.toMarkSupportStream(in); + in = IoUtil.toMarkSupport(in); final ExcelSaxReader reader = ExcelSaxUtil.createSaxReader(ExcelFileUtil.isXlsx(in), rowHandler); reader.read(in, rid); } @@ -107,7 +107,7 @@ public class ExcelUtil { * @since 5.4.4 */ public static void readBySax(InputStream in, final String idOrRidOrSheetName, final RowHandler rowHandler) { - in = IoUtil.toMarkSupportStream(in); + in = IoUtil.toMarkSupport(in); final ExcelSaxReader reader = ExcelSaxUtil.createSaxReader(ExcelFileUtil.isXlsx(in), rowHandler); reader.read(in, idOrRidOrSheetName); } diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/WorkbookUtil.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/WorkbookUtil.java index 093b3bb26..0f743b46f 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/WorkbookUtil.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/WorkbookUtil.java @@ -143,7 +143,7 @@ public class WorkbookUtil { */ public static Workbook createBook(final InputStream in, final String password) { try { - return WorkbookFactory.create(IoUtil.toMarkSupportStream(in), password); + return WorkbookFactory.create(IoUtil.toMarkSupport(in), password); } catch (final Exception e) { throw new POIException(e); } finally { diff --git a/hutool-setting/src/main/java/cn/hutool/setting/dialect/Props.java b/hutool-setting/src/main/java/cn/hutool/setting/dialect/Props.java index f251cdc09..678f74a10 100644 --- a/hutool-setting/src/main/java/cn/hutool/setting/dialect/Props.java +++ b/hutool-setting/src/main/java/cn/hutool/setting/dialect/Props.java @@ -348,7 +348,7 @@ public final class Props extends Properties implements TypeGetter BeanUtil.setProperty(bean, StrUtil.subSuf(key, prefix.length()), entry.getValue()); } catch (final Exception e) { // 忽略注入失败的字段(这些字段可能用于其它配置) - StaticLog.debug("Ignore property: [{}]", key); + StaticLog.debug("Ignore property: [{}],because of: {}", key, e); } } diff --git a/hutool-setting/src/test/java/cn/hutool/setting/Issue3008Test.java b/hutool-setting/src/test/java/cn/hutool/setting/Issue3008Test.java new file mode 100644 index 000000000..b5c229342 --- /dev/null +++ b/hutool-setting/src/test/java/cn/hutool/setting/Issue3008Test.java @@ -0,0 +1,26 @@ +package cn.hutool.setting; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.setting.dialect.Props; +import cn.hutool.setting.dialect.PropsUtil; +import lombok.Data; +import org.junit.Assert; +import org.junit.Test; + +public class Issue3008Test { + /** + * 数组字段追加后生成新的数组,造成赋值丢失
+ * 修复见:BeanUtil.setFieldValue + */ + @Test + public void toBeanTest() { + final Props props = PropsUtil.get("issue3008"); + final MyUser user = props.toBean(MyUser.class, "person"); + Assert.assertEquals("[LOL, KFC, COFFE]", ArrayUtil.toString(user.getHobby())); + } + + @Data + static class MyUser { + private String[] hobby; + } +} diff --git a/hutool-setting/src/test/java/cn/hutool/setting/PropsTest.java b/hutool-setting/src/test/java/cn/hutool/setting/PropsTest.java index 0cbac36bb..7cade096c 100644 --- a/hutool-setting/src/test/java/cn/hutool/setting/PropsTest.java +++ b/hutool-setting/src/test/java/cn/hutool/setting/PropsTest.java @@ -112,4 +112,6 @@ public class PropsTest { private Date theStageTime;//当前阶段开始日期 private Date nextStageTime;//当前阶段结束日期/下一阶段开始日期 } + + } diff --git a/hutool-setting/src/test/resources/issue3008.properties b/hutool-setting/src/test/resources/issue3008.properties new file mode 100644 index 000000000..d6b627c15 --- /dev/null +++ b/hutool-setting/src/test/resources/issue3008.properties @@ -0,0 +1,3 @@ +person.hobby[0]=LOL +person.hobby[1]=KFC +person.hobby[2]=COFFE