如何忽略一个结构体(包含嵌套结构体)的MarshalJSON实现?

huangapple go评论89阅读模式
英文:

How to ignore MarshalJSON implementation of a struct (with nested structs)?

问题

可以忽略自定义的MarshalJSON实现,而只使用标准的编组函数吗?

这个结构体很复杂,有很多嵌套的结构体,它们都使用自定义的MarshalJSON,我想忽略它们。

我觉得这应该很简单。你有什么想法吗?

一些细节

显而易见的解决方案是创建一个新类型,但这并不适用于嵌套的结构体,它们仍然使用它们自己的MarshalJSON

这是代码的一个示例:

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

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

还有很多属性(如Name等),我希望在我的JSON中看到它们(对于Properties和其他嵌套的结构体也是如此)。

这段代码的Python实现提供了这些数据,我的软件使用它,我(将代码移植到Go)也希望能够从我的Go程序中导出这些数据。

英文:

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.

答案1

得分: 2

你可以通过两种方式来实现:

  • 自定义类型(隐藏MarshalJSON方法);
  • 自定义编组器(在运行时使用reflect忽略任何MarshalJSON方法)。

自定义类型

例如,考虑以下嵌套类型:

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

func (x *X) MarshalJSON() ([]byte, error) { return []byte(`"不想要这个"`), nil }
func (y *Y) MarshalJSON() ([]byte, error) { return []byte(`"绝对不想要这个"`), nil }

为了避免调用不需要的MarshalJSON方法,需要创建这些类型的影子类型:

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

//
// 将原始的 'x' 转换为使用我们的影子类型
//
x2 := shadowX{
    Name: x.Name,
    Y:    shadowY(x.Y),
}

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

反射

下面是一个基于reflect的简单JSON编组器,可以实现你想要的效果。它假设所有自定义编组器都使用指针接收器,并对指针进行解引用,以便标准库的json.Marshal不会"看到"它们:

func MyJSONMarshal(v interface{}) (bs []byte, err error) {
    k := reflect.TypeOf(v).Kind() // 指针还是非指针?

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

    // 解引用指针
    v2 := reflect.ValueOf(v).Elem().Interface()
    return MyJSONMarshal(v2)
}

使用此方法时,请注意你的情况可能会有所不同

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

英文:

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

huangapple
  • 本文由 发表于 2022年3月2日 22:58:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/71324812.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定