使用Viper Go解析INI文件的过程并不如预期那样有效。

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

Unmarshaling INI file using Viper Go is not working as expected

问题

我正在使用viper进行应用程序配置。

以下是代码示例。

package main

import "github.com/spf13/viper"

type Config struct {
	WelcomeMessage string `mapstructure:"message"`
}

func main() {

	viper.SetConfigName("config")
	viper.SetConfigType("ini")
	viper.AddConfigPath(".")
	err := viper.ReadInConfig() // 查找并读取配置文件
	if err != nil {             // 处理读取配置文件时的错误
		// 处理错误
	}

	// var result map[string]interface{}
	config := Config{}

	err = viper.Unmarshal(&config)
	if err != nil {
		// 处理错误
	}
	...

}

INI文件中只有一个简单的配置项。

message=Welcome!

如果我使用default.message作为键,我可以使用viper.Get()来读取配置值。

由于某种原因,Unmarshal方法不起作用,也就是说,配置变量的WelcomeMessage字段没有设置值。

我做错了什么?

谢谢。

英文:

I am using viper for app configuration.

Code sample is below.

package main

import "github.com/spf13/viper"

type Config struct {
	WelcomeMessage string `mapstructure:"message"`
}

func main() {

	viper.SetConfigName("config")
	viper.SetConfigType("ini")
	viper.AddConfigPath(".")
	err := viper.ReadInConfig() // Find and read the config file
	if err != nil {             // Handle errors reading the config file
		//handle error
	}

	// var result map[string]interface{}
	config := Config{}

	err = viper.Unmarshal(&config)
	if err != nil {
		//handle error
	}
	...

}

INI file is simple with single config.

message=Welcome!

I can read configuration value using viper.Get() if I use default.message as key.

For some reason, Unmarshal is not working, meaning that there config variable is struct without value set in WelcomeMessage field.

What am I doing wrong?

Thanks.

答案1

得分: 3

假设viper指的是https://pkg.go.dev/github.com/dvln/viper,那么你应该阅读它的文档:

> 问:为什么不使用INI文件?
>
> 答:INI文件相当糟糕。它们没有标准格式,而且很难验证。Viper被设计用于处理JSON、TOML或YAML文件。如果有人真的想要添加这个功能,我很乐意合并。你可以轻松地指定应用程序允许使用的格式。

英文:

Assuming viper refers to https://pkg.go.dev/github.com/dvln/viper then you should read its documentation:

> Q: Why not INI files?
>
> A: Ini files are pretty awful. There’s no standard format, and they
> are hard to validate. Viper is designed to work with JSON, TOML or
> YAML files. If someone really wants to add this feature, I’d be happy
> to merge it. It’s easy to specify which formats your application will
> permit.

答案2

得分: 1

在调用viper.ReadInConfig之后,配置将以以下方式存储在一个映射中:

settings := map[string]interface{}{
	"default": map[string]interface{}{
		"message": "Welcome!",
	},
}

因此,结构应该定义如下:

type Config struct {
	Default struct {
		WelcomeMessage string `mapstructure:"message"`
	}
}

这是完整的示例:

package main

import (
	"fmt"

	"github.com/spf13/viper"
)

type Config struct {
	Default struct {
		WelcomeMessage string `mapstructure:"message"`
	}
}

func main() {
	viper.SetConfigName("config")
	viper.SetConfigType("ini")
	viper.AddConfigPath(".")
	err := viper.ReadInConfig()
	if err != nil {
		panic(err)
	}

	// 查看存储的内容。
	fmt.Printf("%#v\n", viper.AllSettings())

	config := Config{}

	err = viper.Unmarshal(&config)
	if err != nil {
		panic(err)
	}

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

输出结果为:

map[string]interface {}{"default":map[string]interface {}{"message":"Welcome!"}}
{Default:{WelcomeMessage:Welcome!}}
英文:

After viper.ReadInConfig is called, the config is stored in a map like this:

settings := map[string]any{
	"default": map[string]any{
		"message": "Welcome!",
	},
}

So the struct should be defined like this:

type Config struct {
	Default struct {
		WelcomeMessage string `mapstructure:"message"`
	}
}

This is the full example:

package main

import (
	"fmt"

	"github.com/spf13/viper"
)

type Config struct {
	Default struct {
		WelcomeMessage string `mapstructure:"message"`
	}
}

func main() {
	viper.SetConfigName("config")
	viper.SetConfigType("ini")
	viper.AddConfigPath(".")
	err := viper.ReadInConfig()
	if err != nil {
		panic(err)
	}

	// To see what is stored.
	fmt.Printf("%#v\n", viper.AllSettings())

	config := Config{}

	err = viper.Unmarshal(&config)
	if err != nil {
		panic(err)
	}

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

And the output:

map[string]interface {}{"default":map[string]interface {}{"message":"Welcome!"}}
{Default:{WelcomeMessage:Welcome!}}

答案3

得分: 1

https://github.com/spf13/viper 项目的文档非常不完善。通过查看源代码,我找到了使你的代码工作所需的内容。关键的洞察力是,你尝试解组的键隐式地位于名为"default"的部分中。因此,你的结构必须将相应的变量放在名为"Default"的结构中。

我首先创建了一个名为config.ini的文件,内容如下:

message=Welcome!
hello = goodbye

然后,我将以下代码放入一个名为x.go的文件中:

package main

import (
        "fmt"

        "github.com/spf13/viper"
)

type Config struct {
        Default struct {
                WelcomeMessage string `mapstructure:"message"`
                Hello          string
                Undefined      string
        }
}

func main() {

        viper.SetConfigName("config")
        viper.SetConfigType("ini")
        viper.AddConfigPath(".")
        err := viper.ReadInConfig() // 查找并读取配置文件
        if err != nil {             // 处理读取配置文件时的错误
                panic(err)
        }

        // var result map[string]interface{}
        config := Config{}

        err = viper.Unmarshal(&config)
        if err != nil {
                panic(err)
        }

        fmt.Printf("%#v\n", config)
}

执行"go run x.go"会产生以下输出(与你期望的相符,除了需要嵌套结构):

main.Config{Default:struct { WelcomeMessage string "mapstructure:\"message\""; Hello string; Undefined string }{WelcomeMessage:"Welcome!", Hello:"goodbye", Undefined:""}}

附注:在解决这个问题的过程中,我查看了Viper项目的源代码(包括其单元测试、API和文档),我不建议使用该项目。

附注:INI文件格式类似于CSV格式。两者都是由Microsoft创建的,在引入时都非常缺乏规范。导致存在许多歧义和不兼容的实现。应避免使用这两种格式。

英文:

The https://github.com/spf13/viper project is woefully under documented. Looking at the source I figured out what is needed to make your code work. The key insight is that the key you are trying to unmarshal is implicitly in a section named "default". So your structure has to put the corresponding variable inside a structure named "Default".

I started by creating a file named config.ini with this content:

message=Welcome!
hello = goodbye

I then put the following code in a file named x.go:

package main

import (
        "fmt"

        "github.com/spf13/viper"
)

type Config struct {
        Default struct {
                WelcomeMessage string `mapstructure:"message"`
                Hello          string
                Undefined      string
        }
}

func main() {

        viper.SetConfigName("config")
        viper.SetConfigType("ini")
        viper.AddConfigPath(".")
        err := viper.ReadInConfig() // Find and read the config file
        if err != nil {             // Handle errors reading the config file
                panic(err)
        }

        // var result map[string]interface{}
        config := Config{}

        err = viper.Unmarshal(&config)
        if err != nil {
                panic(err)
        }

        fmt.Printf("%#v\n", config)
}

Executing "go run x.go" produces this output (which matches what you expect, modulo the need for a nested struct):

main.Config{Default:struct { WelcomeMessage string "mapstructure:\"message\""; Hello string; Undefined string }{WelcomeMessage:"Welcome!", Hello:"goodbye", Undefined:""}}

P.S., Having gone to the trouble of solving this question, and looking at the Viper project source code (including its unit tests, API and documentation) I do not recommend using that project.

P.P.S., The INI file format is like the CSV format. Both were created by Microsoft and were horribly underspecified at the time of their introduction. Resulting in many ambiguities and incompatible implementations. Both formats should be avoided.

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

发表评论

匿名网友

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

确定