Golang:从从YAML反序列化的map[interface{}]interface{}中删除条目。

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

Golang: remove entry from a map[interface{}]interface{} unmarshalled from YAML

问题

我正在编写一些测试,需要在从YAML文件加载应用程序配置时检查不同的错误条件。由于YAML文件非常长,我的方法是从磁盘上的文件中读取完整的YAML内容,将其内容解组到一个变量中,根据其字符串键动态删除生成的map[interface{}]interface{}中的某些部分,最后将其编组并将内容写入不同的文件中,该文件将用作文本输入。也就是说,当我尝试从解组的YAML中删除部分时,我遇到了问题。以下是我的代码:

func getYAMLWithoutSection(t *testing.T, section string) map[interface{}]interface{} {
	t.Helper()

	yml := unmarshalYAML(t, getYAMLContentFromFile(t))
	var tmp interface{}
	tmp = yml

	keys := strings.Split(section, ".")
	for _, key := range keys {
		tmp = tmp.(map[interface{}]interface{})[key]
	}

	tmp = nil

	return yml
}

// 从磁盘读取文件并返回其内容
func getYAMLContentFromFile(t *testing.T) []byte {
	/* ... */
}

func unmarshalYAML(t *testing.T, ymlIn []byte) map[interface{}]interface{} {
    /* ... */
}

这不起作用,因为最终,tmp变量包含目标YAML部分的值,而不是其内存地址(其类型为interface{},而不是指针),因此当我将其设置为nil时,它对最终返回的原始yml变量中的值没有影响。我尝试了不同的方法,比如将tmp的类型切换为*interface{},但没有成功,我无法弄清楚正确的做法是什么。

英文:

I'm working on writing some tests where I need to check different error conditions when loading the application config from a YAML file. As the YAML file is quite long, my approach is to read the full YAML content from the file on disk, unmarshall its content to a variable, dynamically remove some section/s from the generated map[interface{}]interface{} based on their string keys, and finally marshall it and write the content to a different file on disk that will be used as the text input. That said, I'm facing issues when trying to remove sections from the unmarshalled yaml. Below is my code:

func getYAMLWithoutSection(t *testing.T, section string) map[interface{}]interface{} {
	t.Helper()

	yml := unmarshalYAML(t, getYAMLContentFromFile(t))
	var tmp interface{}
	tmp = yml

	keys := strings.Split(section, ".")
	for _, key := range keys {
		tmp = tmp.(map[interface{}]interface{})[key]
	}

	tmp = nil

	return yml
}

// Reads file from disk and returns its content
func getYAMLContentFromFile(t *testing.T) []byte {
	/* ... */
}

func unmarshalYAML(t *testing.T, ymlIn []byte) map[interface{}]interface{} {
    /* ... */
}

This doesn't work because at the end of the day, the tmp var contains the value of the targeted YAML section instead of its memory address (its type is interface{}, not a pointer), so when I set it to nil it has no effect over the value in the original yml variable that it finally returns. I've tried different things like switching the type of tmp to *interface{}, but with no success, and I can't figure out what would be the proper way of doing this.

答案1

得分: 1

你正在将值存储到一个变量中,然后将该变量设置为nil。这不会修改映射,只会修改变量。

根据你想要如何修改映射,在遍历keys时,如果是最后一次迭代,你可以选择以下两种方式之一:

m := tmp.(map[interface{}]interface{})
m[key] = nil

或者

m := tmp.(map[interface{}]interface{})
delete(m, key)

第一种方法会在映射中保留键,但值将为nil。第二种方法会从映射中删除键。

https://go.dev/tour/moretypes/22

英文:

You're storing the value into a variable and then setting that variable to nil. That won't modify the map only the variable.

Depending on how you want to modify the map, when you're on the last iteration while ranging over keys you can either:

m := tmp.(map[interface{}]interface{})
m[key] = nil

or

m := tmp.(map[interface{}]interface{})
delete(m, key)

The first approach will leave the key in the map but the value will be nil. The second approach removes the key from the map.

https://go.dev/tour/moretypes/22

答案2

得分: -1

我终于找到了实现我所寻找的方式(动态编辑包含未解组的YAML的嵌套映射)。这是我现在的代码样子:

func getYAMLWithoutSection(t *testing.T, section string) map[interface{}]interface{} {
	t.Helper()

	yml := unmarshalYAML(t, getConfigFileContent(t))
	tmp := yml

	keys := strings.Split(section, ".")
	nKeys := len(keys) - 1
	for i, key := range keys {
		if i == nKeys {
			tmp[key] = nil
			break
		}

		tmp = tmp[key].(map[interface{}]interface{})
	}

	return yml
}

问题在于原始版本(我在问题中发布的版本)中,我使用了一个类型为interface{}tmp变量,因此每次将子映射分配给此变量时,其内容都会被复制到它上面,并且在此变量上删除键(或修改其值)对原始的yml变量没有影响。然而,在这个版本中,我将tmp切换为map[interface{}]interface{}类型,并且考虑到在Go中,映射是引用类型,对此变量的每个修改实际上都会反映在tmp上。

英文:

I've finally found the way of achieving what I was looking for (dynamically editing the nested map that contains the unmarshalled YAML). This is how my code looks like now:

func getYAMLWithoutSection(t *testing.T, section string) map[interface{}]interface{} {
	t.Helper()

	yml := unmarshalYAML(t, getConfigFileContent(t))
	tmp := yml

	keys := strings.Split(section, ".")
	nKeys := len(keys) - 1
	for i, key := range keys {
		if i == nKeys {
			tmp[key] = nil
			break
		}

		tmp = tmp[key].(map[interface{}]interface{})
	}

	return yml
}

The thing is in the original version (the one I posted in my question) I was using a tmp variable with type interface{} so every time I assign a submap to this variable, its content is copied to it and removing keys (or modifying their values) on this variable has no effect over the original yml one. However, in this version I switch tmp to a map map[interface{}]interface{}, and taking into account that in Go maps are reference types, every modification on this variable is actually reflected on the tmp one.

huangapple
  • 本文由 发表于 2023年4月12日 23:29:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/75997305.html
匿名

发表评论

匿名网友

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

确定