-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
support custom JSON (de)serialization by delegating to message if it implements json.Marshaler/json.Unmarshaler #325
support custom JSON (de)serialization by delegating to message if it implements json.Marshaler/json.Unmarshaler #325
Conversation
I had some discussion with @zombiezen on the approach for this. We have a concern with the use of json.Marshaler or json.Unmarshaler as interfaces for doing custom serialization or deserialization because the jsonpb.Marshaler (or Unmarshaler) options would not be passed on and hence ignored. This means that if a dynamic message contains other non-dynamic proto messages or WKT, it would not be able to consistently use the same options in (de)serializing those fields/subfields. How about defining the following interfaces?
Let me know if you're amenable to our suggestion. Thanks! |
Sure, that sounds good. I can probably make the changes this weekend. I'll ping you after I've pushed them. |
…implements json.Marshaler/json.Unmarshaler
…nmarshaler settings
4ba9ac5
to
e8c65fd
Compare
@cybrcodr, the suggested changes were quite minimal. So the patch is still nice and small. FYI, when I tried to run the tests, one of them failed. But it's not my change as it also fails when I roll my commits back (e.g. it fails on master). Maybe some sort of CI tests should be configured to run against every pull request, to prevent such regressions? Here's the test output:
|
I suspect you are using go 1.7 and not go 1.8?
…On Mon, 22 May 2017, 16:57 Joshua Humphries, ***@***.***> wrote:
@cybrcodr <https://github.com/cybrcodr>, the suggested changes were quite
minimal. So the patch is still nice and small.
FYI, when I tried to run the tests, one of them failed. But it's not my
change as it also fails when I roll my commits back (e.g. it fails on
master). Maybe some sort of CI tests should be configured to run against
every pull request, to prevent such regressions? Here's the test output:
jsonpb_test.go:570: Any with message: can't unmarshal nested Any proto: json: cannot unmarshal string into Go value of type bool
jsonpb_test.go:570: Any with message and indent: can't unmarshal nested Any proto: json: cannot unmarshal string into Go value of type bool
FAIL github.com/golang/protobuf/jsonpb 0.012s
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#325 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ABvsLaUt2ubYbJigtMqCGklSyaP6bcu6ks5r8aI_gaJpZM4MvH7M>
.
|
@awalterschulze correct. Should this project require 1.8? We are still using 1.7 in our prod environments (and we also have GAE code [standard runtime] code that uses protobufs and runs on Go 1.6). |
Thats a question for the maintainers of this library. I am also interested in the answer. |
// also implement JSONPBUnmarshaler so that the custom format can be | ||
// parsed. | ||
type JSONPBMarshaler interface { | ||
MarshalJSONPB(*Marshaler) ([]byte, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cybrcodr, I wasn't sure if this struct could ever become stateful, so I have it passed by ref. Should this instead be pass-by-value? I know it currently just represents marshaling options; but it seemed like, from the name, it could at some point change to such that a pointer is the correct way to pass it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(same goes for passing Unmarshaler
by pointer in the other interface)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pass by ref sounds good.
I've discussed with @dsnet regarding Go versions. We'd like to support at least the latest 1.6 for now (till Go AppEngine default has been upgraded). I've filed issue #356 to fix these tests. I don't know why you're seeing different errors though, but we'll investigate further. And yes, we agree that we need to set up CI for this project #357. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add unit tests for these.
Current TestUnmarshaling uses comparison of text proto, which should be changed to use proto.Equal instead. Anyways, I'm not sure if you can use that for testing your changes, if not, either change the test to use proto.Equal (which I'm thinking of doing anyways) or write a separate test for it and I'll clean up later.
Thanks!
// also implement JSONPBUnmarshaler so that the custom format can be | ||
// parsed. | ||
type JSONPBMarshaler interface { | ||
MarshalJSONPB(*Marshaler) ([]byte, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pass by ref sounds good.
jsonpb/jsonpb.go
Outdated
@@ -528,6 +553,9 @@ func (u *Unmarshaler) UnmarshalNext(dec *json.Decoder, pb proto.Message) error { | |||
if err := dec.Decode(&inputValue); err != nil { | |||
return err | |||
} | |||
if jsu, ok := pb.(JSONPBUnmarshaler); ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about moving this inside unmarshalValue instead? And I think it may need to be after block that allocates memory for pointer fields, i.e. 597-600.
You can do type assertion using ...
if jsu, ok := target.Addr().Interface().(JSONPBUnmarshaler); ok { ... }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch
@cybrcodr: I addressed your comments. I did not change the existing unmarshal test: simplest way to test customized marshaling/unmarshaling is a hand-written message, which won't work with (Also, my current environment is Go 1.7, in which the PTAL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Just minor nits. Thanks!
jsonpb/jsonpb_test.go
Outdated
|
||
// Implements protobuf.Message but is not a normal generated message type. Provides | ||
// implementations of JSONPBMarshaler and JSONPBUnmarshaler for JSON support. | ||
type notGeneratedMessage struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional. How about just naming this as dynamicMessage?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
jsonpb/jsonpb_test.go
Outdated
t.Errorf("an unexpected error occurred when marshalling JSONPBMarshaler: %v", err) | ||
} | ||
if str != rawJson { | ||
t.Errorf("Marshalling JSON produced incorrect output\nExpected: %s\nGot: %s", rawJson, str) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of nits.
(1) Start with lowercase for error message for consistency, i.e. "marshalling ...".
(2) display got first before want.
"marshalling JSON produced incorrect output: got %s want %s"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@cybrcodr - hopefully it's ready to merge now Thanks for reviewing! |
Fyi. I just discovered issue #360 related to this. Let me know if you're interested in providing a fix, else, I'll try to give it a shot next week. Thanks. |
This is achieved by delegating to the message if it implements
json.Marshaler
orjson.Unmarshaler
.FWIW, this could simplify how the well-known types are handled (e.g. no need to special-case them in this package).
Both the binary and text encodings support the same concept, where a message can implement a particular interface (
proto.Marshaler
/proto.Unmarshaler
,encoding.TextMarshaler
/encoding.TextUnmarshaler
) to customize serialization/de-serialization. This just extends that same concept to JSON encoding/decoding.