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 f4384f30c..cf883219e 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 @@ -54,109 +54,6 @@ import java.util.stream.Collectors; */ public class BeanUtil { - /** - * 判断是否为可读的Bean对象,判定方法是: - * - *
-	 *     1、是否存在只有无参数的getXXX方法或者isXXX方法
-	 *     2、是否存在public类型的字段
-	 * 
- * - * @param clazz 待测试类 - * @return 是否为可读的Bean对象 - * @see #hasGetter(Class) - * @see #hasPublicField(Class) - */ - public static boolean isReadableBean(final Class clazz) { - if (null == clazz) { - return false; - } - return hasGetter(clazz) || hasPublicField(clazz); - } - - /** - * 判断是否为可写Bean对象,判定方法是: - * - *
-	 *     1、是否存在只有一个参数的setXXX方法
-	 *     2、是否存在public类型的字段
-	 * 
- * - * @param clazz 待测试类 - * @return 是否为Bean对象 - * @see #hasSetter(Class) - * @see #hasPublicField(Class) - */ - public static boolean isWritableBean(final Class clazz) { - if (null == clazz) { - return false; - } - return hasSetter(clazz) || hasPublicField(clazz); - } - - /** - * 判断是否有Setter方法
- * 判定方法是否存在只有一个参数的setXXX方法 - * - * @param clazz 待测试类 - * @return 是否为Bean对象 - * @since 4.2.2 - */ - public static boolean hasSetter(final Class clazz) { - if (ClassUtil.isNormalClass(clazz)) { - for (final Method method : clazz.getMethods()) { - if (method.getParameterCount() == 1 && method.getName().startsWith("set")) { - // 检测包含标准的setXXX方法即视为标准的JavaBean - return true; - } - } - } - return false; - } - - /** - * 判断是否为Bean对象
- * 判定方法是否存在只有无参数的getXXX方法或者isXXX方法 - * - * @param clazz 待测试类 - * @return 是否为Bean对象 - * @since 4.2.2 - */ - public static boolean hasGetter(final Class clazz) { - if (ClassUtil.isNormalClass(clazz)) { - for (final Method method : clazz.getMethods()) { - if (method.getParameterCount() == 0) { - final String name = method.getName(); - if (name.startsWith("get") || name.startsWith("is")) { - if (!"getClass".equals(name)) { - return true; - } - } - } - } - } - return false; - } - - /** - * 指定类中是否有public类型字段(static字段除外) - * - * @param clazz 待测试类 - * @return 是否有public类型字段 - * @since 5.1.0 - */ - public static boolean hasPublicField(final Class clazz) { - if (ClassUtil.isNormalClass(clazz)) { - for (final Field field : clazz.getFields()) { - if (ModifierUtil.isPublic(field) && !ModifierUtil.isStatic(field)) { - //非static的public字段 - return true; - } - } - } - return false; - } - /** * 创建动态Bean * @@ -200,8 +97,7 @@ public class BeanUtil { getBeanDesc(clazz).getProps().forEach(action); } - // --------------------------------------------------------------------------------------------------------- PropertyDescriptor - + // region ----- getPropertyDescriptor /** * 获得Bean字段描述数组 * @@ -278,6 +174,7 @@ public class BeanUtil { final Map map = getPropertyDescriptorMap(clazz, ignoreCase); return (null == map) ? null : map.get(fieldName); } + // endregion /** * 获得字段值,通过反射直接获得字段值,并不调用getXXX方法
@@ -380,101 +277,7 @@ public class BeanUtil { BeanPath.of(expression).setValue(bean, value); } - // --------------------------------------------------------------------------------------------- mapToBean - - /** - * Map转换为Bean对象 - * - * @param Bean类型 - * @param map {@link Map} - * @param beanClass Bean Class - * @param isToCamelCase 是否将Map中的下划线风格key转换为驼峰风格 - * @param copyOptions 转Bean选项 - * @return Bean - */ - public static T mapToBean(final Map map, final Class beanClass, final boolean isToCamelCase, final CopyOptions copyOptions) { - return fillBeanWithMap(map, ConstructorUtil.newInstanceIfPossible(beanClass), isToCamelCase, copyOptions); - } - - // --------------------------------------------------------------------------------------------- fillBeanWithMap - - /** - * 使用Map填充Bean对象 - * - * @param Bean类型 - * @param map Map - * @param bean Bean - * @param isIgnoreError 是否忽略注入错误 - * @return Bean - */ - public static T fillBeanWithMap(final Map map, final T bean, final boolean isIgnoreError) { - return fillBeanWithMap(map, bean, false, isIgnoreError); - } - - /** - * 使用Map填充Bean对象,可配置将下划线转换为驼峰 - * - * @param Bean类型 - * @param map Map - * @param bean Bean - * @param isToCamelCase 是否将下划线模式转换为驼峰模式 - * @param isIgnoreError 是否忽略注入错误 - * @return Bean - */ - public static T fillBeanWithMap(final Map map, final T bean, final boolean isToCamelCase, final boolean isIgnoreError) { - return fillBeanWithMap(map, bean, isToCamelCase, CopyOptions.of().setIgnoreError(isIgnoreError)); - } - - /** - * 使用Map填充Bean对象,忽略大小写 - * - * @param Bean类型 - * @param map Map - * @param bean Bean - * @param isIgnoreError 是否忽略注入错误 - * @return Bean - */ - public static T fillBeanWithMapIgnoreCase(final Map map, final T bean, final boolean isIgnoreError) { - return fillBeanWithMap(map, bean, CopyOptions.of().setIgnoreCase(true).setIgnoreError(isIgnoreError)); - } - - /** - * 使用Map填充Bean对象 - * - * @param Bean类型 - * @param map Map - * @param bean Bean - * @param copyOptions 属性复制选项 {@link CopyOptions} - * @return Bean - */ - public static T fillBeanWithMap(final Map map, final T bean, final CopyOptions copyOptions) { - return fillBeanWithMap(map, bean, false, copyOptions); - } - - /** - * 使用Map填充Bean对象 - * - * @param Bean类型 - * @param map Map - * @param bean Bean - * @param isToCamelCase 是否将Map中的下划线风格key转换为驼峰风格 - * @param copyOptions 属性复制选项 {@link CopyOptions} - * @return Bean - * @since 3.3.1 - */ - public static T fillBeanWithMap(Map map, final T bean, final boolean isToCamelCase, final CopyOptions copyOptions) { - if (MapUtil.isEmpty(map)) { - return bean; - } - if (isToCamelCase) { - map = MapUtil.toCamelCaseMap(map); - } - copyProperties(map, bean, copyOptions); - return bean; - } - - // --------------------------------------------------------------------------------------------- fillBean - + // region ----- toBean /** * 对象或Map转Bean * @@ -520,7 +323,9 @@ public class BeanUtil { copyProperties(source, target, options); return target; } + // endregion + // region ----- fillBean /** * 填充Bean的核心方法 * @@ -537,8 +342,25 @@ public class BeanUtil { return BeanCopier.of(valueProvider, bean, copyOptions).copy(); } - // --------------------------------------------------------------------------------------------- beanToMap + /** + * 使用Map填充Bean对象 + * + * @param Bean类型 + * @param map Map + * @param bean Bean + * @param copyOptions 属性复制选项 {@link CopyOptions} + * @return Bean + */ + public static T fillBeanWithMap(final Map map, final T bean, final CopyOptions copyOptions) { + if (MapUtil.isEmpty(map)) { + return bean; + } + return copyProperties(map, bean, copyOptions); + } + // endregion + + // region ----- beanToMap /** * 将bean的部分属性转换成map
* 可选拷贝哪些属性值,默认是不忽略值为{@code null}的值的。 @@ -657,8 +479,9 @@ public class BeanUtil { return BeanCopier.of(bean, targetMap, copyOptions).copy(); } + // endregion - // --------------------------------------------------------------------------------------------- copyProperties + // region ----- copyProperties /** * 按照Bean对象属性创建对应的Class对象,并忽略某些属性 @@ -727,6 +550,20 @@ public class BeanUtil { return BeanCopier.of(source, target, ObjUtil.defaultIfNull(copyOptions, CopyOptions::of)).copy(); } + /** + * 复制集合中的Bean属性
+ * 此方法遍历集合中每个Bean,复制其属性后加入一个新的{@link List}中。 + * + * @param collection 原Bean集合 + * @param targetType 目标Bean类型 + * @param Bean类型 + * @return 复制后的List + * @since 5.6.6 + */ + public static List copyToList(final Collection collection, final Class targetType) { + return copyToList(collection, targetType, CopyOptions.of()); + } + /** * 复制集合中的Bean属性
* 此方法遍历集合中每个Bean,复制其属性后加入一个新的{@link List}中。 @@ -758,20 +595,7 @@ public class BeanUtil { return target; }).collect(Collectors.toList()); } - - /** - * 复制集合中的Bean属性
- * 此方法遍历集合中每个Bean,复制其属性后加入一个新的{@link List}中。 - * - * @param collection 原Bean集合 - * @param targetType 目标Bean类型 - * @param Bean类型 - * @return 复制后的List - * @since 5.6.6 - */ - public static List copyToList(final Collection collection, final Class targetType) { - return copyToList(collection, targetType, CopyOptions.of()); - } + // endregion /** * 给定的Bean的类名是否匹配指定类名字符串
@@ -881,6 +705,110 @@ public class BeanUtil { ); } + /** + * 判断是否为可读的Bean对象,判定方法是: + * + *
+	 *     1、是否存在只有无参数的getXXX方法或者isXXX方法
+	 *     2、是否存在public类型的字段
+	 * 
+ * + * @param clazz 待测试类 + * @return 是否为可读的Bean对象 + * @see #hasGetter(Class) + * @see #hasPublicField(Class) + */ + public static boolean isReadableBean(final Class clazz) { + if (null == clazz) { + return false; + } + return hasGetter(clazz) || hasPublicField(clazz); + } + + /** + * 判断是否为可写Bean对象,判定方法是: + * + *
+	 *     1、是否存在只有一个参数的setXXX方法
+	 *     2、是否存在public类型的字段
+	 * 
+ * + * @param clazz 待测试类 + * @return 是否为Bean对象 + * @see #hasSetter(Class) + * @see #hasPublicField(Class) + */ + public static boolean isWritableBean(final Class clazz) { + if (null == clazz) { + return false; + } + return hasSetter(clazz) || hasPublicField(clazz); + } + + // region ----- hasXXX + /** + * 判断是否有Setter方法
+ * 判定方法是否存在只有一个参数的setXXX方法 + * + * @param clazz 待测试类 + * @return 是否为Bean对象 + * @since 4.2.2 + */ + public static boolean hasSetter(final Class clazz) { + if (ClassUtil.isNormalClass(clazz)) { + for (final Method method : clazz.getMethods()) { + if (method.getParameterCount() == 1 && method.getName().startsWith("set")) { + // 检测包含标准的setXXX方法即视为标准的JavaBean + return true; + } + } + } + return false; + } + + /** + * 判断是否为Bean对象
+ * 判定方法是否存在只有无参数的getXXX方法或者isXXX方法 + * + * @param clazz 待测试类 + * @return 是否为Bean对象 + * @since 4.2.2 + */ + public static boolean hasGetter(final Class clazz) { + if (ClassUtil.isNormalClass(clazz)) { + for (final Method method : clazz.getMethods()) { + if (method.getParameterCount() == 0) { + final String name = method.getName(); + if (name.startsWith("get") || name.startsWith("is")) { + if (!"getClass".equals(name)) { + return true; + } + } + } + } + } + return false; + } + + /** + * 指定类中是否有public类型字段(static字段除外) + * + * @param clazz 待测试类 + * @return 是否有public类型字段 + * @since 5.1.0 + */ + public static boolean hasPublicField(final Class clazz) { + if (ClassUtil.isNormalClass(clazz)) { + for (final Field field : clazz.getFields()) { + if (ModifierUtil.isPublic(field) && !ModifierUtil.isStatic(field)) { + //非static的public字段 + return true; + } + } + } + return false; + } + /** * 判断Bean是否包含值为{@code null}的属性
* 对象本身为{@code null}也返回true @@ -912,6 +840,7 @@ public class BeanUtil { && StrUtil.isEmptyIfStr(FieldUtil.getFieldValue(bean, field)) ); } + // endregion /** * 检查Bean
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/BeanToBeanCopier.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/BeanToBeanCopier.java index c0800bbf6..04e286202 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/BeanToBeanCopier.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/BeanToBeanCopier.java @@ -86,7 +86,7 @@ public class BeanToBeanCopier extends AbsCopier { // 检查目标字段可写性 // 目标字段检查放在键值对编辑之后,因为键可能被编辑修改 - final PropDesc tDesc = targetPropDescMap.get(sFieldName); + final PropDesc tDesc = this.copyOptions.findPropDesc(targetPropDescMap, sFieldName); if (null == tDesc || !tDesc.isWritable(this.copyOptions.transientSupport)) { // 字段不可写,跳过之 return; diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/CopyOptions.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/CopyOptions.java index 0d2df9fe4..ad3008b22 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/CopyOptions.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/CopyOptions.java @@ -12,12 +12,14 @@ package org.dromara.hutool.core.bean.copier; +import org.dromara.hutool.core.bean.PropDesc; import org.dromara.hutool.core.convert.Convert; import org.dromara.hutool.core.convert.Converter; import org.dromara.hutool.core.func.LambdaUtil; import org.dromara.hutool.core.func.SerFunction; import org.dromara.hutool.core.lang.mutable.MutableEntry; import org.dromara.hutool.core.array.ArrayUtil; +import org.dromara.hutool.core.text.StrUtil; import java.io.Serializable; import java.lang.reflect.Field; @@ -76,11 +78,16 @@ public class CopyOptions implements Serializable { */ protected boolean override = true; + /** + * 是否自动转换为驼峰方式 + */ + protected boolean autoTransCamelCase = true; + /** * 自定义类型转换器,默认使用全局万能转换器转换 */ protected Converter converter = (type, value) -> - Convert.convertWithCheck(type, value, null, ignoreError); + Convert.convertWithCheck(type, value, null, ignoreError); //region create @@ -178,7 +185,7 @@ public class CopyOptions implements Serializable { */ public CopyOptions setIgnoreProperties(final String... ignoreProperties) { return setPropertiesFilter((field, o) -> { - if(ignoreCase){ + if (ignoreCase) { // issue#I80FP4 return !ArrayUtil.containsIgnoreCase(ignoreProperties, field.getName()); } @@ -284,7 +291,7 @@ public class CopyOptions implements Serializable { protected MutableEntry editField(final String fieldName, final Object fieldValue) { final MutableEntry entry = new MutableEntry<>(fieldName, fieldValue); return (null != this.fieldEditor) ? - this.fieldEditor.apply(entry) : entry; + this.fieldEditor.apply(entry) : entry; } /** @@ -311,6 +318,19 @@ public class CopyOptions implements Serializable { return this; } + /** + * 设置是否自动转换为驼峰方式
+ * 一般用于map转bean和bean转bean出现非驼峰格式时,在尝试转换失败的情况下,是否二次检查转为驼峰匹配 + * + * @param autoTransCamelCase 是否自动转换为驼峰方式 + * @return this + * @since 6.0.0 + */ + public CopyOptions setAutoTransCamelCase(final boolean autoTransCamelCase) { + this.autoTransCamelCase = autoTransCamelCase; + return this; + } + /** * 设置自定义类型转换器,默认使用全局万能转换器转换。 * @@ -334,7 +354,7 @@ public class CopyOptions implements Serializable { */ protected Object convertField(final Type targetType, final Object fieldValue) { return (null != this.converter) ? - this.converter.convert(targetType, fieldValue) : fieldValue; + this.converter.convert(targetType, fieldValue) : fieldValue; } /** @@ -348,4 +368,25 @@ public class CopyOptions implements Serializable { protected boolean testPropertyFilter(final Field field, final Object value) { return null == this.propertiesFilter || this.propertiesFilter.test(field, value); } + + /** + * 查找Map对应Bean的名称
+ * 尝试原名称、转驼峰名称、isXxx去掉is的名称 + * + * @param targetPropDescMap 目标bean的属性描述Map + * @param sKeyStr 键或字段名 + * @return {@link PropDesc} + */ + protected PropDesc findPropDesc(final Map targetPropDescMap, final String sKeyStr) { + PropDesc propDesc = targetPropDescMap.get(sKeyStr); + // 转驼峰尝试查找 + if (null == propDesc && this.autoTransCamelCase) { + final String camelCaseKey = StrUtil.toCamelCase(sKeyStr); + if (!StrUtil.equals(sKeyStr, camelCaseKey)) { + // 只有转换为驼峰后与原key不同才重复查询,相同说明本身就是驼峰,不需要二次查询 + propDesc = targetPropDescMap.get(camelCaseKey); + } + } + return propDesc; + } } 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 e6e9c2f49..9ffba6a5a 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 @@ -88,7 +88,7 @@ public class MapToBeanCopier extends AbsCopier, T> { // 检查目标字段可写性 // 目标字段检查放在键值对编辑之后,因为键可能被编辑修改 - final PropDesc tDesc = findPropDesc(targetPropDescMap, sFieldName); + final PropDesc tDesc = this.copyOptions.findPropDesc(targetPropDescMap, sFieldName); if (null == tDesc || !tDesc.isWritable(this.copyOptions.transientSupport)) { // 字段不可写,跳过之 return; @@ -109,24 +109,4 @@ public class MapToBeanCopier extends AbsCopier, T> { }); return this.target; } - - /** - * 查找Map对应Bean的名称
- * 尝试原名称、转驼峰名称、isXxx去掉is的名称 - * - * @param targetPropDescMap 目标bean的属性描述Map - * @param sKeyStr 键或字段名 - * @return {@link PropDesc} - */ - private PropDesc findPropDesc(final Map targetPropDescMap, String sKeyStr){ - PropDesc propDesc = targetPropDescMap.get(sKeyStr); - if(null != propDesc){ - return propDesc; - } - - // 转驼峰尝试查找 - sKeyStr = StrUtil.toCamelCase(sKeyStr); - propDesc = targetPropDescMap.get(sKeyStr); - return propDesc; - } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/map/Dict.java b/hutool-core/src/main/java/org/dromara/hutool/core/map/Dict.java index 322e4e075..aa7a41c1a 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/map/Dict.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/map/Dict.java @@ -194,7 +194,7 @@ public class Dict extends CustomKeyMap implements TypeGetter T toBean(final T bean) { - return toBean(bean, false); + return BeanUtil.fillBeanWithMap(this, bean, CopyOptions.of()); } /** @@ -206,33 +206,7 @@ public class Dict extends CustomKeyMap implements TypeGetter T toBeanIgnoreCase(final T bean) { - BeanUtil.fillBeanWithMapIgnoreCase(this, bean, false); - return bean; - } - - /** - * 转换为Bean对象 - * - * @param Bean类型 - * @param bean Bean - * @param isToCamelCase 是否转换为驼峰模式 - * @return Bean - */ - public T toBean(final T bean, final boolean isToCamelCase) { - BeanUtil.fillBeanWithMap(this, bean, isToCamelCase, false); - return bean; - } - - /** - * 转换为Bean对象,并使用驼峰法模式转换 - * - * @param Bean类型 - * @param bean Bean - * @return Bean - */ - public T toBeanWithCamelCase(final T bean) { - BeanUtil.fillBeanWithMap(this, bean, true, false); - return bean; + return BeanUtil.fillBeanWithMap(this, bean, CopyOptions.of().setIgnoreCase(true)); } /** 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 c2d41b979..792c66ea1 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 @@ -90,7 +90,8 @@ public class BeanUtilTest { .put("aGe", 12) .put("openId", "DFDFSDFWERWER") .build(); - final SubPerson person = BeanUtil.fillBeanWithMapIgnoreCase(map, new SubPerson(), false); + final SubPerson person = BeanUtil.fillBeanWithMap( + map, new SubPerson(), CopyOptions.of().setIgnoreCase(true)); Assertions.assertEquals("Joe", person.getName()); Assertions.assertEquals(12, person.getAge()); Assertions.assertEquals("DFDFSDFWERWER", person.getOpenid()); diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/bean/Issue3452Test.java b/hutool-core/src/test/java/org/dromara/hutool/core/bean/Issue3452Test.java new file mode 100644 index 000000000..c702fa94b --- /dev/null +++ b/hutool-core/src/test/java/org/dromara/hutool/core/bean/Issue3452Test.java @@ -0,0 +1,41 @@ +/* + * 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: + * https://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.core.bean; + +import lombok.Data; +import org.dromara.hutool.core.bean.copier.CopyOptions; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +public class Issue3452Test { + + @Test + void fillBeanWithMapTest() { + final Map properties = new HashMap<>(); + properties.put("name", "JohnDoe"); + properties.put("user_age", 25); + final User user = BeanUtil.fillBeanWithMap( + properties, new User(), CopyOptions.of()); + Assertions.assertEquals("JohnDoe", user.getName()); + Assertions.assertEquals(25, user.getUserAge()); + } + + @Data + static class User { + private String name; + private int userAge; + } +} diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/xml/XmlUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/xml/XmlUtilTest.java index f081003a7..15a2c32ea 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/xml/XmlUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/xml/XmlUtilTest.java @@ -14,6 +14,7 @@ package org.dromara.hutool.core.xml; import lombok.Data; import org.dromara.hutool.core.bean.BeanUtil; +import org.dromara.hutool.core.bean.copier.CopyOptions; import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.core.collection.set.SetUtil; import org.dromara.hutool.core.io.file.FileUtil; @@ -282,7 +283,7 @@ public class XmlUtilTest { // 标准方式 final Map map = XmlUtil.xmlToMap(doc.getFirstChild()); final SmsRes res = new SmsRes(); - BeanUtil.fillBeanWithMap(map, res, true); + BeanUtil.fillBeanWithMap(map, res, CopyOptions.of().setIgnoreError(true)); // toBean方式 final SmsRes res1 = XmlUtil.xmlToBean(doc.getFirstChild(), SmsRes.class);