Skip to content

Commit 7a12098

Browse files
authored
Merge pull request #351 from zy84338719/fix/yaml-array-parsing
fix: support []interface{} in GetStringSliceValue and GetIntSliceValue for YAML arrays
2 parents a289c41 + fd123ec commit 7a12098

4 files changed

Lines changed: 283 additions & 30 deletions

File tree

storage/repository.go

Lines changed: 123 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,26 @@ func (c *Config) GetStringSliceValueImmediately(key string, defaultValue []strin
167167
return defaultValue
168168
}
169169

170-
v, ok := value.([]string)
171-
if !ok {
172-
log.Debugf("convert to []string fail ! source type:%T", value)
173-
return defaultValue
170+
// 尝试直接转换为 []string
171+
if v, ok := value.([]string); ok {
172+
return v
174173
}
175-
return v
174+
175+
// 尝试从 []interface{} 转换为 []string (YAML 数组解析后的类型)
176+
if ifaceSlice, ok := value.([]interface{}); ok {
177+
result := make([]string, 0, len(ifaceSlice))
178+
for _, item := range ifaceSlice {
179+
if s, ok := item.(string); ok {
180+
result = append(result, s)
181+
} else {
182+
result = append(result, fmt.Sprintf("%v", item))
183+
}
184+
}
185+
return result
186+
}
187+
188+
log.Debugf("convert to []string fail ! source type:%T", value)
189+
return defaultValue
176190
}
177191

178192
// GetIntSliceValueImmediately 获取配置值([]int),立即返回,初始化未完成直接返回错误
@@ -182,12 +196,43 @@ func (c *Config) GetIntSliceValueImmediately(key string, defaultValue []int) []i
182196
return defaultValue
183197
}
184198

185-
v, ok := value.([]int)
186-
if !ok {
187-
log.Debugf("convert to []int fail ! source type:%T", value)
188-
return defaultValue
199+
// 尝试直接转换为 []int
200+
if v, ok := value.([]int); ok {
201+
return v
189202
}
190-
return v
203+
204+
// 尝试从 []interface{} 转换为 []int (YAML 数组解析后的类型)
205+
if ifaceSlice, ok := value.([]interface{}); ok {
206+
result := make([]int, 0, len(ifaceSlice))
207+
for _, item := range ifaceSlice {
208+
switch v := item.(type) {
209+
case int:
210+
result = append(result, v)
211+
case float64:
212+
// JSON/YAML 数字默认解析为 float64
213+
// 验证值是否为整数(无小数部分)
214+
if v != float64(int(v)) {
215+
log.Debugf("convert to []int fail! float64 value has fractional part: %v", v)
216+
return defaultValue
217+
}
218+
result = append(result, int(v))
219+
case string:
220+
if i, err := strconv.Atoi(v); err == nil {
221+
result = append(result, i)
222+
} else {
223+
log.Debugf("convert to []int fail! value:%s, source type:%T", v, v)
224+
return defaultValue
225+
}
226+
default:
227+
log.Debugf("convert to []int fail! value:%v, source type:%T", item, item)
228+
return defaultValue
229+
}
230+
}
231+
return result
232+
}
233+
234+
log.Debugf("convert to []int fail ! source type:%T", value)
235+
return defaultValue
191236
}
192237

193238
// GetSliceValueImmediately 获取配置值([]interface),立即返回,初始化未完成直接返回错误
@@ -318,16 +363,31 @@ func (c *Config) GetStringSliceValue(key, separator string, defaultValue []strin
318363
return defaultValue
319364
}
320365

321-
v, ok := value.([]string)
322-
if !ok {
323-
s, ok := value.(string)
324-
if !ok {
325-
log.Debugf("convert to []string fail ! source type:%T", value)
326-
return defaultValue
366+
// 尝试直接转换为 []string
367+
if v, ok := value.([]string); ok {
368+
return v
369+
}
370+
371+
// 尝试从 []interface{} 转换为 []string (YAML 数组解析后的类型)
372+
if ifaceSlice, ok := value.([]interface{}); ok {
373+
result := make([]string, 0, len(ifaceSlice))
374+
for _, item := range ifaceSlice {
375+
if s, ok := item.(string); ok {
376+
result = append(result, s)
377+
} else {
378+
result = append(result, fmt.Sprintf("%v", item))
379+
}
327380
}
381+
return result
382+
}
383+
384+
// 尝试从字符串分割
385+
if s, ok := value.(string); ok {
328386
return strings.Split(s, separator)
329387
}
330-
return v
388+
389+
log.Debugf("convert to []string fail ! source type:%T", value)
390+
return defaultValue
331391
}
332392

333393
// GetIntSliceValue 获取配置值([]int)
@@ -337,23 +397,56 @@ func (c *Config) GetIntSliceValue(key, separator string, defaultValue []int) []i
337397
return defaultValue
338398
}
339399

