Golang中如何遍历并更新一个结构体到另一个结构体

Golang中如何遍历并更新一个结构体到另一个结构体 我有一个使用场景,需要从一个结构体(通过反序列化JSON文件获得)中获取值。然后,基本上想用这个结构体的值来遍历另一个结构体(该结构体包含一个空模板——同样通过反序列化获得)。我尝试查阅了网上的文章,但帮助不大。任何指点都将不胜感激。以下是供您参考的代码。

顺便说一下,我能够按预期获取所需的值。我只需要一种方法来获取一个结构体的值,遍历原始结构体并更新其值。

# 下面的结构体返回一个字段为空的模板文件——我想用下面 TemplateTwo 结构体的值来更新它
type Template struct {
	General struct {
		ShowName string                        `json:”showName"`
		Fields      map[string]XYZField `json:"fields"`
	} `json:"xyz"`
}

type XYZField struct {
	ShowName  string   `json:”showName"`
	Required     bool     `json:"required"`
}

type FirstOne struct {
	Xyz TemplateXyz  `json:”xyz”`
}

type showName struct {
	firstName    string `json:”firstName" binding:"required,excludesall=!@#$%^&*()<>?+{}[]"`
	Address string `json:"Address" binding:"required,excludesall=!@#$%^&*()<>?+{}[]"`
}

# 下面的第二个结构体返回一个可以填充字段的模板——我需要找到一种方法,获取下面这个结构体的值,并将其循环赋值给上面的 Template 结构体,这样如果这里有已填充的字段,它也会在上面的结构体中填充
type TemplateTwo struct {
	Xyz map[string] interface{} `json:”XYZ”`
}

err = json.Unmarshal(Form, &TemplateTwo)
if err !=nil{
	logger.Errorf(“Erro msg”)
}

reflectValue := reflect.ValueOf(TemplateTwo)
typeOfValue := reflectValue.Type()

for i := 0; i< reflectValue.NumField(); i++ {
	fmt.Printf("Field: %s\tValue: %v\n", typeOfValue.Field(i).Name, reflectValue.Field(i).Interface())
}

更多关于Golang中如何遍历并更新一个结构体到另一个结构体的实战教程也可以访问 https://www.itying.com/category-94-b0.html

16 回复

你好 @skillian,我已按照你的建议编辑了帖子。谢谢!

更多关于Golang中如何遍历并更新一个结构体到另一个结构体的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


此外,对于增量JSON,其值是动态的,并且两个JSON的文件结构也不同。

是的,没错。唯一的区别在于,我需要在结构体层面反映更新后的值,因为我的函数应该返回的是结构体本身,而不是文件。

你好,@Contra_Boy,你能编辑一下你的帖子,然后把代码重新粘贴到代码块里吗?

要创建一个代码块,请用三个反引号包裹你的代码。

例如:

code

@Contra_Boy 你不能直接将两个JSON文件解组到同一个模型吗?https://play.golang.org/p/bRaASBMVPju?第二个应该只更新第一个。

func main() {
    fmt.Println("hello world")
}

你好,@Contra_Boy,很抱歉,我还没有完全理解你想要做什么。听起来你是有两个 JSON 文件,并且想在字段名重叠的情况下,将一个 JSON 文件的数据合并到另一个文件中。是这样吗?

看起来你的使用场景可能与我之前在另一个帖子中尝试回答的类似:将字符串转换为嵌套结构。那些解决方案中有适合你的吗?

你好,感谢回复。但我已经拥有需要更新到另一个结构体上的值,这些值是JSON格式。我正在尝试找出一种方法,来获取我的结构体(它是一个 map[string]interface{})的键列表,然后遍历这些值,并用它们来替换我另一个结构体中的空值。

这是一个不错的解决方案,唯一的问题是 JSON 是在调用端点时动态获取的。 有没有办法在 JSON 是动态获取的情况下实现下面的代码?

const (
	primaryJSON = `{
	"FirstName": "Sean",
	"Address": "123 Street Rd. Bensalem, PA  19020"
}`

	deltaJSON = `{
	"FirstName": "Bob"
}`
)

@Contra_Boy 我们试试这样:你能提供示例的源 JSON 文件,以及你希望得到的结果示例吗?

编辑: 之所以这样问,是因为我对你所说的“delta Json 的值是动态的”但你又需要将数据放入结构体中感到有些困惑;在 Go 中无法拥有动态结构体,所以我很难理解这个用例。

错误:answerField := reflect.ValueOf(newEntity).FieldByName(value.Name)

Value.FieldByName

FieldByName 返回具有给定名称的结构体字段。如果未找到字段,则返回零值。如果 v 的 Kind 不是结构体,则会引发 panic


你的错误是 Error: panic: reflect: call of reflect.Value.FieldByName on ptr Value,Value 的类型是指针(Ptr),Value 的类型不是结构体,因此引发了 panic。

