Repository: zeromicro/go-zero
Pull request #2586 (2022-11-10 12:58:28)

fix: inherit issue when parent after inherits


Patch:
diff --git a/core/mapping/valuer.go b/core/mapping/valuer.go
index e22c877f..8df2cc09 100644
--- a/core/mapping/valuer.go
+++ b/core/mapping/valuer.go
@@ -70,21 +70,33 @@ func (rv recursiveValuer) Value(key string) (interface{}, bool) {
 		return nil, false
 	}
 
-	if vm, ok := val.(map[string]interface{}); ok {
-		if parent := rv.Parent(); parent != nil {
-			pv, pok := parent.Value(key)
-			if pok {
-				if pm, ok := pv.(map[string]interface{}); ok {
-					for k, v := range vm {
-						pm[k] = v
-					}
-					return pm, true
-				}
-			}
+	vm, ok := val.(map[string]interface{})
+	if !ok {
+		return val, true
+	}
+
+	parent := rv.Parent()
+	if parent == nil {
+		return val, true
+	}
+
+	pv, ok := parent.Value(key)
+	if !ok {
+		return val, true
+	}
+
+	pm, ok := pv.(map[string]interface{})
+	if !ok {
+		return val, true
+	}
+
+	for k, v := range pm {
+		if _, ok := vm[k]; !ok {
+			vm[k] = v
 		}
 	}
 
-	return val, true
+	return vm, true
 }
 
 // Parent get the parent valuer from rv.


Test patch:
diff --git a/core/mapping/unmarshaler_test.go b/core/mapping/unmarshaler_test.go
index e4fd78fd..9619d5da 100644
--- a/core/mapping/unmarshaler_test.go
+++ b/core/mapping/unmarshaler_test.go
@@ -2991,6 +2991,97 @@ func TestUnmarshaler_InheritFromGrandparent(t *testing.T) {
 	assert.Equal(t, "localhost:8080", s.Middle.Value.Discovery)
 }
 
+func TestUnmarshaler_InheritSequence(t *testing.T) {
+	var testConf = []byte(`
+Nacos:
+  NamespaceId: "123"
+RpcConf:
+  Nacos:
+    NamespaceId: "456"
+  Name: hello
+`)
+
+	type (
+		NacosConf struct {
+			NamespaceId string
+		}
+
+		RpcConf struct {
+			Nacos NacosConf `json:",inherit"`
+			Name  string
+		}
+
+		Config1 struct {
+			RpcConf RpcConf
+			Nacos   NacosConf
+		}
+
+		Config2 struct {
+			RpcConf RpcConf
+			Nacos   NacosConf
+		}
+	)
+
+	var c1 Config1
+	assert.NoError(t, UnmarshalYamlBytes(testConf, &c1))
+	assert.Equal(t, "123", c1.Nacos.NamespaceId)
+	assert.Equal(t, "456", c1.RpcConf.Nacos.NamespaceId)
+
+	var c2 Config2
+	assert.NoError(t, UnmarshalYamlBytes(testConf, &c2))
+	assert.Equal(t, "123", c1.Nacos.NamespaceId)
+	assert.Equal(t, "456", c1.RpcConf.Nacos.NamespaceId)
+}
+
+func TestUnmarshaler_InheritNested(t *testing.T) {
+	var testConf = []byte(`
+Nacos:
+  Value1: "123"
+Server:
+  Nacos:
+    Value2: "456"
+  Rpc:
+    Nacos:
+      Value3: "789"
+    Name: hello
+`)
+
+	type (
+		NacosConf struct {
+			Value1 string `json:",optional"`
+			Value2 string `json:",optional"`
+			Value3 string `json:",optional"`
+		}
+
+		RpcConf struct {
+			Nacos NacosConf `json:",inherit"`
+			Name  string
+		}
+
+		ServerConf struct {
+			Nacos NacosConf `json:",inherit"`
+			Rpc   RpcConf
+		}
+
+		Config struct {
+			Server ServerConf
+			Nacos  NacosConf
+		}
+	)
+
+	var c Config
+	assert.NoError(t, UnmarshalYamlBytes(testConf, &c))
+	assert.Equal(t, "123", c.Nacos.Value1)
+	assert.Empty(t, c.Nacos.Value2)
+	assert.Empty(t, c.Nacos.Value3)
+	assert.Equal(t, "123", c.Server.Nacos.Value1)
+	assert.Equal(t, "456", c.Server.Nacos.Value2)
+	assert.Empty(t, c.Nacos.Value3)
+	assert.Equal(t, "123", c.Server.Rpc.Nacos.Value1)
+	assert.Equal(t, "456", c.Server.Rpc.Nacos.Value2)
+	assert.Equal(t, "789", c.Server.Rpc.Nacos.Value3)
+}
+
 func TestUnmarshalValuer(t *testing.T) {
 	unmarshaler := NewUnmarshaler(jsonTagKey)
 	var foo string