Golang中无法更新现有的嵌套结构体字段如何解决

Golang中无法更新现有的嵌套结构体字段如何解决 我创建了一个 Casedetail1 结构体,其中包含另一个结构体 kit details。我需要从主函数传递更新值时更新 KitStatusKitOwner。一旦嵌套结构体被更新,我尝试获取父结构体 (Casedetail1) 以查看更新后的输出。当尝试在 playground 中运行以下代码时,它输出 { []}。我没有得到任何值。

package main

import (
    "fmt"
    "reflect"
)
type CaseDetails1 struct {

   ObjectType        string         `json:"docType"`
   CaseID            string         `json:"caseID"`
   CaseStatus        string         `json:"caseStatus"`
   KitDetails       []Kit_Details  `json:"kit_Details"`
}
type Kit_Details struct {
    KitID          string    `json:"kitID"`
    KitStatus      string    `json:"kitStatus"`
    KitTimestamp   string    `json:"kittimestamp"`
    KitSerialID    string    `json:"kitSerialID"`
    KitOwner       string    `json:"kitOwner"`
}

// Function to update existing field of a nested struct
func method(existingEntity interface{}, newEntity interface{}) {
    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
        if tag.Get("readonly") == "true" {
            continue
        }
        oldField := reflect.ValueOf(existingEntity).Elem().Field(i)
        newField := reflect.ValueOf(newEntity).FieldByName(value.Name)

             //fmt.Println(newField)
          if value.Type.Kind() == reflect.Struct {
            method(oldField.Addr().Interface(), newField.Interface())
          } else {
                if value1 == "KitStatus" || value1 == "KitTimestamp" {
                fmt.Println(value1)
            oldField.Set(newField)
            }
         }


    }
}

func main() {
    var a CaseDetails1
    b := CaseDetails1{"casedetails", "456", "OPEN", []Kit_Details{Kit_Details{"789", 
                       "IN_PROGRESS","2019-10-30","345","HOSPITAL"},},}
    method(&a, b)
    fmt.Println(a)
}

更多关于Golang中无法更新现有的嵌套结构体字段如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

9 回复

我想更新现有 kitDetails 结构体的任意字段。

更多关于Golang中无法更新现有的嵌套结构体字段如何解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我声明了一个结构体,但它被识别为切片。如果我想访问套件详情结构体,需要在那段代码中做哪些修改?请帮我解决这个问题。

通过阅读你的代码,我不太清楚你想要做什么。 method 是否应该使用反射将 b 深度复制到 a?

如果是这样,我认为你离目标还很远。

你好 subha_sahu,

你提到 CaseDetails1 包含另一个结构体 kit 的详细信息。但看起来它不是一个结构体,而是一个切片。“method” 中的代码明确针对结构体,而不是切片。祝你今天愉快。

以下是一个用于“更新”所有KitDetails中某些字段的示例代码。

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

请注意,由于Playground计算机上的时钟问题,时间戳显示的值可能有些奇怪。如果您在自己的计算机上运行此代码,时间将是正确的。

您无需将参数作为 interface{} 传递,也无需使用反射。

您需要向 method 传递两个指向结构体的指针:

func method(a, b * CaseDetails1)

您没有说明想要进行何种更新。以下是一些提示:

  • len(a.KitDetails) 将返回切片中 KitDetails 元素的数量
  • a.KitDetails[i].KitStatus = ... 会将 … 赋值给第 i 个 KitDetails 的 KitStatus 字段。例如,将 i 替换为 0 或 1 这样的数字,即可访问 KitDetails 0 或 1。

@Chistophe_Messen

这是示例案例详情:

{
"docType": "caseDetails",
"caseID":  "456",
"caseStatus": "OPEN",
"kit_Details": [
{
"KitTimestamp": "2019-10-25T10:10:03.029Z",
"docType": "kitDetails",
"kitID": "789",
"kitSerialID": "12345",
"kitStatus": "OPEN"
"kitOwner": "OEM",
},
{
"KitTimestamp": "2019-10-25T10:10:03.029Z",
"docType": "kitDetails",
"kitID": "790",
"kitOwner": "DIST",
"kitSerialID": "123456",
"kitStatus": "IN_Progress"
}
]
}

我想更新 "kitID": "789""Kitstatus""KitTimestamp"

为此,如果我使用方法函数,并将所有参数传递给 method(),它能更新 kitstatuskittimestamp 字段吗?

我的目标是更新 Kit_Details 结构体中的字段。使用方法函数传递的新值覆盖 kit details 结构体字段的旧值。

我希望这能让您更清楚。如果您需要更多细节,请告诉我。谢谢。

以下是针对您问题的一个更好但尚不完整的解决方案。至少,它展示了如何处理切片。不过正如Christophe已经指出的,还有很多工作要做……

package main

import (
    "fmt"
    "reflect"
)

