-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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
encoding/json: bad encoding of field with MarshalJSON method #22967
Comments
This does seem like surprising behavior. It should either be documented or the encoder will need to jump through some hoops to copy the value so that it can be addressable. |
This issue is more general and affects all custom encoders accepted by encoding.TextMarshaler
json.Marshaler
xml.Marshaler
xml.MarshalerAttr More examples at:
It occurs whenever a type Is the address resolution really necessary? I do not think so. The custom encoder should only be called if properly implemented, as clearly stated by the Go documentation:
The idiomatic and recommended implementation for a custom encoder should be with |
A minimal solution could be: clarify the behavior of each custom encoders by adding that A definitive solution could be: change the documentation and implementation of the encoding process by first checking if the value under encoding is a pointer, i.e. the first step should be:
and, therefore, call a custom encoder only if implemented with |
It seems clear that Encode(&v) should return |
I think that I would prefer returning an error in this case rather than exposing such weird behavior, definitely feels like classic 'undefined behavior'. |
I just got bitten by this. Another option is to have vet check that MarshalJSON uses a value receiver. |
Pointer receivers used with MarshalJSON are code rakes. golang/go#22967 dominikh/go-tools#911 I just stepped on one, and it hurt. Turn them over. Signed-off-by: Josh Bleecher Snyder <josharian@gmail.com>
Pointer receivers used with MarshalJSON are code rakes. golang/go#22967 dominikh/go-tools#911 I just stepped on one, and it hurt. Turn it over. While we're here, optimize the code a bit. name old time/op new time/op delta MarshalJSON-8 184ns ± 0% 44ns ± 0% -76.03% (p=0.000 n=20+19) name old alloc/op new alloc/op delta MarshalJSON-8 184B ± 0% 80B ± 0% -56.52% (p=0.000 n=20+20) name old allocs/op new allocs/op delta MarshalJSON-8 4.00 ± 0% 1.00 ± 0% -75.00% (p=0.000 n=20+20) Signed-off-by: Josh Bleecher Snyder <josharian@gmail.com>
Pointer receivers used with MarshalJSON are code rakes. golang/go#22967 dominikh/go-tools#911 I just stepped on one, and it hurt. Turn it over. While we're here, optimize the code a bit. name old time/op new time/op delta MarshalJSON-8 184ns ± 0% 44ns ± 0% -76.03% (p=0.000 n=20+19) name old alloc/op new alloc/op delta MarshalJSON-8 184B ± 0% 80B ± 0% -56.52% (p=0.000 n=20+20) name old allocs/op new allocs/op delta MarshalJSON-8 4.00 ± 0% 1.00 ± 0% -75.00% (p=0.000 n=20+20) Signed-off-by: Josh Bleecher Snyder <josharian@gmail.com>
Pointer receivers used with MarshalJSON are code rakes. golang/go#22967 dominikh/go-tools#911 I just stepped on one, and it hurt. Turn it over. While we're here, optimize the code a bit. name old time/op new time/op delta MarshalJSON-8 184ns ± 0% 44ns ± 0% -76.03% (p=0.000 n=20+19) name old alloc/op new alloc/op delta MarshalJSON-8 184B ± 0% 80B ± 0% -56.52% (p=0.000 n=20+20) name old allocs/op new allocs/op delta MarshalJSON-8 4.00 ± 0% 1.00 ± 0% -75.00% (p=0.000 n=20+20) Signed-off-by: Josh Bleecher Snyder <josharian@gmail.com>
Ran into this today and made a quiz for this issue: https://play.golang.org/p/8WejDUNc907 Some of the people I asked guessed the correct answer but nobody was able to explain why it works like that (doesn't call I think it should return an error, like Russ is suggesting above. |
Using a pointer receiver for `MarshalJSON` is error-prone since custom marshal logic would only be used if addresssible variable is passed, otherwise the default implementaion is used. See: golang/go#22967
Using a pointer receiver for `MarshalJSON` is error-prone since custom marshal logic would only be used if addressable variable is passed, otherwise the default implementation is used. See: golang/go#22967
* cannon: remove final dep on bindings Removes the last dependency that cannon has on `op-bindings/bindings`. This is done my creating reusable code for reading foundry artifacts from disk. This code should be reusable between any service that wants to read the foundry artifacts from disk. This includes roundtrip tests for JSON serialization of the foundry artifacts. * op-chain-ops: address semgrep golang/go#22967
* cannon: remove final dep on bindings Removes the last dependency that cannon has on `op-bindings/bindings`. This is done my creating reusable code for reading foundry artifacts from disk. This code should be reusable between any service that wants to read the foundry artifacts from disk. This includes roundtrip tests for JSON serialization of the foundry artifacts. * op-chain-ops: address semgrep golang/go#22967
Change https://go.dev/cl/606495 mentions this issue: |
Please answer these questions before submitting your issue. Thanks!
What version of Go are you using (
go version
)?version 1.9
Does this issue reproduce with the latest release?
yes
What operating system and processor architecture are you using (
go env
)?windows/amd64
What did you do?
What did you expect to see?
What did you see instead?
Issue
The
json.Marshal
documentation states that:Thus,
&v
should be marshaled to the same JSON value asv
:Moreover, it states that:
Therefore, Marshal should not call the
MarshalJSON
method to produce JSON for theX
field, because itsT
type does not implement thejson.Marshaler
interface. In fact,MarshalJSON
has*T
receiver and the Go documentation states:As a final remark:
from the source code, the most relevant difference between cases
v
and&v
is thatX
field becomes addressable in case&v
, changing the encoding generated bycondAddrEncoder
;implementing
MarshalJSON
withT
value receiver makesT
ajson.Marshaler
interface, withX
values properly encoded by MarshalJSON:if the intended behavior is that Marshal should anyway call the
MarshalJSON
method on encodingT
values, whenever*T
implements thejson.Marshaler
interface, that should be clearly documented.The text was updated successfully, but these errors were encountered: