如何在Go中写入yml文件

huangapple go评论91阅读模式
英文:

How to write to yml files in Go

问题

我正在尝试为Go中的Snowfakery编写一个yml文件,但我对Go非常陌生,对它们的结构体不熟悉。我已经能够使用Python编写自己的脚本来实现这一点,但在Go中遇到了一些困难。我已经查看了一些关于如何在Go中编写yml文件的答案,它们对于入门帮助很大,但由于我特定yml中的不同类型,我感到困惑。

我需要yml文件看起来像这样:

- snowfakery_version: 3
- object: Logistics
  count: 1
  fields:
    Product_ID:
      random_choice:
      - M14860
      - L47181
      - L47182
    Type:
      random_choice:
      - A10
      - C17
      - F16
    Air_Temperature:
      random_number:
        min: 298
        max: 300
    Tool Wear Fail:
      if:
      - choice:
          when: ${{Tool_Wear>2}}
          pick:
            random_choice:
            - 1
            - 0
      - choice:
          when: ${{Tool_Wear<2}}
          pick: 2

所以我尝试创建类似这样的结构体:

package main

import (
	"fmt"
	"io/ioutil"
	"log"

	"gopkg.in/yaml.v3"
)

// TotalSchema represents the YAML schema
type TotalSchema struct {
	Snowfakery_version []string   `yaml:"snowfakery_version"`
	Object             []DataItem `yaml:"objects"`
}

// DataItem represents an object type in the schema
type DataItem struct {
	Object string          `yaml:"object"`
	Count  int             `yaml:"count"`
	Fields LogisticsFields `yaml:"fields"`
}

// Fields represents the fields of an object type
type LogisticsFields struct {
	ProductID     RandomChoice      `yaml:"Product_ID"`
	Type          RandomChoice      `yaml:"Type"`
	AirTemperature RandomNumber     `yaml:"Air_Temperature"`
	ToolWearFail  []ChoiceCondition `yaml:"Tool Wear Fail"`
}

type RandomNumber struct {
	Min int `yaml:"min"`
	Max int `yaml:"max"`
}

type RandomChoice struct {
	RandomChoice []string `yaml:"random_choice"`
}

type ChoiceCondition struct {
	When string `yaml:"when"`
	Pick int    `yaml:"pick"`
}

但是,每当我尝试创建结构体并使用它来写入yml文件时,我得到一个完全空的yml文件。我希望能得到任何帮助。提前谢谢。

编辑
通过使用接口结构和映射,我已经能够完成大部分工作,例如:

package main

import (
	"fmt"
	"io/ioutil"

	"gopkg.in/yaml.v3"
)

// TotalSchema represents the YAML schema
type TotalSchema struct {
	Snowfakery_version int `yaml:"snowfakery_version"`
}

// DataItem represents an object type in the schema
type DataItem struct {
	Object string      `yaml:"object"`
	Count  int         `yaml:"count"`
	Fields interface{} `yaml:"fields"`
}

// Fields represents the fields of an object type
type Fields struct {
	RandomChoices []string          `yaml:"random_choices,omitempty"`
	RandomNumber  RandomNumber      `yaml:"random_number,omitempty"`
	Choice        []ChoiceArray     `yaml:"if,omitempty"`
	Random_Choice []ChoiceProbArray `yaml:"random_choice,omitempty"`
}

type RandomNumber struct {
	Min int `yaml:"min"`
	Max int `yaml:"max"`
}

type RandomChoice struct {
	RandomChoice []string `yaml:"random_choice"`
}

type ChoiceArray struct {
	Choice ChoiceCondition `yaml:"choice"`
}

type ChoiceProbArray struct {
	ChoiceProb ChoiceProbability `yaml:"choice"`
}

type ChoiceCondition struct {
	When string `yaml:"when"`
	Pick int    `yaml:"pick"`
}

type ChoiceProbability struct {
	Probability string `yaml:"probability"`
	Pick        int    `yaml:"pick"`
}

var logisticsDict []interface{}
var totalStructDict TotalSchema
var objArray []DataItem
var object DataItem

