一尘不染

如何有效地更新两个结构的值

go

我有以下代码,该代码分析YAML文件,并且需要匹配一个结构的值external并更新该internal结构的type属性。

例如,这是yaml文件(为简单起见翻译为bin)和正确解析的内容

package main

import (
    "fmt"
    "gopkg.in/yaml.v2"
    "log"
)

//internal config model for parsing

type InternalModel struct {
    models []Model2 `yaml:"models"`
}

type Model2 struct {
    Name   string `yaml:"name"`
    Type   string `yaml:"type"`
    Target string `yaml:"target"`
}

var internal_config = []byte(`
 models:
   - name: myapp
     type: app1
     target: ./

   - name: myapp2
     type: app2
     target: ./
`)

type ExternalConfig struct {
    Landscape Zone `yaml:"Landscape"`
}

type Zone struct {
    Zone   string  `yaml:"zone"`
    Models []Model `yaml:"models"`
}

type Model struct {
    AppType     string `yaml:"app-type"`
    ServiceType string `yaml:"service-type"`
}


var external_config = []byte(`
Landscape:
  zone: zone1
  models:
    - app-type: app1
      service-type: GCP
    - app-type: app2
      service-type: AMAZON
  zone: zone2
  models:
    - app-type: app3
      service-type: AZURE
    - app-type: app4Í
      service-type: HEROKU
`)

func main() {

    // This is the internal config which needs updated

    internalConfiglYaml := InternalModel{}

    err := yaml.Unmarshal([]byte(internal_config), &internalConfiglYaml)
    if err != nil {
        log.Fatalf("error in model internalConfiglYaml: %v", err)
    }
    //fmt.Printf("%+v\n", internalConfiglYaml)

    //--------------------------Second config file-----------------------//
    //This is the external config yaml

    extConfigYaml := ExternalConfig{}

    err = yaml.Unmarshal([]byte(external_config), &extConfigYaml)
    if err != nil {
        log.Fatalf("error in model extConfigYaml: %v", err)
    }
    fmt.Printf("%+v\n", extConfigYaml)

    landscape := "zone1"

    modifiedConfig := ConvertTypes(internalConfiglYaml, extConfigYaml, landscape)

    fmt.Printf("%+v\n", modifiedConfig)

}

func ConvertTypes(int_cfg InternalModel, ext_config ExternalConfig, landscape string) (out_cfg InternalModel) {

    for _, module := range int_cfg.models {

        switch module.Type {
        case "app1":
            //here I hard-coded the value "GCP" but it should come from the yaml struct after parsing
            module.Type = "GCP" // should be something like ext_config.models.service-type when the key in the struct
        case "app2":
            //here I hard-coded the value "AMAZON" but it should come from the yaml struct after parsing
            module.Type = "AMAZON"

        }
    }

    return int_cfg
}

//At the end what I need to do is to get the internal yaml file to be changed to the following struct
//The changes are when the type=app-type I need to modify the type in the internal config, here its GCP and ruby

//internal_config_after_changes := []byte(`
//
//
//models:
// - name: myapp
//   type: GCP
//   target: ./
//
// - name: myapp2
//   type: AMAZON
//   target: ./
//
//
//`)

最后,我需要做的是将内部yaml文件更改为上面的结构internal_config_after_changes 。更改是type=app- type我需要 修改type值时internal_config,这里是从app1to GCPapp2toamazon

问题在于第二个循环,我应该使用它来迭代external_config和匹配的值,我不确定如何以有效的方式将它们组合在一起。


阅读 264

收藏
2020-07-02

共1个答案

一尘不染

Golang常见问题解答介绍了有关地图和切片的指针:

映射和切片值的行为类似于指针:它们是包含指向基础映射或切片数据的指针的描述符。复制地图或切片值不会复制其指向的数据。复制接口值将复制存储在接口值中的事物。如果接口值包含一个结构,则复制接口值将复制该结构。如果接口值包含一个指针,则复制接口值将复制该指针,但不会复制它指向的数据。

