修复props.toBean 数组字段未赋值问题

This commit is contained in:
Looly 2023-03-26 05:53:45 +08:00
parent 8bd76de6fe
commit 5799e018c1
9 changed files with 106 additions and 62 deletions

View File

@ -23,6 +23,7 @@
* 【core 】 修复CalendarUtil.isSameMonth没有判断公元前导致不一致的问题issue#3011@Github * 【core 】 修复CalendarUtil.isSameMonth没有判断公元前导致不一致的问题issue#3011@Github
* 【core 】 修复WatchUtil createModify maxDepth传递后没有使用问题issue#3005@Github * 【core 】 修复WatchUtil createModify maxDepth传递后没有使用问题issue#3005@Github
* 【core 】 修复NullComparator反转无效问题pr#964@Gitee * 【core 】 修复NullComparator反转无效问题pr#964@Gitee
* 【setting】 修复props.toBean 数组字段未赋值问题issue#3008@Github
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
# 5.8.15 (2023-03-09) # 5.8.15 (2023-03-09)

View File

@ -10,11 +10,7 @@ import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** /**
* Bean路径表达式用于获取多层嵌套Bean中的字段值或Bean对象<br> * Bean路径表达式用于获取多层嵌套Bean中的字段值或Bean对象<br>
@ -138,18 +134,24 @@ public class BeanPath implements Serializable {
* *
* @param bean BeanMap或List * @param bean BeanMap或List
* @param patternParts 表达式块列表 * @param patternParts 表达式块列表
* @param nextNumberPart 下一个值是否
* @param value * @param value
* @return
*/ */
private void set(Object bean, List<String> patternParts, boolean nextNumberPart, Object value) { private void set(Object bean, List<String> patternParts, boolean nextNumberPart, Object value) {
Object subBean = this.get(patternParts, bean, true); Object subBean = this.get(patternParts, bean, true);
if (null == subBean) { if (null == subBean) {
// 当前节点是空则先创建父节点
final List<String> parentParts = getParentParts(patternParts); final List<String> parentParts = getParentParts(patternParts);
this.set(bean, parentParts, lastIsNumber(parentParts), nextNumberPart ? new ArrayList<>() : new HashMap<>()); this.set(bean, parentParts, lastIsNumber(parentParts), nextNumberPart ? new ArrayList<>() : new HashMap<>());
//set中有可能做过转换因此此处重新获取bean //set中有可能做过转换因此此处重新获取bean
subBean = this.get(patternParts, bean, true); subBean = this.get(patternParts, bean, true);
} }
BeanUtil.setFieldValue(subBean, patternParts.get(patternParts.size() - 1), value);
final Object newSubBean = BeanUtil.setFieldValue(subBean, patternParts.get(patternParts.size() - 1), value);
if(newSubBean != subBean){
// 对象变更重新加入
this.set(bean, getParentParts(patternParts), nextNumberPart, newSubBean);
}
} }
/** /**

View File

@ -9,29 +9,12 @@ import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Editor; import cn.hutool.core.lang.Editor;
import cn.hutool.core.map.CaseInsensitiveMap; import cn.hutool.core.map.CaseInsensitiveMap;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.*;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ModifierUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import java.beans.BeanInfo; import java.beans.*;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -309,24 +292,32 @@ public class BeanUtil {
/** /**
* 设置字段值通过反射设置字段值并不调用setXXX方法<br> * 设置字段值通过反射设置字段值并不调用setXXX方法<br>
* 对象同样支持Map类型fieldNameOrIndex即为key * 对象同样支持Map类型fieldNameOrIndex即为key支持
* <ul>
* <li>Map</li>
* <li>List</li>
* <li>Bean</li>
* </ul>
* *
* @param bean Bean * @param bean Bean
* @param fieldNameOrIndex 字段名或序号序号支持负数 * @param fieldNameOrIndex 字段名或序号序号支持负数
* @param value * @param value
* @return bean当为数组时返回一个新的数组
*/ */
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
public static void setFieldValue(Object bean, String fieldNameOrIndex, Object value) { public static Object setFieldValue(Object bean, String fieldNameOrIndex, Object value) {
if (bean instanceof Map) { if (bean instanceof Map) {
((Map) bean).put(fieldNameOrIndex, value); ((Map) bean).put(fieldNameOrIndex, value);
} else if (bean instanceof List) { } else if (bean instanceof List) {
ListUtil.setOrPadding((List) bean, Convert.toInt(fieldNameOrIndex), value); ListUtil.setOrPadding((List) bean, Convert.toInt(fieldNameOrIndex), value);
} else if (ArrayUtil.isArray(bean)) { } else if (ArrayUtil.isArray(bean)) {
ArrayUtil.setOrAppend(bean, Convert.toInt(fieldNameOrIndex), value); // issue#3008追加产生新数组此处返回新数组
return ArrayUtil.setOrAppend(bean, Convert.toInt(fieldNameOrIndex), value);
} else { } else {
// 普通Bean对象 // 普通Bean对象
ReflectUtil.setFieldValue(bean, fieldNameOrIndex, value); ReflectUtil.setFieldValue(bean, fieldNameOrIndex, value);
} }
return bean;
} }
/** /**
@ -611,6 +602,7 @@ public class BeanUtil {
} }
// --------------------------------------------------------------------------------------------- beanToMap // --------------------------------------------------------------------------------------------- beanToMap
/** /**
* 将bean的部分属性转换成map<br> * 将bean的部分属性转换成map<br>
* 可选拷贝哪些属性值默认是不忽略值为{@code null}的值的 * 可选拷贝哪些属性值默认是不忽略值为{@code null}的值的
@ -984,8 +976,8 @@ public class BeanUtil {
* @param target 待检测对象2 * @param target 待检测对象2
* @param ignoreProperties 不需要检测的字段 * @param ignoreProperties 不需要检测的字段
* @return 判断结果如果为true则证明所有字段的值都相同 * @return 判断结果如果为true则证明所有字段的值都相同
* @since 5.8.4
* @author Takak11 * @author Takak11
* @since 5.8.4
*/ */
public static boolean isCommonFieldsEqual(Object source, Object target, String... ignoreProperties) { public static boolean isCommonFieldsEqual(Object source, Object target, String... ignoreProperties) {

View File

@ -360,7 +360,8 @@ public class ArrayUtil extends PrimitiveArrayUtil {
} }
/** /**
* 将元素值设置为数组的某个位置当给定的index大于数组长度则追加 * 将元素值设置为数组的某个位置当给定的index大于数组长度则追加<br>
* 替换时返回原数组追加时返回新数组
* *
* @param array 已有数组 * @param array 已有数组
* @param index 位置大于长度追加否则替换 * @param index 位置大于长度追加否则替换

View File

@ -2,6 +2,8 @@ package cn.hutool.core.bean;
import cn.hutool.core.lang.test.bean.ExamInfoDict; import cn.hutool.core.lang.test.bean.ExamInfoDict;
import cn.hutool.core.lang.test.bean.UserInfoDict; import cn.hutool.core.lang.test.bean.UserInfoDict;
import cn.hutool.core.util.ArrayUtil;
import lombok.Data;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -124,4 +126,20 @@ public class BeanPathTest {
beanPath.set(map, "张三"); beanPath.set(map, "张三");
Assert.assertEquals("{list=[[null, {name=张三}]]}", map.toString()); Assert.assertEquals("{list=[[null, {name=张三}]]}", map.toString());
} }
@Test
public void appendArrayTest(){
// issue#3008@Github
final MyUser myUser = new MyUser();
BeanPath.create("hobby[0]").set(myUser, "LOL");
BeanPath.create("hobby[1]").set(myUser, "KFC");
BeanPath.create("hobby[2]").set(myUser, "COFFE");
Assert.assertEquals("[LOL, KFC, COFFE]", ArrayUtil.toString(myUser.getHobby()));
}
@Data
static class MyUser {
private String[] hobby;
}
} }

View File

@ -585,7 +585,7 @@ public final class Props extends Properties implements BasicTypeGetter<String>,
BeanUtil.setProperty(bean, StrUtil.subSuf(key, prefix.length()), entry.getValue()); BeanUtil.setProperty(bean, StrUtil.subSuf(key, prefix.length()), entry.getValue());
} catch (Exception e) { } catch (Exception e) {
// 忽略注入失败的字段这些字段可能用于其它配置 // 忽略注入失败的字段这些字段可能用于其它配置
StaticLog.debug("Ignore property: [{}]", key); StaticLog.debug("Ignore property: [{}],because of: {}", key, e);
} }
} }

View File

@ -0,0 +1,27 @@
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 {
/**
* 数组字段追加后生成新的数组造成赋值丢失<br>
* 修复见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;
}
}

View File

@ -31,11 +31,11 @@ public class PropsTest {
@Test @Test
public void propTest() { public void propTest() {
//noinspection MismatchedQueryAndUpdateOfCollection //noinspection MismatchedQueryAndUpdateOfCollection
Props props = new Props("test.properties"); final Props props = new Props("test.properties");
String user = props.getProperty("user"); final String user = props.getProperty("user");
Assert.assertEquals(user, "root"); Assert.assertEquals(user, "root");
String driver = props.getStr("driver"); final String driver = props.getStr("driver");
Assert.assertEquals(driver, "com.mysql.jdbc.Driver"); Assert.assertEquals(driver, "com.mysql.jdbc.Driver");
} }
@ -43,19 +43,19 @@ public class PropsTest {
@Ignore @Ignore
public void propTestForAbsPAth() { public void propTestForAbsPAth() {
//noinspection MismatchedQueryAndUpdateOfCollection //noinspection MismatchedQueryAndUpdateOfCollection
Props props = new Props("d:/test.properties"); final Props props = new Props("d:/test.properties");
String user = props.getProperty("user"); final String user = props.getProperty("user");
Assert.assertEquals(user, "root"); Assert.assertEquals(user, "root");
String driver = props.getStr("driver"); final String driver = props.getStr("driver");
Assert.assertEquals(driver, "com.mysql.jdbc.Driver"); Assert.assertEquals(driver, "com.mysql.jdbc.Driver");
} }
@Test @Test
public void toBeanTest() { public void toBeanTest() {
Props props = Props.getProp("to_bean_test.properties"); final Props props = Props.getProp("to_bean_test.properties");
ConfigProperties cfg = props.toBean(ConfigProperties.class, "mail"); final ConfigProperties cfg = props.toBean(ConfigProperties.class, "mail");
Assert.assertEquals("mailer@mail.com", cfg.getHost()); Assert.assertEquals("mailer@mail.com", cfg.getHost());
Assert.assertEquals(9000, cfg.getPort()); Assert.assertEquals(9000, cfg.getPort());
Assert.assertEquals("mailer@mail.com", cfg.getFrom()); Assert.assertEquals("mailer@mail.com", cfg.getFrom());
@ -73,14 +73,14 @@ public class PropsTest {
@Test @Test
public void toBeanWithNullPrefixTest(){ public void toBeanWithNullPrefixTest(){
Props configProp = new Props(); final Props configProp = new Props();
configProp.setProperty("createTime", Objects.requireNonNull(DateUtil.parse("2020-01-01"))); configProp.setProperty("createTime", Objects.requireNonNull(DateUtil.parse("2020-01-01")));
configProp.setProperty("isInit", true); configProp.setProperty("isInit", true);
configProp.setProperty("stairPlan", 1); configProp.setProperty("stairPlan", 1);
configProp.setProperty("stageNum", 2); configProp.setProperty("stageNum", 2);
configProp.setProperty("version", 3); configProp.setProperty("version", 3);
SystemConfig systemConfig = configProp.toBean(SystemConfig.class); final SystemConfig systemConfig = configProp.toBean(SystemConfig.class);
Assert.assertEquals(DateUtil.parse("2020-01-01"), systemConfig.getCreateTime()); Assert.assertEquals(DateUtil.parse("2020-01-01"), systemConfig.getCreateTime());
Assert.assertEquals(true, systemConfig.getIsInit()); Assert.assertEquals(true, systemConfig.getIsInit());

View File

@ -0,0 +1,3 @@
person.hobby[0]=LOL
person.hobby[1]=KFC
person.hobby[2]=COFFE