-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
proposal: encoding/json: new MarshalAppender interface to make custom marshallers more efficient #34701
Comments
cc @mvdan |
Regarding the validation and compaction that is applied to the JSON data returned by |
@wI2L could you expand on why it's necessary? Folk can always validate their JSON themselves using json.Valid. Is there a history of people producing bad marshallers that this was introduced to address? |
@philpearl AFAIK, there is no mention of this behavior in the documentation, neither in |
When I read it for a first time I tough, that is a bright idea. However I changed my mind.
|
I'm surprised that this already has over thirty thumbs up after being posted just over a week ago. Have there been discussions about this proposal elsewhere? While I agree that the current interface has some performance limitations, I'm not convinced that this is a change we want to make right now. It would make the json package significantly more complex, and there are other aspects to performance, such as the validation and compaction that @wI2L brings up. There are a large number of proposals that want to significantly change the json package. If we accepted even a small portion of them, the package's API would quickly double in size and become unmanageable. This is why many of them are under the I think this proposal falls under the same category. The design choices that make Until large design refactors happen at some point in the future, I'd personally recommend looking at third-party json packages with performance at the center of their design. For example, there are many that use code generation. |
@mvdan there's a blog post about this reposted on reddit https://www.reddit.com/r/golang/comments/ddqpbt/bad_go_adventures_with_json_marshalling/ That's how I personally found out about this proposal. |
Oh god someone put in on reddit. Save me from the comments! |
One reason I'm pursuing this is that I've tried to change a large codebase to use a code generation based JSON library (easyjson) and there are issues with doing so, including the following.
In my head if there are easy wins that improve the standard JSON library performance (and I'm more concerned with garbage than straight-line speed) then that's a much nicer path for folk who run into performance issues than a lift-and-shift to a whole new JSON library. I'm also struct by the irony that the main use for custom marshallers (other than time.Time & json.RawMessage) appears to be for building nullable values without using pointers - presumably to try to avoid allocations. I liked the idea of this change because it enables improvements without impacting the normal user much if at all. Surely not many folk write custom marshallers, and those that do can I hope deal with a little extra complication? |
At this point, new JSON features should probably be developed in 3rd-party packages; forking the current encoding/json to start is fine. The fact is that encoding/json simply cannot accommodate every last feature every user will want. We should treat the API as basically frozen, I think. |
One small problem with doing this externally is that we can't add new marshalling methods for time.Time and (much weaker argument) json.RawMessage. That said, these could be special-cased in a fork. |
Why not simply patch your stdlib, and avoid duplicating any code? |
Given that this can be explored in 3rd-party packages easily, this seems like a likely decline. |
Curious to know what a successful exploration would look like, and what the consequences of success would be. Would also be interested in any advice as to how to go about such an exploration - my instinct is that any exploration would be just me, and therefore the impact would be precisely zero (as I'm already convinced). |
One consequence of writing your own package is that you could use it and would not need the change in the standard library. If others also found it useful (perhaps by searching https://pkg.go.dev/search?q=json), they could use it too. In general we can't add every possible new feature to the standard library, especially encoding/json and encoding/xml. Third-party alternatives are an important part of a healthy ecosystem. |
No change in consensus, so declining. |
OK, so "can be explored in 3rd-party packages easily" does not imply a mechanism to eventually apply the change back to core if the "exploration" proved successful. Ah well. Will contemplate creating my own special JSON library. Only I would love it, but perhaps that's enough? |
@philpearl if a feature in an alternative json package works well and is very popular, we could always reconsider, but I imagine its chances are low if it trades API complexity for performance. If you're thinking in the long term, what I think is more interesting would be a v2 redesign of |
More thoughts on this. https://philpearl.github.io/post/json_own_way/ |
For those interested, jettison v0.5.0 features a |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes. And with the head of the master branch.
What operating system and processor architecture are you using (
go env
)?go env
OutputProblem
If you marshal a struct with time.Time field in it there are additional allocations per time field. There appear to be 3 reasons for this.
These inefficiencies exist for any type that implements MarshalJSON, but time.Time is a big issue in code I've been involved with.
Proposal
I think we can remove the first 2 of these by creating a new interface that follows the append pattern and trusts implementations to create compact valid JSON.
I think an "Append" model is preferable to using io.Writer as the standard library has many append methods, e.g. time.Time has AppendFormat, and there are many Append functions in strconv. I suspect that means an interface following the append pattern will be less surprising and easier to use efficiently.
I've knocked together a prototype implementation with the following results on a simple benchmark (interestingly I can't find a benchmark that covers using a custom marshaler in the existing codebase).
I've be very pleased to attempt to contribute this if the proposal is accepted.
Apologies if I've not followed the proposal process properly - I don't know what I'm doing!
The text was updated successfully, but these errors were encountered: