Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UnmarshalKey looses some data if a subitem is overridden #1106

Open
SVilgelm opened this issue Apr 2, 2021 · 8 comments
Open

UnmarshalKey looses some data if a subitem is overridden #1106

SVilgelm opened this issue Apr 2, 2021 · 8 comments
Labels
kind/bug Something isn't working

Comments

@SVilgelm
Copy link

SVilgelm commented Apr 2, 2021

Expected behavior (what you expected to happen):
viper.UnmarshalKey should unmarshals whole structure, not overridden keys only

Actual behavior (what actually happened):
if viper.Set has been used to override a value of a sub item, then viper.UnmarshalKey unmarshals only overridden keys

Repl.it link:

https://replit.com/@SVilgelm/viperUnmarshalKey-bug

Code reproducing the issue:

package main

import (
	"strings"

	"github.com/spf13/viper"
)

func main() {
	config := `
resources:
  baz:
    name: "Super bazzz"
    xyz: true
    test: 123
`
	viper.Debug()
	println("########################################################################")

	viper.SetConfigType("yaml")
	viper.ReadConfig(strings.NewReader(config))
	viper.Debug()
	println("########################################################################")

	res1 := struct {
		Name string `mapstructure:"name"`
		XYZ  bool   `mapstructure:"xyz"`
	}{}
	viper.UnmarshalKey("resources.baz", &res1)
	println("res1 Name:", res1.Name, "; XYZ:", res1.XYZ)
	println("########################################################################")

	viper.Set("resources.baz.name", "override name")
	viper.Debug()
	println("########################################################################")

	res2 := struct {
		Name string `mapstructure:"name"`
		XYZ  bool   `mapstructure:"xyz"`
	}{}
	viper.UnmarshalKey("resources.baz", &res2)
	println("res2 Name:", res2.Name, "; XYZ:", res2.XYZ)
	println("########################################################################")
}

Environment:

  • Viper version:v1.7.1
  • Config source: any
  • File format: any
@SVilgelm SVilgelm added the kind/bug Something isn't working label Apr 2, 2021
@github-actions
Copy link

github-actions bot commented Apr 2, 2021

👋 Thanks for reporting!

A maintainer will take a look at your issue shortly. 👀

In the meantime: We are working on Viper v2 and we would love to hear your thoughts about what you like or don't like about Viper, so we can improve or fix those issues.

⏰ If you have a couple minutes, please take some time and share your thoughts: https://forms.gle/R6faU74qPRPAzchZ9

📣 If you've already given us your feedback, you can still help by spreading the news,
either by sharing the above link or telling people about this on Twitter:

https://twitter.com/sagikazarmark/status/1306904078967074816

Thank you! ❤️

@SVilgelm
Copy link
Author

SVilgelm commented Apr 2, 2021

Actually the issue with viper.Get, it does not return the whole structure

@madsoigard
Copy link

any update on this?

@sagikazarmark
Copy link
Collaborator

The problem is that resources.baz exists as a key, so UnmashalKey won't look any further. I wonder what happens if you create a Resources struct and try to unmarshal resources instead. I think that should work.

@SVilgelm
Copy link
Author

SVilgelm commented Feb 2, 2022

@sagikazarmark I have updated the repl, the problem still exists even if I unmarshal resources. I also updated th go.mod to use latest viper.

https://replit.com/@SVilgelm/viperUnmarshalKey-bug#main.go

Code:

package main

import (
	"strings"
  "fmt"

	"github.com/spf13/viper"
)

func main() {
	config := `
resources:
  baz:
    name: "Super bazzz"
    xyz: true
    test: 123
`
	viper.Debug()
	println("########################################################################")

	viper.SetConfigType("yaml")
	viper.ReadConfig(strings.NewReader(config))
	viper.Debug()
	println("########################################################################")

	res1 := struct {
		Name string `mapstructure:"name"`
		XYZ  bool   `mapstructure:"xyz"`
	}{}
	viper.UnmarshalKey("resources.baz", &res1)
	println("res1 Name:", res1.Name, "; XYZ:", res1.XYZ)
	println("########################################################################")

	viper.Set("resources.baz.name", "override name")
	viper.Debug()
	println("########################################################################")

	res2 := struct {
		Name string `mapstructure:"name"`
		XYZ  bool   `mapstructure:"xyz"`
	}{}
	viper.UnmarshalKey("resources.baz", &res2)
	println("res2 Name:", res2.Name, "; XYZ:", res2.XYZ)
	println("########################################################################")

  println("########################################################################")

  res3 := struct {
    Baz struct {
      Name string `mapstructure:"name"`
      XYZ  bool   `mapstructure:"xyz"`
    } `mapstructure:"baz"`
	}{}
	viper.UnmarshalKey("resources", &res3)
	println("res3 Name:", res3.Baz.Name, "; XYZ:", res3.Baz.XYZ)
	println("########################################################################")

  fmt.Printf("viper.Get: %+v\n", viper.Get("resources"))
  println("########################################################################")
}

Here is the output:

Aliases:
map[string]string{}
Override:
map[string]interface {}{}
PFlags:
map[string]viper.FlagValue{}
Env:
map[string]string{}
Key/Value Store:
map[string]interface {}{}
Config:
map[string]interface {}{}
Defaults:
map[string]interface {}{}
########################################################################
Aliases:
map[string]string{}
Override:
map[string]interface {}{}
PFlags:
map[string]viper.FlagValue{}
Env:
map[string]string{}
Key/Value Store:
map[string]interface {}{}
Config:
map[string]interface {}{"resources":map[string]interface {}{"baz":map[string]interface {}{"name":"Super bazzz", "test":123, "xyz":true
 go build
 ./main
Aliases:
map[string]string{}
Override:
map[string]interface {}{}
PFlags:
map[string]viper.FlagValue{}
Env:
map[string]string{}
Key/Value Store:
map[string]interface {}{}
Config:
map[string]interface {}{}
Defaults:
map[string]interface {}{}
########################################################################
Aliases:
map[string]string{}
Override:
map[string]interface {}{}
PFlags:
map[string]viper.FlagValue{}
Env:
map[string]string{}
Key/Value Store:
map[string]interface {}{}
Config:
map[string]interface {}{"resources":map[string]interface {}{"baz":map[string]interface {}{"name":"Super bazzz", "test":123, "xyz":true
 go build
 ./main
Aliases:
map[string]string{}
Override:
map[string]interface {}{}
PFlags:
map[string]viper.FlagValue{}
Env:
map[string]string{}
Key/Value Store:
map[string]interface {}{}
Config:
map[string]interface {}{}
Defaults:
map[string]interface {}{}
########################################################################
Aliases:
map[string]string{}
Override:
map[string]interface {}{}
PFlags:
map[string]viper.FlagValue{}
Env:
map[string]string{}
Key/Value Store:
map[string]interface {}{}
Config:
map[string]interface {}{"resources":map[string]interface {}{"baz":map[string]interface {}{"name":"Super bazzz", "test":123, "xyz":true
 go build
 ./main
Aliases:
map[string]string{}
Override:
map[string]interface {}{}
PFlags:
map[string]viper.FlagValue{}
Env:
map[string][]string{}
Key/Value Store:
map[string]interface {}{}
Config:
map[string]interface {}{}
Defaults:
map[string]interface {}{}
########################################################################
Aliases:
map[string]string{}
Override:
map[string]interface {}{}
PFlags:
map[string]viper.FlagValue{}
Env:
map[string][]string{}
Key/Value Store:
map[string]interface {}{}
Config:
map[string]interface {}{"resources":map[string]interface {}{"baz":map[string]interface {}{"name":"Super bazzz", "test":123, "xyz":true}}}
Defaults:
map[string]interface {}{}
########################################################################
res1 Name: Super bazzz ; XYZ: true
########################################################################
Aliases:
map[string]string{}
Override:
map[string]interface {}{"resources":map[string]interface {}{"baz":map[string]interface {}{"name":"override name"}}}
PFlags:
map[string]viper.FlagValue{}
Env:
map[string][]string{}
Key/Value Store:
map[string]interface {}{}
Config:
map[string]interface {}{"resources":map[string]interface {}{"baz":map[string]interface {}{"name":"Super bazzz", "test":123, "xyz":true}}}
Defaults:
map[string]interface {}{}
########################################################################
res2 Name: override name ; XYZ: false
########################################################################
########################################################################
res3 Name: override name ; XYZ: false
########################################################################
viper.Get: map[baz:map[name:override name]]
########################################################################

@missing1984
Copy link

running into the same issue. any workaround?

@SVilgelm
Copy link
Author

there is no workaround, the issue is that the viper has several internal storages. In order to fix the issue, the logic of Get should be changed to merge all possible combinations, like take the baz from the loaded config, then take the baz.name from the overrides and apply it and etc...

@kasparjarek
Copy link

Hi, since the release v1.18.0 the issue is also happening for the Unmarshal() function. I suspect that the issue was introduced by this pull request #1429. FYI @krakowski @sagikazarmark

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants