Reputation:
type Alpha struct {
Name string `json:"name"`
SkipWhenMarshal string `json:"skipWhenMarshal"`
}
func MarshalJSON(out interface{}){
json.Marshal(out)
}
Is it possible to ignore the SkipWhenMarshal field when I do json.Marshal but not when I do json.Unmarshal. It should work for any type who calls MarshalJSON
Upvotes: 4
Views: 12115
Reputation: 11
If the structure member variable starts with a lower case, it will not be incuded.
Example:
type Alpha struct {
Name string `json:"name"`
skipWhenMarshal string
}
Upvotes: 1
Reputation: 3
You can try this i.e. breaking the structure into components -> composition
while unmarshalling use Beta type. It will unmarshall only fields that are defined inside Beta struct
type Beta struct {
Name string `json:"name"`
}
type Alpha struct {
*Beta
SkipWhenMarshal string `json:"skipWhenMarshal"`
}
Upvotes: 0
Reputation: 19985
I would write a custom marshal function like so: playground
package main
import (
"encoding/json"
"fmt"
"reflect"
"strings"
)
type Alpha struct {
Name string `json:"name"`
SkipWhenMarshal string `json:"SkipWhenMarshal,skip"`
}
func main() {
var a Alpha
a.Name = "John"
a.SkipWhenMarshal = "Snow"
out, _ := marshal(a)
fmt.Println(string(out))
var b Alpha
json.Unmarshal([]byte(`{"Name":"Samwell","SkipWhenMarshal":"Tarly"}`), &b)
fmt.Println(b)
}
// custom marshaling function for json that accepts an additional tag named skip to ignore the field
func marshal(v Alpha) ([]byte, error) {
m := map[string]interface{}{}
ut := reflect.TypeOf(v)
uv := reflect.ValueOf(v)
for i := 0; i < ut.NumField(); i++ {
field := ut.Field(i)
js, ok := field.Tag.Lookup("json")
if !ok || !strings.Contains(js, "skip") {
intf := uv.Field(i).Interface()
switch val := intf.(type) {
case int, int8, uint8:
m[field.Name] = val
case string:
m[field.Name] = val
}
}
}
return json.Marshal(m)
}
It basically rebuilds the struct as a map without the skipped fields and passes it to the original Marshal function.
Now just add the SKIP tag to any field and it should work.
You just need to improve this part:
switch val := intf.(type) {
Since this only assumes you only have strings or ints as fields. Handling more types would be more ideal.
Upvotes: 0
Reputation: 42413
What you want simply cannot be done with encoding/json.
But you can have two types
type AlphaIn struct {
Name string `json:"name"`
Bar string `json:"skipWhenMarshal"`
}
type AlphaOut struct {
Name string `json:"name"`
Bar string `json:"-"`
}
Use AlphaIn
to deserialise JSON with encoding/json.Unmarshal and use AlphaOut
to serialise a struct with encoding/json.Marshal to JSON.
Now this alone would be absolute painful to work with but: struct tags do not play a role in convertibility between types which lets you convert from AlphaIn to AlphaOut with a simple type conversion:
var a AlphaIn = ...
var b AlphaOut = AlphaOut(a)
(A saner naming scheme would be Alpha
and AlphaToJSON
or soemthing like this.)
Upvotes: 3
Reputation: 273456
Field tag modifiers like "omitempty" and "-" apply to both marshaling and unmarshaling, so there's no automatic way.
You can implement a MarshalJSON
for your type that ignores whatever fields you need. There's no need to implement a custom unmarshaler, because the default works for you.
E.g. something like this:
type Alpha struct {
Id int32
Name string
SkipWhenMarshal string
}
func (a Alpha) MarshalJSON() ([]byte, error) {
m := map[string]string{
"id": fmt.Sprintf("%d", a.Id),
"name": a.Name,
// SkipWhenMarshal *not* marshaled here
}
return json.Marshal(m)
}
You can also make it simpler by using an alias type:
func (a Alpha) MarshalJSON() ([]byte, error) {
type AA Alpha
aa := AA(a)
aa.SkipWhenMarshal = ""
return json.Marshal(aa)
}
Here SkipWhenMarshal
will be output, but its value is zeroed out. The advantage in this approach is that if Alpha
has many fields, you don't have to repeat them.
Upvotes: 5