func logisticsHandler(number int, objectName string) {
	object.Count = number
	object.Object = objectName

	logisticsDict = []interface{}{
		TotalSchema{Snowfakery_version: 3},
		DataItem{
			Object: "logistics",
			Count:  4,
			Fields: map[string]interface{}{
				"Product ID": Fields{
					RandomChoices: []string{"M14860",
						"L47181",
						"L47182",
					},
				},
				"Air_Temperature": Fields{
					RandomNumber: RandomNumber{
						Min: 298,
						Max: 300,
					},
				},
				"Process_Temperature": Fields{
					RandomNumber: RandomNumber{
						Min: 298,
						Max: 300,
					},
				},
				"Heat Dissipation Fail": Fields{
					Choice: []ChoiceArray{
						{ChoiceCondition{
							When: "${{(Process_Temperature - Air_Temperature)<8.6 and Rotational_Speed<1380}}",
							Pick: 1,
						}},
						{ChoiceCondition{
							When: "${{(Process_Temperature - Air_Temperature)>8.6 or Rotational_Speed>1380}}",
							Pick: 0,
						}},
					},
				},
			},
		},
	}

}

func main() {

	//other funciton I use to populate the rest of the struct
	logisticsHandler(4, "logistics")
	fmt.Println(logisticsDict)
	data, err := yaml.Marshal(logisticsDict)
	if err != nil {
		panic(err)
	}

	err = ioutil.WriteFile("logistics.yml", data, 0)
	if err != nil {
		panic(err)
	}

	fmt.Println("YAML data has been written to 'logistics.yml' file.")
}

但是,每当我运行yaml.Marshal代码时,它会自动对我的代码进行排序。我现在想知道如何使其保持与我创建的顺序完全一致,因为Snowfakery依赖于按顺序定义变量。

英文:

I am trying to write a yml file for snowfakery in go and I am very new to go so I am unfamiliar with their structs. I have been able to write my own script to do this in python but am struggling a bit more with go. I have looked at some of the other answers for how to write to a yml file in go and they helped a lot with starting but because of the different types in my specific yml I got confused

I need the yml file to look something like this

- snowfakery_version: 3
- object: Logistics
count: 1
fields:
Product_ID:
random_choice:
- M14860
- L47181
- L47182
Type:
random_choice:
- A10
- C17
- F16
Air_Temperature:
random_number:
min: 298
max: 300
Tool Wear Fail:
if:
- choice:
when: ${{Tool_Wear&gt;2}}
pick:
random_choice:
- 1
- 0
- choice:
when: ${{Tool_Wear&lt;2}}
pick: 2

so i have tried making structs like

package main
import (
&quot;fmt&quot;
&quot;io/ioutil&quot;
&quot;log&quot;
&quot;gopkg.in/yaml.v3&quot;
// &quot;os&quot;
// &quot;strings&quot;
)
// TotalSchema represents the YAML schema
type TotalSchema struct {
snowfakery_version []string   `yaml:&quot;snowfakery_version&quot;`
object             []DataItem `yaml:&quot;objects&quot;`
}
// DataItem represents an object type in the schema
type DataItem struct {
Object  string          `yaml:&quot;object&quot;`
Count   int             `yaml:&quot;count&quot;`
Fields  LogisticsFields `yaml:&quot;fields&quot;`
}
// Fields represents the fields of an object type
type LogisticsFields struct {
ProductID           RandomChoice        `yaml:&quot;Product_ID&quot;`
Type                RandomChoice        `yaml:&quot;Type&quot;`
ToolWear            RandomNumber        `yaml:&quot;Tool_Wear&quot;`
ToolWearFail        []ChoiceCondition   `yaml:&quot;Tool Wear Fail&quot;`
}
type RandomNumber struct {
Min int `yaml:&quot;min&quot;`
Max int `yaml:&quot;max&quot;`
}
type RandomChoice struct {
RandomChoice []string `yaml:&quot;random_choice&quot;`
}
type ChoiceCondition struct {
When string `yaml:&quot;when&quot;`
Pick int    `yaml:&quot;pick&quot;`
}

but whenever I try to create the struct and use it to write to a yml file

