diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java index b74f0c3a2..bab0b19c5 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java @@ -77,11 +77,14 @@ public class BeanUtil { * @see #hasPublicField(Class) */ public static boolean isReadableBean(final Class> clazz) { + if(null == clazz){ + return false; + } return hasGetter(clazz) || hasPublicField(clazz); } /** - * 判断是否为Bean对象,判定方法是: + * 判断是否为可写Bean对象,判定方法是: * *
* 1、是否存在只有一个参数的setXXX方法 @@ -93,7 +96,10 @@ public class BeanUtil { * @see #hasSetter(Class) * @see #hasPublicField(Class) */ - public static boolean isBean(final Class> clazz) { + public static boolean isWritableBean(final Class> clazz) { + if(null == clazz){ + return false; + } return hasSetter(clazz) || hasPublicField(clazz); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/MapToBeanCopier.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/MapToBeanCopier.java index ce1243f94..e50193f46 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/MapToBeanCopier.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/MapToBeanCopier.java @@ -15,6 +15,7 @@ package org.dromara.hutool.core.bean.copier; import org.dromara.hutool.core.bean.BeanUtil; import org.dromara.hutool.core.bean.PropDesc; import org.dromara.hutool.core.lang.Assert; +import org.dromara.hutool.core.lang.Console; import org.dromara.hutool.core.lang.mutable.MutableEntry; import org.dromara.hutool.core.map.CaseInsensitiveMap; import org.dromara.hutool.core.map.MapWrapper; diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java index 5c4f4358d..dc92bc867 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java @@ -15,6 +15,7 @@ package org.dromara.hutool.core.collection; import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.bean.BeanUtil; import org.dromara.hutool.core.codec.hash.Hash32; +import org.dromara.hutool.core.collection.iter.ArrayIter; import org.dromara.hutool.core.collection.iter.IterUtil; import org.dromara.hutool.core.collection.iter.IteratorEnumeration; import org.dromara.hutool.core.collection.queue.BoundedPriorityQueue; @@ -1553,6 +1554,10 @@ public class CollUtil { // String按照逗号分隔的列表对待 final String arrayStr = StrUtil.unWrap((CharSequence) value, '[', ']'); iter = SplitUtil.splitTrim(arrayStr, StrUtil.COMMA).iterator(); + } else if(value instanceof Map && BeanUtil.isWritableBean(TypeUtil.getClass(elementType))){ + //https://github.com/dromara/hutool/issues/3139 + // 如果值为Map,而目标为一个Bean,则Map应整体转换为Bean,而非拆分成Entry转换 + iter = new ArrayIter<>(new Object[]{value}); } else { iter = IterUtil.getIter(value); } @@ -2032,7 +2037,7 @@ public class CollUtil { @Override public int hash32(final T t) { - if (null == t || !BeanUtil.isBean(t.getClass())) { + if (null == t || !BeanUtil.isWritableBean(t.getClass())) { // 非Bean放在同一子分组中 return 0; } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java index 73689c5b4..e95d3251e 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java @@ -148,7 +148,7 @@ public class CompositeConverter extends RegisterConverter { } // 尝试转Bean - if (BeanUtil.isBean(rowType)) { + if (BeanUtil.isWritableBean(rowType)) { return (T) BeanConverter.INSTANCE.convert(type, value); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/BeanConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/BeanConverter.java index 65112f1a8..db9b1d4f9 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/BeanConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/BeanConverter.java @@ -23,7 +23,6 @@ import org.dromara.hutool.core.lang.Assert; import org.dromara.hutool.core.map.MapProxy; import org.dromara.hutool.core.reflect.ConstructorUtil; import org.dromara.hutool.core.reflect.TypeUtil; -import org.dromara.hutool.core.reflect.kotlin.KClassUtil; import java.io.Serializable; import java.lang.reflect.Type; @@ -87,7 +86,7 @@ public class BeanConverter implements Converter, Serializable { private Object convertInternal(final Type targetType, final Class> targetClass, final Object value) { if (value instanceof Map || value instanceof ValueProvider || - BeanUtil.isBean(value.getClass())) { + BeanUtil.isWritableBean(value.getClass())) { if (value instanceof Map && targetClass.isInterface()) { // 将Map动态代理为Bean return MapProxy.of((Map, ?>) value).toProxyBean(targetClass); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java index 68bebf473..a0799818f 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java @@ -77,7 +77,7 @@ public class EntryConverter implements Converter { } else if (value instanceof CharSequence) { final CharSequence str = (CharSequence) value; map = strToMap(str); - } else if (BeanUtil.isBean(value.getClass())) { + } else if (BeanUtil.isWritableBean(value.getClass())) { map = BeanUtil.beanToMap(value); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/KBeanConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/KBeanConverter.java index 1ca2c1c99..bafd2ca6a 100755 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/KBeanConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/KBeanConverter.java @@ -69,7 +69,7 @@ public class KBeanConverter implements Converter, Serializable { valueProvider = (ValueProvider) value; } else if(value instanceof Map){ valueProvider = new MapValueProvider((Map ) value); - } else if(BeanUtil.isBean(value.getClass())){ + } else if(BeanUtil.isWritableBean(value.getClass())){ valueProvider = new BeanValueProvider(value); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/MapConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/MapConverter.java index 690cf1a39..0867852ae 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/MapConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/MapConverter.java @@ -84,7 +84,7 @@ public class MapConverter implements Converter, Serializable { map = MapUtil.createMap(TypeUtil.getClass(targetType), LinkedHashMap::new); convertMapToMap(keyType, valueType, (Map) value, map); - } else if (BeanUtil.isBean(value.getClass())) { + } else if (BeanUtil.isWritableBean(value.getClass())) { map = BeanUtil.beanToMap(value); // 二次转换,转换键值类型 map = convert(targetType, keyType, valueType, map); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/RecordConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/RecordConverter.java index 06521d465..bd9db80ca 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/RecordConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/RecordConverter.java @@ -46,7 +46,7 @@ public class RecordConverter extends AbstractConverter { valueProvider = (ValueProvider ) value; } else if (value instanceof Map) { valueProvider = new MapValueProvider((Map ) value); - } else if (BeanUtil.isBean(value.getClass())) { + } else if (BeanUtil.isWritableBean(value.getClass())) { valueProvider = new BeanValueProvider(value); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileUtil.java index 81f2791e5..04d7356cc 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileUtil.java @@ -23,6 +23,7 @@ import org.dromara.hutool.core.io.resource.ResourceUtil; import org.dromara.hutool.core.io.stream.BOMInputStream; import org.dromara.hutool.core.io.unit.DataSizeUtil; import org.dromara.hutool.core.lang.Assert; +import org.dromara.hutool.core.lang.Console; import org.dromara.hutool.core.net.url.URLUtil; import org.dromara.hutool.core.reflect.ClassUtil; import org.dromara.hutool.core.regex.ReUtil; @@ -142,6 +143,7 @@ public class FileUtil extends PathUtil { } // region ----- loop and walk + /** * 递归遍历目录以及子目录中的所有文件
* 如果提供file为文件,直接返回过滤结果 @@ -273,6 +275,7 @@ public class FileUtil extends PathUtil { // endregion // region ----- file and newFile + /** * 创建File对象,相当于调用new File(),不做任何处理 * @@ -419,6 +422,7 @@ public class FileUtil extends PathUtil { } // region ----- exists + /** * 判断文件是否存在,如果path为null,则返回false * @@ -468,6 +472,7 @@ public class FileUtil extends PathUtil { // endregion // region ----- lastModifiedTime + /** * 指定文件最后修改时间 * @@ -590,6 +595,7 @@ public class FileUtil extends PathUtil { } // region ----- touch + /** * 创建文件及其父目录,如果这个文件存在,直接返回这个文件
* 此方法不对File对象类型做判断,如果File不存在,无法判断其类型 @@ -803,6 +809,7 @@ public class FileUtil extends PathUtil { } // region ----- createTempFile + /** * 创建临时文件
* 创建后的文件名为 prefix[Random].tmp @@ -939,10 +946,10 @@ public class FileUtil extends PathUtil { Assert.notNull(src, "Src file must be not null!"); Assert.notNull(target, "target file must be not null!"); return copy( - src.toPath(), - target.toPath(), - isOverride ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[]{}) - .toFile(); + src.toPath(), + target.toPath(), + isOverride ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[]{}) + .toFile(); } /** @@ -965,10 +972,10 @@ public class FileUtil extends PathUtil { Assert.notNull(src, "Src file must be not null!"); Assert.notNull(target, "target file must be not null!"); return copyContent( - src.toPath(), - target.toPath(), - isOverride ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[]{}) - .toFile(); + src.toPath(), + target.toPath(), + isOverride ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[]{}) + .toFile(); } /** @@ -1210,8 +1217,8 @@ public class FileUtil extends PathUtil { if (!file1.exists() || !file2.exists()) { // 两个文件都不存在判断其路径是否相同, 对于一个存在一个不存在的情况,一定不相同 return !file1.exists()// - && !file2.exists()// - && pathEquals(file1, file2); + && !file2.exists()// + && pathEquals(file1, file2); } return equals(file1.toPath(), file2.toPath()); } @@ -1501,6 +1508,7 @@ public class FileUtil extends PathUtil { } // region ----- in + /** * 获得输入流 * @@ -1598,6 +1606,7 @@ public class FileUtil extends PathUtil { // endregion // region ----- read + /** * 读取文件所有数据
* 文件的长度不能超过Integer.MAX_VALUE @@ -2101,6 +2110,7 @@ public class FileUtil extends PathUtil { // endregion // region ----- write and append + /** * 将String写入文件,覆盖模式,字符集为UTF-8 * @@ -2638,13 +2648,25 @@ public class FileUtil extends PathUtil { */ public static File checkSlip(final File parentFile, final File file) throws IllegalArgumentException { if (null != parentFile && null != file) { - if(!file.toPath().startsWith(parentFile.toPath())){ + if (!startsWith(parentFile, file)) { throw new IllegalArgumentException("New file is outside of the parent dir: " + file.getName()); } } return file; } + /** + * 检查父文件是否为文件真正的父目录 + * + * @param parentFile 父目录 + * @param file 文件 + * @return 是否为文件真正的父目录 + */ + public static boolean startsWith(final File parentFile, final File file) { + return PathUtil.toAbsNormal(parentFile.toPath()) + .startsWith(PathUtil.toAbsNormal(file.toPath())); + } + /** * 根据文件扩展名获得MimeType * @@ -2664,7 +2686,7 @@ public class FileUtil extends PathUtil { * @since 4.1.15 */ public static String getMimeType(final String filePath) { - if(StrUtil.isBlank(filePath)){ + if (StrUtil.isBlank(filePath)) { return null; } @@ -2790,8 +2812,8 @@ public class FileUtil extends PathUtil { // 替换Windows路径分隔符为Linux路径分隔符,便于统一处理 fileName = fileName.replace(CharUtil.BACKSLASH, CharUtil.SLASH); if (!isWindows() - // 检查文件名中是否包含"/",不考虑以"/"结尾的情况 - && fileName.lastIndexOf(CharUtil.SLASH, fileName.length() - 2) > 0) { + // 检查文件名中是否包含"/",不考虑以"/"结尾的情况 + && fileName.lastIndexOf(CharUtil.SLASH, fileName.length() - 2) > 0) { // 在Linux下多层目录创建存在问题,/会被当成文件名的一部分,此处做处理 // 使用/拆分路径(zip中无\),级联创建父目录 final ListpathParts = SplitUtil.split(fileName, StrUtil.SLASH, false, true); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java index 684307910..c9791a989 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java @@ -32,6 +32,7 @@ import java.util.List; * @since 5.4.1 */ public class PathUtil { + /** * 目录是否为空 * @@ -559,10 +560,30 @@ public class PathUtil { * @since 5.5.5 */ public static Path toAbsNormal(final Path path) { - Assert.notNull(path); + if (null == path) { + return null; + } return path.toAbsolutePath().normalize(); } + /** + * 获取实际路径,路径文件必须存在 + * + * @param path 路径 + * @return 实际路径 + * @throws IORuntimeException IO异常,如文件不存在等 + */ + public static Path toRealPath(Path path) throws IORuntimeException{ + if (null != path) { + try { + path = path.toRealPath(); + } catch (final IOException e) { + throw new IORuntimeException(e); + } + } + return path; + } + /** * 获得文件的MimeType * diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanUtilTest.java index f4a35dd40..7f718bea5 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanUtilTest.java @@ -39,7 +39,7 @@ public class BeanUtilTest { public void isBeanTest() { // HashMap不包含setXXX方法,不是bean - final boolean isBean = BeanUtil.isBean(HashMap.class); + final boolean isBean = BeanUtil.isWritableBean(HashMap.class); Assertions.assertFalse(isBean); } diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/io/FileUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/io/FileUtilTest.java index 86ff80dc8..5d4649139 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/io/FileUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/io/FileUtilTest.java @@ -24,15 +24,16 @@ import java.util.List; public class FileUtilTest { @Test - public void fileTest() { + void fileTest1() { + final File file = FileUtil.file("d:/aaa", "bbb"); + Assertions.assertNotNull(file); + } + + @Test + public void fileTest2() { Assertions.assertThrows(IllegalArgumentException.class, ()->{ - final File file = FileUtil.file("d:/aaa", "bbb"); - Assertions.assertNotNull(file); - // 构建目录中出现非子目录抛出异常 - FileUtil.file(file, "../ccc"); - - FileUtil.file("E:/"); + FileUtil.file("d:/aaa/bbb", "../ccc"); }); } diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/util/XmlUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/util/XmlUtilTest.java index 27f0f2518..cb3ea50dd 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/util/XmlUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/util/XmlUtilTest.java @@ -32,6 +32,7 @@ import org.xml.sax.helpers.DefaultHandler; import javax.xml.xpath.XPathConstants; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Set; @@ -380,4 +381,29 @@ public class XmlUtilTest { private String age; private String email; } + + @Test + public void issue3139Test() { + final String xml = " \n" + + " "; + + final R r = XmlUtil.xmlToBean(XmlUtil.parseXml(xml), R.class); + Assertions.assertEquals("1", r.getC().get(0).getS()); + Assertions.assertEquals("str", r.getC().get(0).getP()); + } + + @Data + static class C { + String s; + String p; + } + + @Data + static class R { + List\n" + + " \n" + + "1\n" + + "str
\n" + + "c; + } } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java index aaf723107..44e8a0866 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java @@ -211,7 +211,7 @@ public class JSONConverter implements Converter { } // 尝试转Bean - if (BeanUtil.isBean(rawType)) { + if (BeanUtil.isWritableBean(rawType)) { // issue#I5WDP0 对于Kotlin对象,由于参数可能非空限制,导致无法创建一个默认的对象再赋值 if(KClassUtil.isKotlinClass(rawType) && json instanceof JSONGetter){ return KClassUtil.newInstance(rawType, new JSONGetterValueProvider<>((JSONGetter )json)); diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/Issue3139Test.java b/hutool-json/src/test/java/org/dromara/hutool/json/Issue3139Test.java new file mode 100755 index 000000000..1e59a597a --- /dev/null +++ b/hutool-json/src/test/java/org/dromara/hutool/json/Issue3139Test.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.json; + +import lombok.Data; +import org.dromara.hutool.core.lang.Console; +import org.dromara.hutool.core.util.XmlUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class Issue3139Test { + @Test + public void toBeanTest() { + final String xml = " \n" + + " "; + + final JSONObject jsonObject = XmlUtil.xmlToBean(XmlUtil.parseXml(xml).getDocumentElement(), JSONObject.class); + final R bean = jsonObject.toBean(R.class); + Assertions.assertNotNull(bean); + + final List\n" + + " \n" + + "1\n" + + "str
\n" + + "c = bean.getC(); + Assertions.assertEquals(1, c.size()); + Assertions.assertEquals("1", c.get(0).getS()); + Assertions.assertEquals("str", c.get(0).getP()); + } + + @Data + static class C { + String s; + String p; + } + + @Data + static class R { + List c; + } +} diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelWriter.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelWriter.java index 6105f5b6e..34c69f68f 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelWriter.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelWriter.java @@ -991,7 +991,7 @@ public class ExcelWriter extends ExcelBase { } else if (rowBean instanceof Hyperlink) { // Hyperlink当成一个值 return writeRow(ListUtil.of(rowBean), isWriteKeyAsHead); - } else if (BeanUtil.isBean(rowBean.getClass())) { + } else if (BeanUtil.isWritableBean(rowBean.getClass())) { if (MapUtil.isEmpty(this.headerAlias)) { rowMap = BeanUtil.beanToMap(rowBean, new LinkedHashMap<>(), false, false); } else { diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/word/TableUtil.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/word/TableUtil.java index 602939075..b2448d790 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/word/TableUtil.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/word/TableUtil.java @@ -102,7 +102,7 @@ public class TableUtil { final Map rowMap; if(rowBean instanceof Map) { rowMap = (Map) rowBean; - } else if (BeanUtil.isBean(rowBean.getClass())) { + } else if (BeanUtil.isWritableBean(rowBean.getClass())) { rowMap = BeanUtil.beanToMap(rowBean, new LinkedHashMap<>(), false, false); } else { // 其它转为字符串默认输出