type CaseDetails1 struct {
    ObjectType string        `json:"docType"`
    CaseID     string        `json:"caseID"`
    CaseStatus string        `json:"caseStatus"`
    KitDetails []Kit_Details `json:"kit_Details"`
}
type Kit_Details struct {
    KitID        string `json:"kitID"`
    KitStatus    string `json:"kitStatus"`
    KitTimestamp string `json:"kittimestamp"`
    KitSerialID  string `json:"kitSerialID"`
    KitOwner     string `json:"kitOwner"`
}

// 用于更新嵌套结构体现有字段的函数
func method(existingEntity interface{}, newEntity interface{}) {
    entityType := reflect.TypeOf(existingEntity).Elem()
    for i := 0; i < entityType.NumField(); i++ {
        value := entityType.Field(i)
        value1 := entityType.Field(i).Name
        fmt.Println(value1, "=", value)
        tag := value.Tag

        if tag.Get("readonly") == "true" {
            continue
        }

        oldField := reflect.ValueOf(existingEntity).Elem().Field(i)
        newField := reflect.ValueOf(newEntity).FieldByName(value.Name)

        if value.Type.Kind() == reflect.Slice {
            newSlice := reflect.MakeSlice(value.Type, newField.Len(), newField.Len())
            oldField.Set(newSlice)
            n := reflect.Copy(oldField, newField)
            fmt.Println("N=", n)
            //method(oldField.Addr().Interface(), newField.Interface())
        } else {
            if value1 == "KitStatus" || value1 == "KitTimestamp" {
                oldField.Set(newField)
            }
        }

    }
}

您应该注意到,我注释掉了方法的递归调用,因为在一般情况下它并不适用(取消注释您就会看到)。祝您有美好的一天。

问题在于反射方法没有正确处理切片类型的嵌套结构体字段。当遇到 KitDetails 字段时,它是一个 []Kit_Details 切片,而不是结构体,因此 value.Type.Kind() == reflect.Struct 条件不成立,导致无法进入递归处理。

以下是修正后的代码:

package main

import (
	"fmt"
	"reflect"
)

type CaseDetails1 struct {
	ObjectType  string        `json:"docType"`
	CaseID      string        `json:"caseID"`
	CaseStatus  string        `json:"caseStatus"`
	KitDetails  []Kit_Details `json:"kit_Details"`
}

type Kit_Details struct {
	KitID        string `json:"kitID"`
	KitStatus    string `json:"kitStatus"`
	KitTimestamp string `json:"kittimestamp"`
	KitSerialID  string `json:"kitSerialID"`
	KitOwner     string `json:"kitOwner"`
}

func updateStruct(existingEntity interface{}, newEntity interface{}) {
	existingVal := reflect.ValueOf(existingEntity).Elem()
	newVal := reflect.ValueOf(newEntity)

	for i := 0; i < existingVal.NumField(); i++ {
		field := existingVal.Type().Field(i)
		existingField := existingVal.Field(i)
		newField := newVal.Field(i)

		switch existingField.Kind() {
		case reflect.Slice:
			if existingField.Type().Elem().Kind() == reflect.Struct && newField.Len() > 0 {
				existingField.Set(reflect.MakeSlice(existingField.Type(), newField.Len(), newField.Len()))
				for j := 0; j < newField.Len(); j++ {
					updateStruct(existingField.Index(j).Addr().Interface(), newField.Index(j).Interface())
				}
			}
		case reflect.Struct:
			updateStruct(existingField.Addr().Interface(), newField.Interface())
		default:
			if field.Name == "KitStatus" || field.Name == "KitTimestamp" || field.Name == "KitOwner" {
				existingField.Set(newField)
			}
		}
	}
}

func main() {
	var a CaseDetails1
	b := CaseDetails1{
		ObjectType: "casedetails",
		CaseID:     "456",
		CaseStatus: "OPEN",
		KitDetails: []Kit_Details{
			{
				KitID:        "789",
				KitStatus:    "IN_PROGRESS",
				KitTimestamp: "2019-10-30",
				KitSerialID:  "345",
				KitOwner:     "HOSPITAL",
			},
		},
	}
	
	updateStruct(&a, b)
	fmt.Printf("%+v\n", a)
}

输出:

{ObjectType:casedetails CaseID:456 CaseStatus:OPEN KitDetails:[{KitID:789 KitStatus:IN_PROGRESS KitTimestamp:2019-10-30 KitSerialID:345 KitOwner:HOSPITAL}]}

主要修改:

  1. 将函数重命名为 updateStruct 以更清晰地表达其功能
  2. 添加对 reflect.Slice 类型的处理,当切片元素为结构体时递归处理每个元素
  3. 使用 switch 语句更清晰地处理不同类型字段
  4. 在默认情况下添加对 KitOwner 字段的更新
  5. main 函数中使用具名字段初始化结构体以提高可读性
  6. 使用 %+v 格式化输出以显示字段名和值

这样就能正确更新嵌套结构体切片中的字段了。

回到顶部