1

Is it possible to ignore a custom MarshalJSON implementation of a struct, and use just standard marshaling function instead?

The struct is complex, and has a lot of nested structs, all of which are using custom MarshalJSON, and I would like to ignore them all.

I feel that it should be trivial. Do you have an idea?

Some details

An obvious solution with a new type creation does not work well, because the nested structs still use their MarshalJSONs.

Here is an example of the code:

func (de DeploymentExtended) MarshalJSON() ([]byte, error) {
    objectMap := make(map[string]interface{})
    if de.Location != nil {
        objectMap["location"] = de.Location
    }
    if de.Properties != nil {
        objectMap["properties"] = de.Properties
    }
    if de.Tags != nil {
        objectMap["tags"] = de.Tags
    }
    return json.Marshal(objectMap)
}

(source: https://github.com/Azure/azure-sdk-for-go/blob/v62.0.0/services/resources/mgmt/2020-10-01/resources/models.go#L366)

And there are a lot of properties (like Name, etc), which I would like to see in my JSON (the same for Properties and other nested structs).

The Python implementation of this code provides that data, my software use it, and I (porting the code to Go) would like to be able to export these data from my Go program too.

7
  • 2
    "I feel that it should be trivial" - it is not. There is no option to ignore a type's explicitly-defined JSON methods. You've found the only viable solution, using a new type, but indeed it will get quite messy with nested types. If this is something you're finding you need to do, it's likely that those types shouldn't have MarshalJSON defined on them to begin with. Commented Mar 2, 2022 at 15:28
  • 1
    @Adrian: but it is really strange, in my opinion. Some structs have public data, and I can't really access it in some standard fashion. Basically, the only thing I need is to somehow dump them to some maps or whatever. I wonder why nobody wanted to it before... Commented Mar 2, 2022 at 16:04
  • 3
    Can you update the question with an example snippet of a top-level & nested type? It may be tedious, but working bottom up, it's not that difficult to redefine new types to prevent the original types' MarshalJSON logic. Commented Mar 2, 2022 at 16:13
  • 2
    Your types implement MarshalJSON() because they implement how they should be marshaled into JSON. If this isn't what you want, then either the current implementations of MarshalJSON() should be changed, or you shouldn't use the values of these types to begin with. There are no multiple variants of JSON marshaling. If you need different logic, you need different types (with different logic implemented). Commented Mar 2, 2022 at 16:15
  • 1
    @IgorChubin you could fork the dependency and change/drop the custom marshaler implementations. Or perhaps look for non-standard json marshalers that provide you with the features you are looking for. The standard library can't do what you want. Commented Mar 2, 2022 at 17:19

1 Answer 1

2

You can do this two ways:

  • custom types (to hide the MarshalJSON methods); or
  • custom marshaler (using reflect to ignore any MarshalJSON methods at runtime)

Custom Types

For example, take these nested types:

type Y struct {
    FieldZ string
}
type X struct {
    Name string
    Y    Y
}


func (x *X) MarshalJSON() ([]byte, error) { return []byte(`"DONT WANT THIS"`), nil }
func (y *Y) MarshalJSON() ([]byte, error) { return []byte(`"DEFINITELY DONT WANT THIS"`), nil }

one would need to shadow these types to avoid the unwanted MarshalJSON methods from being invoked:

type shadowY struct {
    FieldZ string
}
type shadowX struct {
    Name string
    Y    shadowY
}

//
// transform original 'x' to use our shadow types
//
x2 := shadowX{
    Name: x.Name,
    Y:    shadowY(x.Y),
}

https://go.dev/play/p/vzKtb0gZZov


Reflection

Here's a simple reflect-based JSON marshaler to achieve what you want. It assumes that all the custom marshalers use pointer receivers - and dereferences the pointer so the standard library's json.Marshal will not "see" them:

func MyJSONMarshal(v interface{}) (bs []byte, err error) {
    k := reflect.TypeOf(v).Kind() // ptr or not?

    if k != reflect.Ptr {
        return json.Marshal(v)
    }

    // dereference pointer
    v2 := reflect.ValueOf(v).Elem().Interface()
    return MyJSONMarshal(v2)
}

YMMV with this method.

https://go.dev/play/p/v9YjYRno7RV

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

3 Comments

Yes, that is what I proposed originally, but I had a hope that there is a more elegant approach for that. Let's wait for some time, maybe someone will propose some better solution (or the haters will downvote the question into oblivion); if there is nothing better, I will take this one && thank you for the help
I added some links and infos, just to make it more concrete
Added a generic reflect-based solution.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.