好的,我尝试了类似下面的代码,但它总是报以下错误。有什么办法可以修复吗? 我基本上是将旧结构体和新结构体的指针作为 existingEntitynewEntity 传入。

错误:panic: reflect: call of reflect.Value.FieldByName on ptr Value

entityType := reflect.TypeOf(existingEntity).Elem()
	for i := 0; i < entityType.NumField(); i++ {
		value := entityType.Field(i)
		value1 := entityType.Field(i).Name
		fmt.Println(value1)
		//tag := value.Tag
	templateField := reflect.ValueOf(existingEntity).Elem().Field(i)
	answerField := reflect.ValueOf(newEntity).FieldByName(value.Name)

	fmt.Println("the old field looks like ", oldField)
	fmt.Println("the new field looks like ", newField)
111

检查结构体字段?

https://play.golang.org/p/GZcw9BmcNHd

package main

import (
	"fmt"
	"reflect"
)

type showName struct {
	FirstName string `json:"firstName" binding:"required,excludesall=!@#$%^&*()<>?+{}[]"`
	Address   string `json:"Address" binding:"required,excludesall=!@#$%^&*()<>?+{}[]"`
}

func main() {
	fmt.Println("Hello, playground")
	var i = &showName{
		FirstName: "ss",
		// 你设置的字段
	}
	// 因为 i 是 showName 指针类型,使用 Elem 方法移除指针。
	iType := reflect.TypeOf(i).Elem()
	iValue := reflect.ValueOf(i).Elem()
	for _, name := range []string{"FirstName", "b"} {
		// 不检查标签,需要遍历所有字段的标签以匹配你的标签。
		fieldType, ok := iType.FieldByName(name)
		if !ok {
			// 未找到 name 字段
			continue
		}
		fmt.Println(fieldType.Name, fieldType.Tag, iValue.FieldByName(name).Interface())
	}

}

好的,这是转换后的Markdown文档:

基本上,辅助文件中的所有更新数据都需要反映到主文件中。

主文件:
{
  "general": {
    "displayName": "Information",
    "fields": {
      "company": {
        "type": "text",
        "currentValue": "",
        "displayName": "Company Name",
        "required": true
      },
      "streetAddress1": {
        "type": "text",
        "currentValue": "",
        "displayName": "Street Address",
        "required": true
      },
      "streetAddress2": {
        "type": "text",
        "currentValue": "",
        "displayName": "Street Address 2"
      },
      "city": {
        "type": "text",
        "currentValue": "",
        "displayName": "City",
        "required": true
      },
      "state": {
        "inline": true,
        "type": "dropdown",
        "currentValue": "",
        "displayName": "State",
        "required": true,
        "options": [
          "CA",
          "NH"
        ]
      },
      "zipcode": {
        "inline": true,
        "type": "text",
        "currentValue": "",
        "displayName": "Zipcode",
        "required": true
      },
      "companyWebsite": {
        "type": "text",
        "currentValue": "",
        "displayName": "Company Website",
        "required": true
      }
    }
  }

辅助文件:
{
	"general": {
		"company": "XYZ",
		"streetAddress1": "ABC street",
		"streetAddress2": "",
		"city": "SFO",
		"state": "CA",
		"zipcode": "12345",
		"companyWebsite": "https://example.com"
	}

在Golang中遍历并更新结构体,可以使用反射(reflect)包来实现。以下是基于你提供的代码示例:

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

type Template struct {
    General struct {
        ShowName string                `json:"showName"`
        Fields   map[string]XYZField   `json:"fields"`
    } `json:"xyz"`
}

type XYZField struct {
    ShowName string   `json:"showName"`
    Required bool     `json:"required"`
}

type TemplateTwo struct {
    Xyz map[string]interface{} `json:"xyz"`
}

func updateTemplateFromTemplateTwo(template *Template, templateTwo *TemplateTwo) error {
    // 遍历TemplateTwo的Xyz字段
    for key, value := range templateTwo.Xyz {
        // 使用反射获取Template的General字段
        templateValue := reflect.ValueOf(template).Elem()
        generalField := templateValue.FieldByName("General")
        
        if generalField.IsValid() {
            // 根据key更新对应的字段
            switch key {
            case "showName":
                if strVal, ok := value.(string); ok {
                    // 更新ShowName字段
                    showNameField := generalField.FieldByName("ShowName")
                    if showNameField.IsValid() && showNameField.CanSet() {
                        showNameField.SetString(strVal)
                    }
                }
            case "fields":
                if mapVal, ok := value.(map[string]interface{}); ok {
                    // 更新Fields映射
                    fieldsField := generalField.FieldByName("Fields")
                    if fieldsField.IsValid() {
                        // 确保Fields映射已初始化
                        if fieldsField.IsNil() {
                            fieldsField.Set(reflect.MakeMap(fieldsField.Type()))
                        }
                        
                        // 遍历并更新Fields映射
                        for fieldKey, fieldValue := range mapVal {
                            if fieldMap, ok := fieldValue.(map[string]interface{}); ok {
                                xyzField := XYZField{}
                                
                                // 设置ShowName
                                if showName, ok := fieldMap["showName"].(string); ok {
                                    xyzField.ShowName = showName
                                }
                                
                                // 设置Required
                                if required, ok := fieldMap["required"].(bool); ok {
                                    xyzField.Required = required
                                }
                                
                                // 将XYZField添加到映射中
                                fieldsField.SetMapIndex(reflect.ValueOf(fieldKey), reflect.ValueOf(xyzField))
                            }
                        }
                    }
                }
            }
        }
    }
    
    return nil
}

// 通用反射更新函数
func updateStructByReflection(dest interface{}, src map[string]interface{}) error {
    destValue := reflect.ValueOf(dest).Elem()
    destType := destValue.Type()
    
    for i := 0; i < destValue.NumField(); i++ {
        field := destValue.Field(i)
        fieldType := destType.Field(i)
        
        // 获取JSON标签
        jsonTag := fieldType.Tag.Get("json")
        if jsonTag == "" {
            jsonTag = fieldType.Name
        }
        
        // 检查源映射中是否有对应字段
        if srcValue, exists := src[jsonTag]; exists {
            if field.CanSet() {
                srcValueReflect := reflect.ValueOf(srcValue)
                
                // 处理嵌套结构体
                if field.Kind() == reflect.Struct {
                    if nestedMap, ok := srcValue.(map[string]interface{}); ok {
                        // 递归处理嵌套结构体
                        updateStructByReflection(field.Addr().Interface(), nestedMap)
                    }
                } 
                // 处理映射类型
                else if field.Kind() == reflect.Map {
                    if srcMap, ok := srcValue.(map[string]interface{}); ok {
                        // 创建新映射
                        mapType := field.Type()
                        newMap := reflect.MakeMap(mapType)
                        
                        // 遍历源映射并设置值
                        for mapKey, mapValue := range srcMap {
                            if nestedMap, ok := mapValue.(map[string]interface{}); ok {
                                // 创建XYZField实例
                                xyzField := XYZField{}
                                if showName, ok := nestedMap["showName"].(string); ok {
                                    xyzField.ShowName = showName
                                }
                                if required, ok := nestedMap["required"].(bool); ok {
                                    xyzField.Required = required
                                }
                                newMap.SetMapIndex(reflect.ValueOf(mapKey), reflect.ValueOf(xyzField))
                            }
                        }
                        field.Set(newMap)
                    }
                }
                // 处理基本类型
                else if srcValueReflect.Type().AssignableTo(field.Type()) {
                    field.Set(srcValueReflect)
                }
            }
        }
    }
    
    return nil
}

func main() {
    // 示例JSON数据
    jsonStr1 := `{"xyz": {"showName": "", "fields": {}}}`
    jsonStr2 := `{"xyz": {"showName": "Test Show", "fields": {"field1": {"showName": "First Name", "required": true}}}}`
    
    // 反序列化Template
    var template Template
    if err := json.Unmarshal([]byte(jsonStr1), &template); err != nil {
        fmt.Printf("Error unmarshaling template: %v\n", err)
        return
    }
    
    // 反序列化TemplateTwo
    var templateTwo TemplateTwo
    if err := json.Unmarshal([]byte(jsonStr2), &templateTwo); err != nil {
        fmt.Printf("Error unmarshaling templateTwo: %v\n", err)
        return
    }
    
    fmt.Println("Before update:")
    fmt.Printf("ShowName: %s\n", template.General.ShowName)
    fmt.Printf("Fields: %v\n", template.General.Fields)
    
    // 使用方法1:特定更新函数
    if err := updateTemplateFromTemplateTwo(&template, &templateTwo); err != nil {
        fmt.Printf("Error updating template: %v\n", err)
    }
    
    fmt.Println("\nAfter update (method 1):")
    fmt.Printf("ShowName: %s\n", template.General.ShowName)
    fmt.Printf("Fields: %v\n", template.General.Fields)
    
    // 重置template进行方法2测试
    json.Unmarshal([]byte(jsonStr1), &template)
    
    // 使用方法2:通用反射函数
    if err := updateStructByReflection(&template, templateTwo.Xyz); err != nil {
        fmt.Printf("Error updating with reflection: %v\n", err)
    }
    
    fmt.Println("\nAfter update (method 2):")
    fmt.Printf("ShowName: %s\n", template.General.ShowName)
    fmt.Printf("Fields: %v\n", template.General.Fields)
}

这个示例提供了两种方法:

  1. updateTemplateFromTemplateTwo - 针对特定结构体的更新函数
  2. updateStructByReflection - 通用的反射更新函数,可以处理嵌套结构体和映射

两种方法都使用反射来遍历和更新结构体字段,根据JSON标签匹配字段名,并处理不同类型的字段赋值。

回到顶部