340-
v, ok := value.([]int)
341-
if !ok {
342-
sl := c.GetStringSliceValue(key, separator, nil)
343-
if sl == nil {
344-
return defaultValue
345-
}
346-
v = make([]int, 0, len(sl))
347-
for index := range sl {
348-
i, err := strconv.Atoi(sl[index])
349-
if err != nil {
350-
log.Debugf("convert to []int fail! value:%s, source type:%T", sl[index], sl[index])
400+
// 尝试直接转换为 []int
401+
if v, ok := value.([]int); ok {
402+
return v
403+
}
404+
405+
// 尝试从 []interface{} 转换为 []int (YAML 数组解析后的类型)
406+
if ifaceSlice, ok := value.([]interface{}); ok {
407+
result := make([]int, 0, len(ifaceSlice))
408+
for _, item := range ifaceSlice {
409+
switch v := item.(type) {
410+
case int:
411+
result = append(result, v)
412+
case float64:
413+
// JSON/YAML 数字默认解析为 float64
414+
// 验证值是否为整数(无小数部分)
415+
if v != float64(int(v)) {
416+
log.Debugf("convert to []int fail! float64 value has fractional part: %v", v)
417+
return defaultValue
418+
}
419+
result = append(result, int(v))
420+
case string:
421+
if i, err := strconv.Atoi(v); err == nil {
422+
result = append(result, i)
423+
} else {
424+
log.Debugf("convert to []int fail! value:%s, source type:%T", v, v)
425+
return defaultValue
426+
}
427+
default:
428+
log.Debugf("convert to []int fail! value:%v, source type:%T", item, item)
351429
return defaultValue
352430
}
353-
v = append(v, i)
354431
}
432+
return result
355433
}
356-
return v
434+
435+
// 尝试从字符串分割后转换
436+
sl := c.GetStringSliceValue(key, separator, nil)
437+
if sl == nil {
438+
return defaultValue
439+
}
440+
result := make([]int, 0, len(sl))
441+
for index := range sl {
442+
i, err := strconv.Atoi(sl[index])
443+
if err != nil {
444+
log.Debugf("convert to []int fail! value:%s, source type:%T", sl[index], sl[index])
445+
return defaultValue
446+
}
447+
result = append(result, i)
448+
}
449+
return result
357450
}
358451

359452
// GetSliceValue 获取配置值([]interface)

storage/repository_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,3 +327,85 @@ func TestGetConfigImmediately(t *testing.T) {
327327
sliceInter := config.GetSliceValueImmediately("sliceInter", []interface{}{})
328328
Assert(t, sliceInter, Equal([]interface{}{1, "2", 3}))
329329
}
330+
331+
// TestGetStringSliceValueFromInterfaceSlice 测试从 []interface{} 转换为 []string (YAML 数组场景)
332+
func TestGetStringSliceValueFromInterfaceSlice(t *testing.T) {
333+
configurations := make(map[string]interface{})
334+
// 模拟 YAML 解析后的数组类型
335+
configurations["items"] = []interface{}{"test111", "test222"}
336+
configurations["mixed"] = []interface{}{1, "two", 3.0}
337+
configurations["nested.items"] = []interface{}{"a", "b", "c"}
338+
339+
c := creatTestApolloConfig(configurations, "test")
340+
config := c.GetConfig("test")
341+
Assert(t, config, NotNilVal())
342+
343+
// 测试纯字符串数组
344+
slice := config.GetStringSliceValue("items", ",", []string{})
345+
Assert(t, slice, Equal([]string{"test111", "test222"}))
346+
347+
// 测试混合类型数组(非字符串元素会被转为字符串)
348+
slice = config.GetStringSliceValue("mixed", ",", []string{})
349+
Assert(t, slice, Equal([]string{"1", "two", "3"}))
350+
351+
// 测试嵌套 key
352+
slice = config.GetStringSliceValue("nested.items", ",", []string{})
353+
Assert(t, slice, Equal([]string{"a", "b", "c"}))
354+
355+
// Immediately 版本测试
356+
slice = config.GetStringSliceValueImmediately("items", []string{})
357+
Assert(t, slice, Equal([]string{"test111", "test222"}))
358+
359+
slice = config.GetStringSliceValueImmediately("mixed", []string{})
360+
Assert(t, slice, Equal([]string{"1", "two", "3"}))
361+
}
362+
363+
// TestGetIntSliceValueFromInterfaceSlice 测试从 []interface{} 转换为 []int (YAML 数组场景)
364+
func TestGetIntSliceValueFromInterfaceSlice(t *testing.T) {
365+
configurations := make(map[string]interface{})
366+
// 模拟 YAML 解析后的数组类型(数字会被解析为 float64)
367+
configurations["numbers"] = []interface{}{float64(1), float64(2), float64(3)}
368+
configurations["mixed"] = []interface{}{1, 2, 3}
369+
configurations["stringNumbers"] = []interface{}{"4", "5", "6"}
370+
// 测试小数值(应该返回 defaultValue)
371+
configurations["fractionalNumbers"] = []interface{}{float64(1.5), float64(2), float64(3)}
372+
configurations["oneFractionalNumber"] = []interface{}{1, float64(2.9), 3}
373+
374+
c := creatTestApolloConfig(configurations, "test")
375+
config := c.GetConfig("test")
376+
Assert(t, config, NotNilVal())
377+
378+
// 测试 float64 数组(YAML 数字默认类型)
379+
slice := config.GetIntSliceValue("numbers", ",", []int{})
380+
Assert(t, slice, Equal([]int{1, 2, 3}))
381+
382+
// 测试 int 数组
383+
slice = config.GetIntSliceValue("mixed", ",", []int{})
384+
Assert(t, slice, Equal([]int{1, 2, 3}))
385+
386+
// 测试字符串数字数组
387+
slice = config.GetIntSliceValue("stringNumbers", ",", []int{})
388+
Assert(t, slice, Equal([]int{4, 5, 6}))
389+
390+
// 测试包含小数的数组(应该返回 defaultValue)
391+
slice = config.GetIntSliceValue("fractionalNumbers", ",", []int{99})
392+
Assert(t, slice, Equal([]int{99}))
393+
394+
// 测试包含一个小数的数组(应该返回 defaultValue)
395+
slice = config.GetIntSliceValue("oneFractionalNumber", ",", []int{88})
396+
Assert(t, slice, Equal([]int{88}))
397+
398+
// Immediately 版本测试
399+
slice = config.GetIntSliceValueImmediately("numbers", []int{})
400+
Assert(t, slice, Equal([]int{1, 2, 3}))
401+
402+
slice = config.GetIntSliceValueImmediately("mixed", []int{})
403+
Assert(t, slice, Equal([]int{1, 2, 3}))
404+
405+
slice = config.GetIntSliceValueImmediately("stringNumbers", []int{})
406+
Assert(t, slice, Equal([]int{4, 5, 6}))
407+
408+
// 测试包含小数的数组(Immediately 版本)
409+
slice = config.GetIntSliceValueImmediately("fractionalNumbers", []int{77})
410+
Assert(t, slice, Equal([]int{77}))
411+
}

utils/parse/yaml/parser_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,42 @@ func TestYAMLParserOnException(t *testing.T) {
6060
m := convertToMap(nil)
6161
Assert(t, m, NilVal())
6262
}
63+
64+
// TestYAMLParserArray 测试 YAML 数组解析
65+
func TestYAMLParserArray(t *testing.T) {
66+
s, err := yamlParser.Parse(`
67+
items:
68+
- test111
69+
- test222
70+
numbers:
71+
- 1
72+
- 2
73+
- 3
74+
nested:
75+
items:
76+
- a
77+
- b
78+
- c
79+
`)
80+
Assert(t, err, NilVal())
81+
82+
// 验证字符串数组被解析为 []interface{}
83+
items, ok := s["items"].([]interface{})
84+
Assert(t, ok, Equal(true))
85+
Assert(t, len(items), Equal(2))
86+
Assert(t, items[0], Equal("test111"))
87+
Assert(t, items[1], Equal("test222"))
88+
89+
// 验证数字数组被解析为 []interface{}
90+
numbers, ok := s["numbers"].([]interface{})
91+
Assert(t, ok, Equal(true))
92+
Assert(t, len(numbers), Equal(3))
93+
94+
// 验证嵌套数组
95+
nestedItems, ok := s["nested.items"].([]interface{})
96+
Assert(t, ok, Equal(true))
97+
Assert(t, len(nestedItems), Equal(3))
98+
Assert(t, nestedItems[0], Equal("a"))
99+
Assert(t, nestedItems[1], Equal("b"))
100+
Assert(t, nestedItems[2], Equal("c"))
101+
}

utils/parse/yml/parser_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,42 @@ func TestYMLParserOnException(t *testing.T) {
5959
m := convertToMap(nil)
6060
Assert(t, m, NilVal())
6161
}
62+
63+
// TestYMLParserArray 测试 YAML 数组解析
64+
func TestYMLParserArray(t *testing.T) {
65+
s, err := ymlParser.Parse(`
66+
items:
67+
- test111
68+
- test222
69+
numbers:
70+
- 1
71+
- 2
72+
- 3
73+
nested:
74+
items:
75+
- a
76+
- b
77+
- c
78+
`)
79+
Assert(t, err, NilVal())
80+
81+
// 验证字符串数组被解析为 []interface{}
82+
items, ok := s["items"].([]interface{})
83+
Assert(t, ok, Equal(true))
84+
Assert(t, len(items), Equal(2))
85+
Assert(t, items[0], Equal("test111"))
86+
Assert(t, items[1], Equal("test222"))
87+
88+
// 验证数字数组被解析为 []interface{}
89+
numbers, ok := s["numbers"].([]interface{})
90+
Assert(t, ok, Equal(true))
91+
Assert(t, len(numbers), Equal(3))
92+
93+
// 验证嵌套数组
94+
nestedItems, ok := s["nested.items"].([]interface{})
95+
Assert(t, ok, Equal(true))
96+
Assert(t, len(nestedItems), Equal(3))
97+
Assert(t, nestedItems[0], Equal("a"))
98+
Assert(t, nestedItems[1], Equal("b"))
99+
Assert(t, nestedItems[2], Equal("c"))
100+
}

0 commit comments

Comments
 (0)