func main() {
totalStructDict.snowfakery_version = []string{&quot;3&quot;}
//other funciton I use to populate the rest of the struct
logisticsHandler(4, &quot;logistics&quot;)
fmt.Println(totalStructDict)
data, err := yaml.Marshal(totalStructDict)
if err != nil {
panic(err)
}
fmt.Println( data)
err = ioutil.WriteFile(&quot;logistics.yml&quot;, data, 0)
if err != nil {
panic(err)
}
fmt.Println(&quot;YAML data has been written to &#39;logistics.yml&#39; file.&quot;)

I get a completely empty yml file. I would love any help. Thanks in advance

EDIT
I have been able to get most of the way with by using the interface structure and maps ie.

     package main
import (
&quot;fmt&quot;
&quot;io/ioutil&quot;
&quot;gopkg.in/yaml.v3&quot;
// &quot;os&quot;
// &quot;strings&quot;
)
// TotalSchema represents the YAML schema
type TotalSchema struct {
Snowfakery_version int `yaml:&quot;snowfakery_version&quot;`
}
// DataItem represents an object type in the schema
type DataItem struct {
Object string      `yaml:&quot;object&quot;`
Count  int         `yaml:&quot;count&quot;`
Fields interface{} `yaml:&quot;fields&quot;`
}
// Fields represents the fields of an object type
type Fields struct {
RandomChoices []string          `yaml:&quot;random_choices,omitempty&quot;`
RandomNumber  RandomNumber      `yaml:&quot;random_number,omitempty&quot;`
Choice        []ChoiceArray     `yaml:&quot;if,omitempty&quot;`
Random_Choice []ChoiceProbArray `yaml:&quot;random_choice,omitempty&quot;`
}
type RandomNumber struct {
Min int `yaml:&quot;min&quot;`
Max int `yaml:&quot;max&quot;`
}
type RandomChoice struct {
RandomChoice []string `yaml:&quot;random_choice&quot;`
}
type ChoiceArray struct {
Choice ChoiceCondition `yaml:&quot;choice&quot;`
}
type ChoiceProbArray struct {
ChoiceProb ChoiceProbability `yaml:&quot;choice&quot;`
}
type ChoiceCondition struct {
When string `yaml:&quot;when&quot;`
Pick int    `yaml:&quot;pick&quot;`
}
type ChoiceProbability struct {
Probability string `yaml:&quot;probability&quot;`
Pick        int    `yaml:&quot;pick&quot;`
}
var logisticsDict []interface{}
var totalStructDict TotalSchema
var objArray []DataItem
var object DataItem
func logisticsHandler(number int, objectName string) {
object.Count = number
object.Object = objectName
logisticsDict = []interface{}{
TotalSchema{Snowfakery_version: 3},
DataItem{
Object: &quot;logistics&quot;,
Count:  4,
Fields: map[string]interface{}{
&quot;Product ID&quot;: Fields{
RandomChoices: []string{&quot;M14860&quot;,
&quot;L47181&quot;,
&quot;L47182&quot;,
},
},
&quot;Air_Temperature&quot;: Fields{
RandomNumber: RandomNumber{
Min: 298,
Max: 300,
},
},
&quot;Process_Temperature&quot;: Fields{
RandomNumber: RandomNumber{
Min: 298,
Max: 300,
},
},
&quot;Heat Dissipation Fail&quot;: Fields{
Choice: []ChoiceArray{
{ChoiceCondition{
When: &quot;${{(Process_Temperature - Air_Temperature)&lt;8.6 and Rotational_Speed&lt;1380}}&quot;,
Pick: 1,
}},
{ChoiceCondition{
When: &quot;${{(Process_Temperature - Air_Temperature)&gt;8.6 or Rotational_Speed&gt;1380}}&quot;,
Pick: 0,
}},
},
},
},
},
}
}
func main() {
//other funciton I use to populate the rest of the struct
logisticsHandler(4, &quot;logistics&quot;)
fmt.Println(logisticsDict)
data, err := yaml.Marshal(logisticsDict)
if err != nil {
panic(err)
}
// fmt.Println(data)
err = ioutil.WriteFile(&quot;logistics.yml&quot;, data, 0)
if err != nil {
panic(err)
}
fmt.Println(&quot;YAML data has been written to &#39;logistics.yml&#39; file.&quot;)
}

But now whenever I run the yaml.Marshel code it is auto sorting my code. I am now wondering how to get it to stay exactly in the order that I created it in because snowfakery relies on variables to be defined in order

答案1

得分: 1

在你的示例中,snowfakery_version是一个字典中唯一的键,其值是标量整数值3。这个字典是列表中的第一项:

- snowfakery_version: 3

上述内容的JSON等效形式是:

[
{"snowfakery_version": 3}
]

这与你的数据结构不匹配,你的数据结构看起来是这样的:

type TotalSchema struct {
snowfakery_version []string   `yaml:"snowfakery_version"`
object             []DataItem `yaml:"objects"`
}

这对应于一个类似以下的YAML文件:

snowfakery_version: [3]

或者作为JSON:

{
"snowfakery_version": [3]
}

考虑到你正在解析一个已建立的格式,我认为将其解析为Go结构可能会有些麻烦:

  1. 似乎没有正式的模式文档

  2. 看起来一些字段实际上是“联合”类型。也就是说:

    • 字段定义可以是结构,也可以是其他对象的列表。
    • pick可以是标量值(整数、字符串)或结构体

    等等。

在Go中,解析联合类型通常意味着使用interface{},然后通过在运行时检查数据来动态确定要执行的操作。

从顶部开始,你的文件格式是一个“事物”的列表,其中每个事物可以是snowfakery_version或对象定义。因此,我们从以下内容开始:

type SnowfakeryConfig []interface{}

然后,我们为预期的各种类型创建struct定义,例如:

SnowfakeryVersion struct {
SnowfakeryVersion int `yaml:"snowfakery_version"`
}
ObjectSpec struct {
Object string                 `yaml:"object,omitempty"`
Count  int                    `yaml:"count,omitempty"`
Fields map[string]interface{} `yaml:"fields,omitempty"`
}
.
.
.

这些可以附加到SnowfakeryConfig列表中。以下是一个通过程序生成你问题中示例配置的示例:

package main

import (
  "os"

  "gopkg.in/yaml.v3"
)

type (
  SnowfakeryConfig []interface{}

  SnowfakeryVersion struct {
    SnowfakeryVersion int `yaml:"snowfakery_version"`
  }

  ObjectSpec struct {
    Object string                 `yaml:"object,omitempty"`
    Count  int                    `yaml:"count,omitempty"`
    Fields map[string]interface{} `yaml:"fields,omitempty"`
  }

  FieldSpec struct {
    Fake         string        `yaml:"fake,omitempty"`
    RandomChoice RandomChoice  `yaml:"random_choice,omitempty"`
    RandomNumber *RandomNumber `yaml:"random_number,omitempty"`
    IfExpression IfExpression  `yaml:"if,omitempty"`
    Object       string        `yaml:"object,omitempty"`
  }

  RandomChoice []interface{}
  RandomNumber struct {
    Min int
    Max int
  }

  IfExpression []Choice

  Choice struct {
    Choice ChoiceSpec
  }

  ChoiceSpec struct {
    When string
    Pick interface{}
  }
)

func main() {
  config := SnowfakeryConfig{}

  config = append(config, SnowfakeryVersion{SnowfakeryVersion: 3})

  config = append(config, ObjectSpec{
    Object: "Logistics",
    Count:  1,
    Fields: map[string]interface{}{
      "Product_ID": FieldSpec{
        RandomChoice: RandomChoice{
          "M14860",
          "L47181",
          "L47182",
        },
      },
      "Type": FieldSpec{
        RandomChoice: RandomChoice{
          "A10",
          "C17",
          "F16",
        },
      },
      "Air_Temperature": FieldSpec{
        RandomNumber: &RandomNumber{
          Min: 298,
          Max: 300,
        },
      },
      "Tool Wear Fail": FieldSpec{
        IfExpression: IfExpression{
          Choice{
            Choice: ChoiceSpec{
              When: "${{Tool_Wear>2}}",
              Pick: FieldSpec{
                RandomChoice: RandomChoice{
                  1,
                  0,
                },
              },
            },
          },
          Choice{
            Choice: ChoiceSpec{
              When: "${{Tool_Wear<2}}",
              Pick: 2,
            },
          },
        },
      },
    },
  })

  content, err := yaml.Marshal(config)
  if err != nil {
    panic(err)
  }

  if err := os.WriteFile("output.yaml", content, 0666); err != nil {
    panic(err)
  }

}

运行上述代码会在output.yaml中生成以下内容:

- snowfakery_version: 3
- object: Logistics
  count: 1
  fields:
    Air_Temperature:
        random_number:
            min: 298
            max: 300
    Product_ID:
        random_choice:
            - M14860
            - L47181
            - L47182
    Tool Wear Fail:
        if:
            - choice:
                when: ${{Tool_Wear>2}}
                pick:
                    random_choice:
                        - 1
                        - 0
            - choice:
                when: ${{Tool_Wear<2}}
                pick: 2
    Type:
        random_choice:
            - A10
            - C17
            - F16

解析YAML需要我们在运行时动态检测对象类型。我们可以通过查找从文件中读取的每个项中的特定键来实现这一点:

fd, err := os.ReadFile("sample.yaml")
if err != nil {
  panic(err)
}

if err := yaml.Unmarshal(fd, &config); err != nil {
  panic(err)
}

for _, item := range config {
  if _, ok := item.(map[string]interface{})["snowfakery_version"]; ok {
    fmt.Println("found version")
    if err := mapstructure.Decode(item, &version); err != nil {
      panic(err)
    }
    fmt.Printf("version: %d\n", version.SnowfakeryVersion)
    continue
  }
  if _, ok := item.(map[string]interface{})["object"]; ok {
    fmt.Println("found object")
    if err := mapstructure.Decode(item, &objspec); err != nil {
      panic(err)
    }
    fmt.Printf("object name: %s\n", objspec.Object)
    for k := range objspec.Fields {
      fmt.Printf("  field: %s\n", k)
    }
    continue
  }
}

在这个示例中,我使用mapstructure模块将我们的map[string]interface{}转换为我们在确定对象类型后所需的结构。

这里包含了我使用的完整测试代码;你可以调用./example generate来创建output.yaml,或者调用./example parse somefile.yaml来解析somefile.yaml中的YAML。

package main

import (
  "fmt"
  "os"

  "github.com/mitchellh/mapstructure"
  "gopkg.in/yaml.v3"
)

type (
  SnowfakeryConfig []interface{}

  SnowfakeryVersion struct {
    SnowfakeryVersion int `mapstructure:"snowfakery_version"`
  }

  ObjectSpec struct {
    Object string                 `mapstructure:"object,omitempty"`
    Count  int                    `mapstructure:"count,omitempty"`
    Fields map[string]interface{} `mapstructure:"fields,omitempty"`
  }

  FieldSpec struct {
    Fake         string        `mapstructure:"fake,omitempty"`
    RandomChoice RandomChoice  `mapstructure:"random_choice,omitempty"`
    RandomNumber *RandomNumber `mapstructure:"random_number,omitempty"`
    IfExpression IfExpression  `mapstructure:"if,omitempty"`
    Object       string        `mapstructure:"object,omitempty"`
  }

  RandomChoice []interface{}
  RandomNumber struct {
    Min int
    Max int
  }

  IfExpression []Choice

  Choice struct {
    Choice ChoiceSpec
  }

  ChoiceSpec struct {
    When string
    Pick interface{}
  }
)

func main() {
  config := SnowfakeryConfig{}

  if os.Args[1] == "parse" {
    version := SnowfakeryVersion{}
    objspec := ObjectSpec{}

    fd, err := os.ReadFile(os.Args[2])
    if err != nil {
      panic(err)
    }

    if err := yaml.Unmarshal(fd, &config); err != nil {
      panic(err)
    }

    for _, item := range config {
      if _, ok := item.(map[string]interface{})["snowfakery_version"]; ok {
        fmt.Println("found version")
        if err := mapstructure.Decode(item, &version); err != nil {
          panic(err)
        }
        fmt.Printf("version: %d\n", version.SnowfakeryVersion)
        continue
      }
      if _, ok := item.(map[string]interface{})["object"]; ok {
        fmt.Println("found object")
        if err := mapstructure.Decode(item, &objspec); err != nil {
          panic(err)
        }
        fmt.Printf("object name: %s\n", objspec.Object)
        for k := range objspec.Fields {
          fmt.Printf("  field: %s\n", k)
        }
        continue
      }
    }
  } else if os.Args[1] == "generate" {
    config = append(config, SnowfakeryVersion{SnowfakeryVersion: 3})

    config = append(config, ObjectSpec{
      Object: "Logistics",
      Count:  1,
      Fields: map[string]interface{}{
        "Product_ID": FieldSpec{
          RandomChoice: RandomChoice{
            "M14860",
            "L47181",
            "L47182",
          },
        },
        "Type": FieldSpec{
          RandomChoice: RandomChoice{
            "A10",
            "C17",
            "F16",
          },
        },
        "Air_Temperature": FieldSpec{
          RandomNumber: &RandomNumber{
            Min: 298,
            Max: 300,
          },
        },
        "Tool Wear Fail": FieldSpec{
          IfExpression: IfExpression{
            Choice{
              Choice: ChoiceSpec{
                When: "${{Tool_Wear>2}}",
                Pick: FieldSpec{
                  RandomChoice: RandomChoice{
                    1,
                    0,
                  },
                },
              },
            },
            Choice{
              Choice: ChoiceSpec{
                When: "${{Tool_Wear<2}}",
                Pick: 2,
              },
            },
          },
        },
      },
    })

    content, err := yaml.Marshal(config)
    if err != nil {
      panic(err)
    }

    if err := os.WriteFile("output.yaml", content, 0666); err != nil {
      panic(err)
    }
  }
}
英文:

In your example, snowfakery_version is the only key in a dictionary whose value is the scalar integer value 3. This dictionary is the first item in a list:

- snowfakery_version: 3

The JSON equivalent to the above is:

[
{&quot;snowfakery_version&quot;: 3}
]

That doesn't match your data structure, which looks like:

type TotalSchema struct {
snowfakery_version []string   `yaml:&quot;snowfakery_version&quot;`
object             []DataItem `yaml:&quot;objects&quot;`
}

This would correspond to a YAML file that looks like:

snowfakery_version: [3]

Or as JSON:

{
&quot;snowfakery_version&quot;: [3]
}

With the understanding that you're parsing an established format, I think it's going to be a bit of pain to parse this into Go structs:

  1. There does not appear to be a formal schema document

  2. It looks like some of the fields are effectively "union" types. That is:

    • A field definition can either be a structure, or it can be a list of additional objects.
    • pick can be a scalar value (integer, string) or structure

    etc.

In go, parsing a union type usually means using an interface{} and then figuring out what to do dynamically by inspecting the data at runtime.

Starting at the top, your file format is a list of "things", where each thing can be either a snowfakery_version or an object definition. So that means we start with:

type SnowfakeryConfig []interface{}

Then we create the struct definitions for various types that we expect, e.g.:

SnowfakeryVersion struct {
SnowfakeryVersion int `yaml:&quot;snowfakery_version&quot;`
}
ObjectSpec struct {
Object string                 `yaml:&quot;object,omitempty&quot;`
Count  int                    `yaml:&quot;count,omitempty&quot;`
Fields map[string]interface{} `yaml:&quot;fields,omitempty&quot;`
}
.
.
.

These can be appended to a SnowfakeryConfig list. Here's an example that programatically produces the sample config from your question:

package main
import (
&quot;os&quot;
&quot;gopkg.in/yaml.v3&quot;
)
type (
SnowfakeryConfig []interface{}
SnowfakeryVersion struct {
SnowfakeryVersion int `yaml:&quot;snowfakery_version&quot;`
}
ObjectSpec struct {
Object string                 `yaml:&quot;object,omitempty&quot;`
Count  int                    `yaml:&quot;count,omitempty&quot;`
Fields map[string]interface{} `yaml:&quot;fields,omitempty&quot;`
}
FieldSpec struct {
Fake         string        `yaml:&quot;fake,omitempty&quot;`
RandomChoice RandomChoice  `yaml:&quot;random_choice,omitempty&quot;`
RandomNumber *RandomNumber `yaml:&quot;random_number,omitempty&quot;`
IfExpression IfExpression  `yaml:&quot;if,omitempty&quot;`
Object       string        `yaml:&quot;object,omitempty&quot;`
}
RandomChoice []interface{}
RandomNumber struct {
Min int
Max int
}
IfExpression []Choice
Choice struct {
Choice ChoiceSpec
}
ChoiceSpec struct {
When string
Pick interface{}
}
)
func main() {
config := SnowfakeryConfig{}
config = append(config, SnowfakeryVersion{SnowfakeryVersion: 3})
config = append(config, ObjectSpec{
Object: &quot;Logistics&quot;,
Count:  1,
Fields: map[string]interface{}{
&quot;Product_ID&quot;: FieldSpec{
RandomChoice: RandomChoice{
&quot;M14860&quot;,
&quot;L47181&quot;,
&quot;L47182&quot;,
},
},
&quot;Type&quot;: FieldSpec{
RandomChoice: RandomChoice{
&quot;A10&quot;,
&quot;C17&quot;,
&quot;F16&quot;,
},
},
&quot;Air_Temperature&quot;: FieldSpec{
RandomNumber: &amp;RandomNumber{
Min: 298,
Max: 300,
},
},
&quot;Tool Wear Fail&quot;: FieldSpec{
IfExpression: IfExpression{
Choice{
Choice: ChoiceSpec{
When: &quot;${{Tool_Wear&gt;2}}&quot;,
Pick: FieldSpec{
RandomChoice: RandomChoice{
1,
0,
},
},
},
},
Choice{
Choice: ChoiceSpec{
When: &quot;${{Tool_Wear&lt;2}}&quot;,
Pick: 2,
},
},
},
},
},
})
content, err := yaml.Marshal(config)
if err != nil {
panic(err)
}
if err := os.WriteFile(&quot;output.yaml&quot;, content, 0666); err != nil {
panic(err)
}
}

Running the above code produces, in output.yaml:

- snowfakery_version: 3
- object: Logistics
count: 1
fields:
Air_Temperature:
random_number:
min: 298
max: 300
Product_ID:
random_choice:
- M14860
- L47181
- L47182
Tool Wear Fail:
if:
- choice:
when: ${{Tool_Wear&gt;2}}
pick:
random_choice:
- 1
- 0
- choice:
when: ${{Tool_Wear&lt;2}}
pick: 2
Type:
random_choice:
- A10
- C17
- F16

Parsing the YAML requires us to dynamically detect object types at runtime. We can do this by looking for specific keys in each item read from the file:

fd, err := os.ReadFile(&quot;sample.yaml&quot;)
if err != nil {
panic(err)
}
if err := yaml.Unmarshal(fd, &amp;config); err != nil {
panic(err)
}
for _, item := range config {
if _, ok := item.(map[string]interface{})[&quot;snowfakery_version&quot;]; ok {
fmt.Println(&quot;found version&quot;)
if err := mapstructure.Decode(item, &amp;version); err != nil {
panic(err)
}
fmt.Printf(&quot;version: %d\n&quot;, version.SnowfakeryVersion)
continue
}
if _, ok := item.(map[string]interface{})[&quot;object&quot;]; ok {
fmt.Println(&quot;found object&quot;)
if err := mapstructure.Decode(item, &amp;objspec); err != nil {
panic(err)
}
fmt.Printf(&quot;object name: %s\n&quot;, objspec.Object)
for k := range objspec.Fields {
fmt.Printf(&quot;  field: %s\n&quot;, k)
}
continue
}
}

In this example I'm using the mapstructure module to convert our map[string]interface{} into the desired structure after we've figured out what object type we have.

The complete test code I used here is included below; you can call ./example generate to create output.yaml, or ./example parse somefile.yaml to parse the YAML in somefile.yaml.

package main
import (
&quot;fmt&quot;
&quot;os&quot;
&quot;github.com/mitchellh/mapstructure&quot;
&quot;gopkg.in/yaml.v3&quot;
)
type (
SnowfakeryConfig []interface{}
SnowfakeryVersion struct {
SnowfakeryVersion int `mapstructure:&quot;snowfakery_version&quot;`
}
ObjectSpec struct {
Object string                 `mapstructure:&quot;object,omitempty&quot;`
Count  int                    `mapstructure:&quot;count,omitempty&quot;`
Fields map[string]interface{} `mapstructure:&quot;fields,omitempty&quot;`
}
FieldSpec struct {
Fake         string        `mapstructure:&quot;fake,omitempty&quot;`
RandomChoice RandomChoice  `mapstructure:&quot;random_choice,omitempty&quot;`
RandomNumber *RandomNumber `mapstructure:&quot;random_number,omitempty&quot;`
IfExpression IfExpression  `mapstructure:&quot;if,omitempty&quot;`
Object       string        `mapstructure:&quot;object,omitempty&quot;`
}
RandomChoice []interface{}
RandomNumber struct {
Min int
Max int
}
IfExpression []Choice
Choice struct {
Choice ChoiceSpec
}
ChoiceSpec struct {
When string
Pick interface{}
}
)
func main() {
config := SnowfakeryConfig{}
if os.Args[1] == &quot;parse&quot; {
version := SnowfakeryVersion{}
objspec := ObjectSpec{}
fd, err := os.ReadFile(os.Args[2])
if err != nil {
panic(err)
}
if err := yaml.Unmarshal(fd, &amp;config); err != nil {
panic(err)
}
for _, item := range config {
if _, ok := item.(map[string]interface{})[&quot;snowfakery_version&quot;]; ok {
fmt.Println(&quot;found version&quot;)
if err := mapstructure.Decode(item, &amp;version); err != nil {
panic(err)
}
fmt.Printf(&quot;version: %d\n&quot;, version.SnowfakeryVersion)
continue
}
if _, ok := item.(map[string]interface{})[&quot;object&quot;]; ok {
fmt.Println(&quot;found object&quot;)
if err := mapstructure.Decode(item, &amp;objspec); err != nil {
panic(err)
}
fmt.Printf(&quot;object name: %s\n&quot;, objspec.Object)
for k := range objspec.Fields {
fmt.Printf(&quot;  field: %s\n&quot;, k)
}
continue
}
}
} else if os.Args[1] == &quot;generate&quot; {
config = append(config, SnowfakeryVersion{SnowfakeryVersion: 3})
config = append(config, ObjectSpec{
Object: &quot;Logistics&quot;,
Count:  1,
Fields: map[string]interface{}{
&quot;Product_ID&quot;: FieldSpec{
RandomChoice: RandomChoice{
&quot;M14860&quot;,
&quot;L47181&quot;,
&quot;L47182&quot;,
},
},
&quot;Type&quot;: FieldSpec{
RandomChoice: RandomChoice{
&quot;A10&quot;,
&quot;C17&quot;,
&quot;F16&quot;,
},
},
&quot;Air_Temperature&quot;: FieldSpec{
RandomNumber: &amp;RandomNumber{
Min: 298,
Max: 300,
},
},
&quot;Tool Wear Fail&quot;: FieldSpec{
IfExpression: IfExpression{
Choice{
Choice: ChoiceSpec{
When: &quot;${{Tool_Wear&gt;2}}&quot;,
Pick: FieldSpec{
RandomChoice: RandomChoice{
1,
0,
},
},
},
},
Choice{
Choice: ChoiceSpec{
When: &quot;${{Tool_Wear&lt;2}}&quot;,
Pick: 2,
},
},
},
},
},
})
content, err := yaml.Marshal(config)
if err != nil {
panic(err)
}
if err := os.WriteFile(&quot;output.yaml&quot;, content, 0666); err != nil {
panic(err)
}
}
}

huangapple
  • 本文由 发表于 2023年7月4日 03:50:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76607717.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定