go: encoding/json: tag `json:"-"` doesn't hide an embedded field
What version of Go are you using (go version
)?
$ go version go version go1.13.3 linux/amd64 $ gotip version go version devel +696c41488a Mon Nov 11 15:37:55 2019 +0000 linux/amd64
Does this issue reproduce with the latest release?
Yes, see gotip
version.
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/home/ainar/.cache/go-build" GOENV="/home/ainar/.config/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/home/ainar/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/home/ainar/go/go1.13" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/home/ainar/go/go1.13/pkg/tool/linux_amd64" GCCGO="/usr/bin/gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="/home/ainar/dev/tmp/go.mod" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build199093238=/tmp/go-build -gno-record-gcc-switches"
What did you do?
type A struct {
Name string `json:"name"`
}
type B struct {
A
Name string `json:"-"`
}
https://play.golang.org/p/uKY3umGYEp3
What did you expect to see?
Either:
<nil> {}
Or:
some error about tag conflict
What did you see instead?
<nil> {"name":"1234"}
About this issue
- Original URL
- State: open
- Created 5 years ago
- Reactions: 11
- Comments: 29 (26 by maintainers)
Also, that’s not quite true. Using the rules that already exist today, you can do:
See https://play.golang.org/p/7wHioEMdfaS
Another (more intuitive) way to omit attributes from json marshalling is:
See https://play.golang.org/p/0OdxDtXcx8-, see also this old blog post.
It’s work for me to omit attributes from json marshalling: type A struct { Name string
json:"name"
}type B struct { A Name string
json:"name,-,omitempty"
}I’m have been programming in Go as my main language for over 7 years and you just blew my mind.
Thanks @breml. I agree that this is intuitively what one would understand; it was my first impression too. You also raise a good point about how
omitempty
does allow shadowing via both struct field name and json field tag name.For the record, I still think this would be nice to have, for the sake of consistency. Unfortunately, while
omitempty
is an option,-
is not, so you cannot use it while also specifying a field tag name. This is what I tried to explain in https://github.com/golang/go/issues/35501#issuecomment-552890930.For example, taking the original example once again:
Following the
omitempty
logic of shadowing via the json field name, we don’t get any shadowing, since the names areName
andname
. They would only be equal ifA.Name
didn’t have a struct tag, or if we allowed-
as an option like`json:"name,-"`
, as I showed above.Aside from turning
-
into an option, the only other option we have is to always try struct field name shadowing, even if embedded fields have a struct tag field. That is,Name string `json:"-"`
would shadowName string `json:"name"`
as well asX string `json:"Name"`
.I assume this is what you meant by your sentence below:
It’s certainly worth a try, and it could work, but I still wonder if it would break any existing valid uses of
encoding/json
. I have a CL ready, which we can experiment with. If people find it reasonable, we can attempt a merge early in the 1.15 cycle, and revert if any users bring up regressions.I looked into this issue myself and I feel like the observation of @ainar-g is valid. The reason for this is the following:
In the documentation of encoding/json the section explaining
"-"
is directly following the section about",omitempty"
and there it says, that a field is omitted, if it contains the zero value:Now, if we extend the examples provided above (by @ainar-g and @mvdan) with the
",omitempty"
cases, we see, that in this case the anonymous struct is not considered, even if the field in the “parent” struct is empty (zero value). This is the case regardless of the fact, if the final “name” of the field in the encoded json is defined by the struct field name or the struct tag, the behavior is consistent. See: https://play.golang.org/p/VjMa8H7EI9eTherefore I would argue, that if the “omitting” for
",omitempty"
is always decided based on the value in the “parent” struct, the same should be the case for"-"
, that is, if in the parent struct we have"-"
, this named field (evaluated either by the struct tag or by the struct field name, in that order) is never shown in the final encoded json.Yes, whatever we decide to do, I think it would be good to clarify that the visibility rules apply to the JSON names alone.
If the behaviour isn't changed, can it at least be documented better, so that <del>I could bother @dominikh</del> the community could build static analysis tools to detect such idiosyncrasies?
I’ll leave this open for a week if anyone has more thoughts, or a specific way in which this could be changed without breaking existing valid use cases.
The only idea that comes to mind that would be truly backwards-compatible would be to extend the json tag syntax to allow omitting a field name, not just a specific field only. For example,
`json:"name,-"`
to omit any embedded field with`json:"name"`
."-"
already takes the place of a name, so it taking the place of an option doesn’t seem like a terrible idea.The question then would be if it’s worth adding this feature. How often does one want to omit fields from anonymous structs? Are there any current workarounds? Do any external json libraries support this feature?