首頁 >後端開發 >Golang >合併來自 YAML 檔案的兩個映射層次結構,保留所有鍵

合併來自 YAML 檔案的兩個映射層次結構,保留所有鍵

WBOY
WBOY轉載
2024-02-10 22:00:101306瀏覽

合并来自 YAML 文件的两个映射层次结构,保留所有键

php小編西瓜在這篇文章中將要介紹如何合併兩個來自 YAML 檔案的映射層次結構,並保留所有按鍵。這個過程非常簡單,只需要使用適當的函數和方法來完成。透過合併兩個映射層次結構,我們可以將它們的鍵和值組合在一起,以創建一個更大、更全面的映射層次結構。無論您是在處理設定檔還是在進行資料處理,合併映射層次結構都是一個非常有用的技巧。接下來,我們將一步一步為您介紹這個過程,讓您輕鬆掌握這個技巧。

問題內容

我正在研究涉及 yaml 檔案中的巢狀鍵的解決方案。軟體將讀取參數中傳遞的檔案並按照更新/新增密鑰的順序載入它們。

我有 2 個 yaml 文件,我想合併它們而不丟失任何金鑰。我想堆疊所有配置文件以生成單個地圖而不刪除任何鍵。

所以我有 yaml 1

env: test1
template:
  app:
    database: 
      name: oracle

yaml2

env: test2
template:
  app:
    database: 
      version : 12

我想要的結果是(順序是yaml1 - yaml2)

env: test2
template:
  app:
    database: 
      name: oracle
      version: 12

我嘗試使用地圖進行複製,但由於鍵具有相同的名稱,所以我最終得到了

env: test2
template:
    app:
        database:
            version: 12

我正在使用

用於讀取 yaml 的 gopkg.in/yaml.v3 為我提供了 map[string] 介面{}

# 和地圖使用複製

package main

import (
    "fmt"
    "log"
    "maps"
    "os"
    "path/filepath"

    "gopkg.in/yaml.v3"
)

type configuration struct {
    c  m
    fl []string
}

type m = map[string]interface{}

func (c *configuration) Update(nc m) {
    if c.c == nil {
        c.c = nc
    } else {
        maps.Copy(c.c, nc)
    }
}

func (c configuration) Print() {
    d, err := yaml.Marshal(&c.c)
    if err != nil {
        log.Fatalf("error: %v", err)
    }
    fmt.Printf("---:\n%s\n\n", string(d))
}

func (c configuration) ParseDir(path string) {

}

func (c *configuration) LoadFromFile(filename string) {

    // YAML string stored in a variable
    yf, yfErr := os.ReadFile(filename)

    if yfErr != nil {
        log.Fatal("Error reading the file ", yfErr)
    }
    // Map to store the parsed YAML data
    var data m

    // Unmarshal the YAML string into the data map
    err := yaml.Unmarshal(yf, &data)
    if err != nil {
        log.Fatal(err)
    }
    c.Update(data)
}

func listFiles(path string) []string {
    var returnLf []string
    err := filepath.Walk(path,
        func(path string, info os.FileInfo, err error) error {
            if err != nil {
                return err
            }
            if info.Mode().IsRegular() {
                returnLf = append(returnLf, path)
            }
            return nil
        })
    if err != nil {
        log.Println(err)
    }
    return returnLf

}

解決方法

假設您想要合併兩個 YAML 文件中以「範本」為鍵的 YAML 映射,一個相當簡單的實作將如下所示:

package main

import (
    "fmt"

    "gopkg.in/yaml.v3"
)

const data1 = `---
env: test1
template:
  app:
    database: 
      name: oracle
      foo: whatever
`

const data2 = `---
env: test2
template:
  app:
    some_stuff: [1, 2, 3, 4]
    database: 
      version : 12
      foo: 42
`

type T struct {
    Env  string         `yaml:"env"`
    Tmpl map[string]any `yaml:"template"`
}

func mergeMapsRecursively(dst, src map[string]any) map[string]any {
    res := make(map[string]any)

    for dstKey, dstVal := range dst {
        srcVal, exists := src[dstKey]
        if !exists {
            res[dstKey] = dstVal
            continue
        }

        dstValMap, dstValIsMap := dstVal.(map[string]any)
        srcValMap, srcValIsMap := srcVal.(map[string]any)
        if dstValIsMap && srcValIsMap {
            res[dstKey] = mergeMapsRecursively(dstValMap, srcValMap)
        } else {
            res[dstKey] = srcVal
        }
    }

    for srcKey, srcVal := range src {
        if _, exists := dst[srcKey]; !exists {
            res[srcKey] = srcVal
        }
    }

    return res
}

func main() {
    var a, b T

    if err := yaml.Unmarshal([]byte(data1), &a); err != nil {
        panic(err)
    }

    if err := yaml.Unmarshal([]byte(data2), &b); err != nil {
        panic(err)
    }

    fmt.Printf("%#v\n%#v\n%#v\n", a.Tmpl, b.Tmpl, mergeMapsRecursively(a.Tmpl, b.Tmpl))
}

Playground 連結

mergeMapsRecursively 函數遞歸地合併兩個映射中存在的欄位(如果它們都是映射),或將dst 中的值替換為src中的值,否則— 就像maps.Copy 所做的那樣。

如果這不完全是您所要求的,我希望我的範例無論如何都能讓您走上正軌。

以上是合併來自 YAML 檔案的兩個映射層次結構,保留所有鍵的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除