在遍历内部模型切片时,ConvertType实际上是在创建[]Models其值的切片副本。由于该原因,Type不会更改原始结构的值。

for _, module := range int_cfg.models{}

上面的代码段正在创建的副本int_cfg.models{}

索引切片模型以指向切片模型的确切基础数组,以将值更改为:

package main

import (
    "fmt"
    "log"
    "strings"

    "gopkg.in/yaml.v2"
)

//internal config model for parsing

type InternalModel struct {
    Models []Model2 `yaml:"models"`
}

type Model2 struct {
    Name   string `yaml:"name"`
    Type   string `yaml:"type"`
    Target string `yaml:"target"`
}

var internal_config = []byte(`
  models:
    - name: myapp
      type: app1
      target: ./

    - name: myapp2
      type: app2
      target: ./
`)

type ExternalConfig struct {
    Landscape []Zone `yaml:"Landscape"`
}

type Zone struct {
    Zone   string  `yaml:"zone"`
    Models []Model `yaml:"models"`
}

type Model struct {
    AppType     string `yaml:"app-type"`
    ServiceType string `yaml:"service-type"`
}

var external_config = []byte(`
Landscape:
  - zone: zone1
    models:
     - app-type: app1
       service-type: GCP
     - app-type: app2
       service-type: AMAZON
  - zone: zone2
    models:
     - app-type: app3
       service-type: AZURE
     - app-type: app4Í
       service-type: HEROKU
`)

func main() {

    //This is the internal config which needs to be update

    internalConfiglYaml := InternalModel{}

    err := yaml.Unmarshal(internal_config, &internalConfiglYaml)
    if err != nil {
        log.Fatalf("error in model internalConfiglYaml: %v", err)
    }
    fmt.Printf("%+v\n", internalConfiglYaml)

    //--------------------------Second config file-----------------------//
    //This is the external config yaml

    extConfigYaml := ExternalConfig{}
    // var response interface{}

    err = yaml.Unmarshal(external_config, &extConfigYaml)
    if err != nil {
        log.Fatalf("error in model extConfigYaml: %v", err)
    }
    fmt.Printf("%+v\n", extConfigYaml)

    landscape := "zone1"

    modifiedConfig := ConvertTypes(&internalConfiglYaml, extConfigYaml, landscape)

    fmt.Printf("%+v\n", modifiedConfig)

}

// ConvertTypes for changing the intConfig struct types
func ConvertTypes(int_cfg *InternalModel, ext_config ExternalConfig, landscape string) (out_cfg *InternalModel) {

    for _, module := range ext_config.Landscape {
        if module.Zone == landscape {
            for i, value := range module.Models {
                switch strings.Compare(value.AppType, int_cfg.Models[i].Type) {
                case 0:
                    //here I hard-coded the value "GCP" but it should come from the yaml struct after parsing
                    int_cfg.Models[i].Type = value.ServiceType // should be something like ext_config.models.service-type when the key in the struct
                default:
                }
            }
        }
    }

    return int_cfg
}

如果检查以上代码片段,您还将认识到我已经更改了结构。

type InternalModel struct {
    models []Model2 `yaml:"models"`
}

首字母大写以使其可导出为:

type InternalModel struct {
    Models []Model2 `yaml:"models"`
}

由于struct InternalModelis
unexportable,因此字段model无法解析提供的internal_configyaml,从而导致在解组yaml后导致空的[]
slice数据。

我注意到的另一件事是您再次将字节转换为字节。没有必要。

err := yaml.Unmarshal([]byte(internal_config), &internalConfiglYaml)

所以我将其更改为:

err := yaml.Unmarshal(internal_config, &internalConfiglYaml)

因为internal_config已经[]byte在全局变量中使用字节声明。

2020-07-02