Go语言中更新YAML文件指定字段后写入新文件时Raw数据异常及空字段过多的问题求助
Let's break down your two issues and fix them step by step:
Issue 1: Raw Data Shows as Numbers
The Raw field in runtime.RawExtension is a byte slice. When you use gyaml.Marshal directly on the Shoot struct, it serializes this byte slice as a list of integers (each byte's ASCII value) instead of parsing it as structured YAML content. This happens because you were converting the InfraConfig to JSON first— the YAML serializer doesn't recognize those bytes as valid structured data.
Issue 2: Empty Fields Flooding the Output
The v1beta1.Shoot struct relies mostly on JSON tags, not YAML tags. Generic YAML marshalers like gyaml.Marshal don't respect the omitempty flags from JSON tags, so they output every zero-value field (empty strings, nil pointers, empty slices, etc.). You need to use Kubernetes' dedicated YAML utilities that handle their struct conventions correctly.
Fixed Solution
Here's the revised code that addresses both problems:
package main import ( "fmt" "github.com/gardener/gardener/pkg/apis/core/v1beta1" gyaml "github.com/go-yaml/yaml/v3" "io/ioutil" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/yaml" ) type InfraConfig struct { APIVersion string `yaml:"apiVersion,omitempty" json:"apiVersion,omitempty"` Kind string `yaml:"kind,omitempty" json:"kind,omitempty"` Networks struct { CloudNAT struct { MinPortsPerVM int `yaml:"minPortsPerVM,omitempty" json:"minPortsPerVM,omitempty"` NatIPNames []struct { Name string `yaml:"name,omitempty" json:"name,omitempty"` } `yaml:"natIPNames,omitempty" json:"natIPNames,omitempty"` } `yaml:"cloudNAT,omitempty" json:"cloudNAT,omitempty"` } `yaml:"networks,omitempty" json:"networks,omitempty"` } func main() { shoot, err := parseShoot("test.yaml") if err != nil { fmt.Printf("Failed to parse shoot: %v\n", err) return } // Parse InfrastructureConfig directly from YAML (no JSON conversion) var existingInfraConfig InfraConfig if err := yaml.Unmarshal(shoot.Spec.Provider.InfrastructureConfig.Raw, &existingInfraConfig); err != nil { fmt.Printf("Failed to unmarshal infra config: %v\n", err) return } // Update the target field existingInfraConfig.Networks.CloudNAT.NatIPNames[0].Name = "test2" // Marshal updated config back to YAML bytes updatedInfraBytes, err := gyaml.Marshal(existingInfraConfig) if err != nil { fmt.Printf("Failed to marshal updated infra config: %v\n", err) return } // Update RawExtension with valid YAML bytes shoot.Spec.Provider.InfrastructureConfig = &runtime.RawExtension{ Raw: updatedInfraBytes, } // Use Kubernetes' YAML marshaler to avoid empty fields outputBytes, err := yaml.Marshal(shoot) if err != nil { fmt.Printf("Failed to marshal shoot: %v\n", err) return } // Write the final cleaned-up YAML file if err := ioutil.WriteFile("test2.yaml", outputBytes, 0644); err != nil { fmt.Printf("Failed to write file: %v\n", err) return } fmt.Println("Successfully updated YAML file!") } func parseShoot(path string) (*v1beta1.Shoot, error) { var shootSpec *v1beta1.Shoot bytes, err := ioutil.ReadFile(path) if err != nil { return nil, err } // Use Kubernetes' YAML unmarshaler for correct struct tag handling err = yaml.Unmarshal(bytes, &shootSpec) if err != nil { return nil, err } return shootSpec, nil }
Key Changes Explained:
- Added YAML tags to
InfraConfig: The struct now supports both JSON and YAML serialization, matching the input format instead of relying on JSON conversion. - Worked directly with YAML for
InfraConfig: We parse the raw YAML bytes directly and marshal the updated config back to YAML. This ensures theRawfield contains valid YAML that the final marshaler will interpret correctly (not as a list of numbers). - Used Kubernetes'
yaml.Marshal: This utility understands Kubernetes struct conventions— it respectsomitemptyflags even for fields with only JSON tags, uses correct YAML field names (likeapiVersioninstead ofapiversion), and omits all zero-value fields from the output.
Expected Output
The generated test2.yaml will mirror your input exactly, with only the target field updated, no empty fields:
apiVersion: core.gardener.cloud/v1beta1 kind: Shoot metadata: name: test namespace: ns spec: provider: type: aaa infrastructureConfig: apiVersion: gcp.provider.extensions.gardener.cloud/v1alpha1 kind: InfrastructureConfig networks: cloudNAT: minPortsPerVM: 10000 natIPNames: - name: test2
内容的提问来源于stack exchange,提问作者PJEM

