2017-04-03 92 views
1

我想执行一些额外的步骤来初始化我的实现中的数据结构UnmarshalJSON。在该实现中调用json.Unmarshal(b, type)自然会导致堆栈溢出。在UnmarshalJSON函数内部调用json.Unmarshal而不会导致堆栈溢出

JSON解码器不断尝试查找,如果有自定义的UnmarshalJSON实现,然后再调用json.Unmarshal

是否有另一种方式做到这一点?只需调用底层的默认实现而不造成这种情况?

+1

显示你到目前为止。 –

回答

2

一个简单而常见的方式来避免这种/保护它是创建一个新的类型与所type关键字,并使用类型conversion通过这种类型的值(该值可能是你原来的值,类型转换是可能的,因为新类型具有原始类型作为其基础类型)。

这是可行的,因为type关键字创建一个新类型,而新类型将有零方法(它不会“继承”底层类型的方法)。

这是否会导致一些运行时间开销?号从Spec: Type declarations:

具体规则引用适用于数字类型之间或向和从一个串型(非恒定)的转换。这些转换可能会更改x的表示形式并导致运行时成本。 所有其他转换仅改变类型,但不改变x的表示。

我们来看一个例子。我们有一个Person类型的数字Age,我们要确保Age不能为负(小于0)。

type Person struct { 
    Name string `json:"name"` 
    Age int `json:"age"` 
} 

func (p *Person) UnmarshalJSON(data []byte) error { 
    type person2 Person 
    if err := json.Unmarshal(data, (*person2)(p)); err != nil { 
     return err 
    } 

    // Post-processing after unmarshaling: 
    if p.Age < 0 { 
     p.Age = 0 
    } 
    return nil 
} 

测试它:

var p *Person 
fmt.Println(json.Unmarshal([]byte(`{"name":"Bob","age":10}`), &p)) 
fmt.Println(p) 

fmt.Println(json.Unmarshal([]byte(`{"name":"Bob","age":-1}`), &p)) 
fmt.Println(p) 

输出(尝试在Go Playground):

<nil> 
&{Bob 10} 
<nil> 
&{Bob 0} 

当然同样的方法适用于自定义编组(MarshalJSON())太:

func (p *Person) MarshalJSON() ([]byte, error) { 
    // Pre-processing before marshaling: 
    if p.Age < 0 { 
     p.Age = 0 
    } 

    type person2 Person 
    return json.Marshal((*person2)(p)) 
} 

测试它:

p = &Person{"Bob", 10} 
fmt.Println(json.NewEncoder(os.Stdout).Encode(p)) 
p = &Person{"Bob", -1} 
fmt.Println(json.NewEncoder(os.Stdout).Encode(p)) 

输出(同一Go Playground为例):

{"name":"Bob","age":10} 
<nil> 
{"name":"Bob","age":0} 
<nil> 

一个非常类似的问题是,当你定义为fmt包的自定义文本表示String() string方法,并且要使用您修改的默认字符串表示。阅读更多关于它的地方:The difference between t and *t