duanqianwei2485 2018-07-09 13:11
浏览 395
已采纳

如何正确解组Viper配置值以正确包含字符串数组的结构?

I noticed that this is perhaps a bug when viper tries to unmarshall to struct. To explain it better, consider this:

I have a cli command like below dd-cli submit-bug --name "Bug 1" --tag reason1 --tag reason2

Here is my command line source code

package cmd

import (
    "fmt"

    "github.com/spf13/viper"

    "github.com/spf13/cobra"
)

// SubmitBugOpts is a set of flags being exposed by this Deploy command
type SubmitBugOpts struct {
    Name string `mapstructure:"bug-name"`

    ReasonTags []string `mapstructure:"tags"`
}

var (
    submitBugOpts = SubmitBugOpts{}
)

func submitBugRun(cmd *cobra.Command, args []string) {
    fmt.Printf("Bug Name is %+v
", submitBugOpts.Name)
    fmt.Printf("List of tags is %+v
", submitBugOpts.ReasonTags)
    fmt.Printf("Length of tags is %d
", len(submitBugOpts.ReasonTags))
    for index, el := range submitBugOpts.ReasonTags {
        fmt.Printf("tag[%d] = %s
", index, el)
    }
}

var submitBugCmd = &cobra.Command{
    Use:   "submit-bug",
    Short: "Deploy/Install a helm chart to Kubernetes cluster",
    Run:   submitBugRun,
    PreRun: func(cmd *cobra.Command, args []string) {
        pFlags := cmd.PersistentFlags()
        viper.BindPFlag("bug-name", pFlags.Lookup("name"))
        viper.BindPFlag("tags", pFlags.Lookup("tag"))

        fmt.Printf("Viper all setting value: %+v
", viper.AllSettings())
        fmt.Printf("Before unmarshall: %+v
", submitBugOpts)
        viper.Unmarshal(&submitBugOpts)
        fmt.Printf("After unmarshall: %+v
", submitBugOpts)
    },
}

func init() {
    rootCmd.AddCommand(submitBugCmd)

    pFlags := submitBugCmd.PersistentFlags()
    pFlags.StringVar(&submitBugOpts.Name, "name", "", "the bug name")
    pFlags.StringArrayVar(&submitBugOpts.ReasonTags, "tag", nil, "the bug's reason tag. You can define it multiple times")

    submitBugCmd.MarkPersistentFlagRequired("name")
    submitBugCmd.MarkPersistentFlagRequired("tag")
}

I run this command:

dd-cli submit-bug --name "Bug 1" --tag reason1 --tag reason2

And the output is below

Viper all setting value: map[bug-name:Bug 1 tags:[reason1,reason2]]
Before unmarshall: {Name:Bug 1 ReasonTags:[reason1 reason2]}
After unmarshall: {Name:Bug 1 ReasonTags:[[reason1 reason2]]}
Bug Name is Bug 1
List of tags is [[reason1 reason2]]
Length of tags is 2
tag[0] = [reason1
tag[1] = reason2]

I expect the viper.Unmarshall() will correctly omit the [ for submitBugOpts.ReasonTags [0] and omit the ] for submitBugOpts.ReasonTags[1]. So the expected value of submitBugOpts.ReasonTags doesn't contains any [ and ] .

Any pointer how to fix this? I've submitted this issue on viper repo: https://github.com/spf13/viper/issues/527. However I am asking on SO just in case you guys know how to handle this too.

  • 写回答

1条回答 默认 最新

  • dqqpf32897 2018-07-13 15:11
    关注

    After digging into codes of github.com/spf13/{cobra,viper,pflag}for quite a while, I finally find the problem.

    When you call pFlags.StringArrayVar(&submitBugOpts.ReasonTags, "tag", nil, ...), the ReasonTags, of course, binds to a wrapper of pflag.stringArrayValue. source.

    And when you call viper.Unmarshall, viper uses v.Get to get the value binds to ReasonTags and then it calls v.find.

    In v.find, after the value is found, it uses the wrapper's ValueType() method to determine its type, the wrapper then calls the Type method of the wrapped type, pflag.stringArrayValue, and returns "stringArray". source

    But viper only handle "stringSlice" as a special case, so the value gets to default part of type switch, which uses its ValueString() method - making it into a string, with "[" and "]" on both side. source

    And when finally unmarshalling, as your output param, ReasonTags is of []string, the program simply splitted the string and set it into the field.

    As for solution, if you are ok with prohibitting tag to contain ,, simply change StringArrayVar to StringSliceVar, but this will results in --tag "Yet, the problem re-occurs" into []string{"Yet"," the problem re-occrus"}.

    If that is critical, you'll need to ask the developers of viper to create a case for stringArray.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 想问一下stata17中这段代码哪里有问题呀
  • ¥15 flink cdc无法实时同步mysql数据
  • ¥100 有人会搭建GPT-J-6B框架吗?有偿
  • ¥15 求差集那个函数有问题,有无佬可以解决
  • ¥15 【提问】基于Invest的水源涵养
  • ¥20 微信网友居然可以通过vx号找到我绑的手机号
  • ¥15 寻一个支付宝扫码远程授权登录的软件助手app
  • ¥15 解riccati方程组
  • ¥15 使用rabbitMQ 消息队列作为url源进行多线程爬取时,总有几个url没有处理的问题。
  • ¥15 Ubuntu在安装序列比对软件STAR时出现报错如何解决