5

Lets say I have this type:

type Foo struct{
   Bar string `json:"bar"`
}

and I want to unmarshal this json into it:

in := []byte(`{"bar":"aaa", "baz":123}`)

foo := &Foo{}
json.Unmarshal(in,foo)

will succeed just fine. I would like to at least know that there were fields that were skipped in the processing. Is there any good way to access that information?

playground snippet

captncraig
  • 22,118
  • 17
  • 108
  • 151

1 Answers1

1

As you're probably aware you can unmarshal any valid json into a map[string]interface{}. Having unmarsahled into an instance of Foo already there is no meta data available where you could check fields that were excluded or anything like that. You could however, unmarshal into both types and then check the map for keys that don't correspond to fields on Foo.

in := []byte(`{"bar":"aaa", "baz":123}`)

foo := &Foo{}
json.Unmarshal(in,foo)

allFields := &map[string]interface{}
json.Unmarshal(in, allFields)
for k, _ := range allFields {
    fmt.Println(k)
    // could also use reflect to get field names as string from Foo
    // the get the symmetric difference by nesting another loop here
    // and appending any key that is in allFields but not on Foo to a slice
}
evanmcdonnal
  • 46,131
  • 16
  • 104
  • 115
  • Very interesting solution. Would probably be a bit hard with deeply nested structs, but should work on with simple ones. – captncraig Dec 18 '15 at 19:16
  • @captncraig yeah it's less than ideal. If you wanna get really serious I think you could implement a general solution by flattening the `allFields` map and using a recursive algorithm to get all fields off nested structures in the concrete type. FYI for getting field names might find this useful; http://stackoverflow.com/questions/24337145/get-name-of-struct-field-using-reflection – evanmcdonnal Dec 18 '15 at 19:19
  • I really like how this toml lib handles it: https://godoc.org/github.com/BurntSushi/toml#MetaData.Undecoded but that kind of thing would require changes to the core json package :( – captncraig Dec 18 '15 at 19:20
  • @captncraig I don't know how feasible this is, just an idea that came to mind based on the package you linked to; implement `UnmarshalJSON` for `interface{}`, chain a call to the normal version of it, but not before reflecting the type and checking for all fields within the incoming string. If you access the json tag, you can guarantee a match pretty well by doing `tag = " + tag + ":` (ofc with proper quoting there, not sure how to do it in comment). Input strings can't really cause collision because they would need escapes so the `string.Contains` would be fairly reliable I think. – evanmcdonnal Dec 18 '15 at 19:26