6
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

1
  • I would use reflection with a custom marshal function. added an answer. Commented May 18, 2021 at 15:19

6 Answers 6

7

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.

Sign up to request clarification or add additional context in comments.

11 Comments

I need something that can be used in general. I am working with a lot of types that face this problem!
@NiranjanShetty: I'm not aware of a built-in options to do this. Adding some more details to the answer
Why would you want to import something from json, but not translate it back to json?
do you mean something like func MarshalJSON(out interface{}, fieldsTobeIgnored ..interface{}){} can you improve the function if so?
@ChenA. I have an object field & and id field, object already has id so I don't wish to send it again!
|
3

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.)

1 Comment

Yes, I am aware of this practice, wasn't sure we do this in go/go-gorm Besides, as you said it will be a pain to do this mid project with 300-500 structs
2

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
}

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
0

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.

1 Comment

Unfortunately, as you're discovering with your edits, to make a really robust function you'll end up having to rewrite much of json.Marshal eventually :)
0

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"`
}

1 Comment

This doesn't work for me, the fields declared in Beta remains nil. In fact the whole type is empty for me
0

This is YEARS late, but might help the next person.

You can use omitzero and use a type alias. This will allow you to READ the value, but you wont WRITE the value.

// ReadOnly is a generic struct that can be used to create read-only types.
type ReadOnly string

func (r ReadOnly) IsZero() bool {
    return true
}

type Foo struct {
    Bar ReadOnly `json:"bar,omitzero"`
}

Playground link:
https://go.dev/play/p/oSIuQngIRHj

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.