Skip to content

Decode always clobbers existing values in destination map for nested struct fields. #62

Description

@skaggmannen

Description

We are using mapstructure to deal with JSON payloads where our application only knows about a sub-set of the fields. We do this by unmarshalling the JSON to a map[string]any, and then use mapstructure.Decode() to decode to a struct containing only the fields we know about.

Then when we need to commit changes made to the struct we use mapstructure.Decode() to copy the fields to the map[string]any, which we then marshal into JSON.

So very simplified, this is what we're doing:

someJSON := readDataFromSomewhere()

var m map[string]any
json.Unmarshal(someJSON, &m)

var s struct {
	A int
	B struct {
		C int
	}
}
mapstructure.Decode(m, &s)

s.B.C = 42;

mapstructure.Decode(s, &m)
someJSON, _ = json.Marshal(m)

writeDataSomeWhere(someJSON)

The issue

What we noticed is that any nested structs used will "erase" all other fields for the nested map in the destination.

So if the input in the example above looks like this:

{
  "A": 1,
  "B": {
    "C": 2,
    "D": "we don't know about this field"
  },
  "E": "another field we don't know about" 
}

The output would look like this:

{
  "A": 1,
  "B": {
    "C": 42
  }
  "E": "another field we don't know about"
}

This erases the D field from the final output, which is less than desirable in our use case!

Proposed solution

I've created a pull request with a proposed new config flag called MergeIntoExisting which will make mapstructure.Decoder recursively merge the input into the destination for all nested structs: #61

I have tested that the changes works for our use case but I also purposefully limited it to only be applied when decoding structs to maps, since I'm not sure how generally applicable this is to all other cases (e.g. map-to-map or map-to-struct).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions