user6743038 user6743038 - 3 months ago 19
JSON Question

Override struct tags in go

I have an entity in my project that is viewable by public and by admin. Not all fields should be accessible by public.

For example

type Foo struct {
Id bson.ObjectId `json:"id" bson:"_id"`
DateAdded time.Time `json:"date_added" bson:"date_added"`
Bar string `json:"bar" bson:"bar"`
AdminOnly string `json:"admin_only" bson:"admin_only"`
}


AdminOnly field should be only visible to admins.
For now, when requests comes from public, I call separate method that copies every needed field to new struct

type FooPublic struct {
Id bson.ObjectId `json:"id" bson:"_id"`
DateAdded time.Time `json:"date_added" bson:"date_added"`
Bar string `json:"bar" bson:"bar"`
}

func (f *Foo) Public() (res FooPublic) {
res = FooPublic{
Id: f.Id,
DateAdded: f.DateAdded,
Bar:f.Bar,
}
return
}


But if I need to add new field to my entity, I need to add it in 3 places. In struct itself, in PublicFoo and in Public method.
This seems to be agains DRY principle. What is correct, idiomatic solution here? Can I define FooPublic so it overrides tags of neede fields? Or probably there is at least good way to copy corresponding fields from one struct to another, so I don't need to do this manually in Public method?

Answer

In general this repetition can be avoided by using embedding. Your Foo type should embed FooPublic:

type FooPublic struct {
    Id        bson.ObjectId `json:"id" bson:"_id"`
    DateAdded time.Time     `json:"date_added" bson:"date_added"`
    Bar       string        `json:"bar" bson:"bar"`      
}

type Foo struct {
    FooPublic
    AdminOnly string `json:"admin_only" bson:"admin_only"`
}

func (f *Foo) Public() FooPublic {
    return f.FooPublic
}

But if someone is able to call the Foo.Public(), that someone already has the Foo or *Foo value (and so can access the exported AdminOnly field), so what's the point?

A better solution would be to use an interface, and not expose the Foo struct.