Golang – 在具有空值的指针上进行客户端解组/组合

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

Golang - Customer Unmarshaler/Marshaler on pointer with nil/null value

问题

Golang - 在具有空指针/空值的指针上实现自定义的Unmarshaler/Marshaler

我正在尝试在指针类型上实现自定义的UnmarshalJSONMarshalJSON,但是当JSON数据为null/nil时,这些函数都不会被调用,就像下面的示例中一样:

package main

import (
    "encoding/json"
	"fmt"
)

type A struct {
	B *B `json:"b,omitempty"`
}

type B int

// 仅用于在调用`fmt.Println`时显示值而不是指针地址
func (b *B) String() string {
	if b == nil {
		return "nil"
	}

	return fmt.Sprintf("%d", *b)
}

// 当JSON数据包含null而不是数字值时,此函数不会被触发
func (b *B) UnmarshalJSON(data []byte) error {
	fmt.Println("UnmarshalJSON on B called")

	var value int
	if err := json.Unmarshal(data, &value); err != nil {
		return err
	}

	if value == 7 {
		*b = B(3)
	}

	return nil
}

// 当`B`是指针类型且具有`nil`值时,此函数不会被触发
func (b *B) MarshalJSON() ([]byte, error) {
	fmt.Println("MarshalJSON on B called")

	if b == nil {
		return json.Marshal(0)
	}

	if *b == 3 {
		return json.Marshal(7)
	}

	return json.Marshal(*b)
}

func main() {
	var a A

	// 这不会调用`UnmarshalJSON`
	json.Unmarshal([]byte(`{ "b": null }`), &a)
	fmt.Printf("a: %+v\n", a)

	// 这不会调用`MarshalJSON`
	b, _ := json.Marshal(a)
	fmt.Printf("b: %s\n\n", string(b))

	// 这将调用`UnmarshalJSON`
	json.Unmarshal([]byte(`{ "b": 7 }`), &a)
	fmt.Printf("a: %+v\n", a)

	// 这将调用`MarshalJSON`
	b, _ = json.Marshal(a)
	fmt.Printf("b: %s\n\n", string(b))
}

输出:

a: {B:nil}
b: {}

UnmarshalJSON on B called
a: {B:3}
MarshalJSON on B called
b: {"b":7}

我的问题是:

  • 为什么在指针类型上,当值为null/nil时,UnmarshalJSON/MarshalJSON不会被调用?
  • 我们如何在数据为null/nil且类型为指针时,每次都调用UnmarshalJSON/MarshalJSON,而不是在A类型上实现UnmarshalJSON/MarshalJSON并从A级别修改b属性?
英文:

Golang - Customer Unmarshaler/Marshaler on pointer with nil/null value

I'm trying to implement custom UnmarshalJSON and MarshalJSON on pointer type, however none of this function is called when data from json is null/nil like in example below:

package main

import (
    "encoding/json"
	"fmt"
)

type A struct {
	B *B `json:"b,omitempty"`
}

type B int

// Only for displaying value instead of
// pointer address when calling `fmt.Println`
func (b *B) String() string {
	if b == nil {
		return "nil"
	}

	return fmt.Sprintf("%d", *b)
}

// This function is not triggered when json
// data contains null instead of number value
func (b *B) UnmarshalJSON(data []byte) error {
	fmt.Println("UnmarshalJSON on B called")

	var value int
	if err := json.Unmarshal(data, &value); err != nil {
		return err
	}

	if value == 7 {
		*b = B(3)
	}

	return nil
}

// This function is not triggered when `B`
// is pointer type and has `nil` value
func (b *B) MarshalJSON() ([]byte, error) {
	fmt.Println("MarshalJSON on B called")

	if b == nil {
		return json.Marshal(0)
	}

	if *b == 3 {
		return json.Marshal(7)
	}

	return json.Marshal(*b)
}

func main() {
	var a A

	// this won't call `UnmarshalJSON`
	json.Unmarshal([]byte(`{ "b": null }`), &a)
	fmt.Printf("a: %+v\n", a)

	// this won't call `MarshalJSON`
	b, _ := json.Marshal(a)
	fmt.Printf("b: %s\n\n", string(b))

	// this would call `UnmarshalJSON`
	json.Unmarshal([]byte(`{ "b": 7 }`), &a)
	fmt.Printf("a: %+v\n", a)

	// this would call `MarshalJSON`
	b, _ = json.Marshal(a)
	fmt.Printf("b: %s\n\n", string(b))
}

output:

a: {B:nil}
b: {}

UnmarshalJSON on B called
a: {B:3}
MarshalJSON on B called
b: {"b":7}

My questions are:

  • why UnmarshalJSON/MarshalJSON is not called with null/nil value on pointer type
  • how we can call UnmarshalJSON/MarshalJSON everytime when data is null/nil and type is a pointer instead of implementing UnmarshalJSON/MarshalJSON on A type and modify b property from level of A

答案1

得分: 1

简而言之

目前,在解组/组合一个Go结构体时,只会发出非零字段,因为nil指针是Go中的零值之一,因此在这种情况下不会调用UnmarshalJSON/MarshalJSON


此外,似乎有一些相关的提案

然而,目前还没有解决方案来解决这个问题。


根据代码 解组器

> 解组器将UnmarshalJSON([]byte("null"))实现为无操作

// 按照约定,为了近似Unmarshal本身的行为,
// 解组器将`UnmarshalJSON([]byte("null"))`实现为无操作。
type Unmarshaler interface {
	UnmarshalJSON([]byte) error
}
英文:

For Short

Currently, unmarshaling/marshaling a Go struct will emit only the non-zero fields, since nil pointer is one zero value in Go, the UnmarshalJSON/MarshalJSON is not called in this case.


Also, it seems there are some related proposals

However, there is no solution to resolve it now.


Per code Unmarshalers

> Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op

// By convention, to approximate the behavior of Unmarshal itself,
// Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op.
type Unmarshaler interface {
	UnmarshalJSON([]byte) error
}

huangapple
  • 本文由 发表于 2022年9月30日 03:59:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/73901013.html
匿名

发表评论

匿名网友

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

确定