Skip to content
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: make "json" tag user-settable #7337

Closed
gopherbot opened this issue Feb 16, 2014 · 4 comments
Closed

encoding/json: make "json" tag user-settable #7337

gopherbot opened this issue Feb 16, 2014 · 4 comments

Comments

@gopherbot
Copy link

by beatgammit:

I have a working example of what I'd like to achieve here:
https://github.com/beatgammit/freddy. TL;DR: I added UnmarshalTag(b []byte, v
interface{}, tags ...string) and MarshalTag(v interface{}, tags ...string).

The problem I'm trying to solve is using the same struct for multiple interfaces. I'm
using JSON in my database and I want all fields stored there, but I don't want to expose
all fields via my API. For example:

    type User struct {
        Username string `json:"username"`
        // never exposed to users, but needs to be stored in the db
        Password string `db:"password" json:"-"`
        Logins int `admin:"logins" db:"logins" json:"-"`
    }

Then, when I marshal data for the API, I just do:

    tags := []string{"json"} // or have "json" always work
    if isAdmin {
        tags = append(tags, "admin")
    }
    json.MarshalTag(user, tags...)

For the database:

    json.MarshalTag(user, "db", "json")

Et cetera. My current implementation picks the first match.

I don't think there's a huge performance penalty to this, it's a backwards compatible
change and I've found it to be very useful.

I can submit a patch-set for tip if this feature is accepted. If the general idea is
accepted but the API isn't, I can bring it up on the ML for bikeshedding.
@vdobler
Copy link
Contributor

vdobler commented Feb 20, 2014

Comment 1:

Struct tags are nice but I think this overuses them a bit.
Custom JSON marshalling can be done by implementing
the Marshaler interface already. Why not
type User struct { /* whatever */ }
type APIUser User
func (u APIUser) MarshalJSON() ([]byte, error) {
    // Manually marshal only the fields you want to expose. 
}
u := User{}
json.Marshal(u) // for all fields
json.Marshal(APIUser(u)) // just some fields

@rsc
Copy link
Contributor

rsc commented Mar 3, 2014

Comment 2:

I don't believe this is a road I want to go down. Yes it's useful but it's getting to be
a bit much in the tags.

Status changed to WorkingAsIntended.

@gopherbot
Copy link
Author

Comment 3 by beatgammit:

What about adding a field "Tag string" to Decoder such that I can choose which tag gets
used for marshalling? The current behavior uses the magic string "json", and it would be
nice to be able to change this. This would give the nice behavior without promoting use
of multiple tags by having it be on an exported function.
The problem with the prior suggestion of using MarshalJSON() is that *every* type that
embeds a type that implements it would have to implement it as well:
http://play.golang.org/p/B5W8uMs3bA
I understand the opposition to polluting tags, but perhaps there's a way to go mid-way?
If this idea isn't acceptable either, perhaps an interactive marshal would work (kind of
like flag's Visit)? This would allow doing something like MarshalJSON, but on a
per-field basis.

@gopherbot
Copy link
Author

Comment 4 by google@0xc0dedbad.com:

> What about adding a field "Tag string" to Decoder such that I can choose which tag
gets used for marshalling? The current behavior uses the magic string "json", and it
would be nice to be able to change this. This would give the nice behavior without
promoting use of multiple tags by having it be on an exported function.
I'd really love to be able to pass a tag string to the encoder/decoder to make it easy
to input/output different representations of a struct.  It keeps all representations in
a single authoritative location, and allows use of a consistent, easily understood
method for doing so, for a pretty trivial change.

@golang golang locked and limited conversation to collaborators Jun 25, 2016
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants