encode/json 的Marshal函数对于指针和对象返回得到结果不一样

```go package main import ( "encoding/json" "fmt" ) type X struct { B json.RawMessage `json:"b"` } func main() { x := X{B: []byte(`{"test":"t"}`)} b, err := json.Marshal(x) if err != nil { fmt.Println(err.Error()) return } fmt.Printf("%s\n", b) b, err = json.Marshal(&x) if err != nil { fmt.Println(err.Error()) return } fmt.Printf("%s\n", b) return } ``` 代码如上,结果为 {"b":"eyJ0ZXN0IjoidCJ9"} {"b":{"test":"t"}} 为什么会不同呢
已邀请:

傅小黑

赞同来自: 九命猫 niugou

https://groups.google.com/forum/#!topic/Golang-Nuts/38ShOlhxAYY

http://stackoverflow.com/questions/19145202/marshal-of-json-rawmessage

`*json.RawMessage` 的行为才会保持,否则就当作 []byte 了

stevewang

赞同来自: 九命猫 niugou

这个问题实际上取决于`T.B`这个值是否可以取地址,即`func (reflect.Value) CanAddr() bool`的返回值。

规则是:如果`T.B`的值可以取址,因为`RawMessage`实现了`json.Marshaler`接口,因此调用`RawMessage.Marshal`函数,该函数的实现是直接返回原生`[]byte`;如果`T.B`不能取址,则当作原生`[]byte`处理,编码成base64字符串。

那么问题又来了:什么时候可以取址,什么时候不能取址呢?

1.struct中的成员类型如果是`*[]byte`并且值非空,那么总是可以取址的。

2.struct中的成员类型如果是`[]byte`且值非空,那么是否可以取址则取决于struct对象是否可以取址。在原文程序中,第一种情况中传给`json.Marshal`函数的struct对象拷贝是不能取址的,第二种情况中函数参数是struct指针,因此是可以取址的。

结论:
如果希望输出结果是`{"b":{"test":"t"}}`,那么要么调用`json.Marshal(&x)`,或者把`X.B`的类型改为指针类型`*json.RawMessage`。

注:是否可以取址可以用以下实验程序验证:
```go
package main

import (
"reflect"
"fmt"
)

type T1 struct {
B []byte
}

type T2 struct {
B *[]byte
}
func Check(i interface{}) {
v := reflect.ValueOf(i)
t := reflect.TypeOf(i)
if v.Kind() == reflect.Ptr {
v = v.Elem()
t = t.Elem()
}
count := t.NumField()
for i := 0; i < count; i++ {
fv := v.Field(i)
ft := t.Field(i).Type
if fv.Kind() == reflect.Ptr {
fv = fv.Elem()
ft = ft.Elem()
}
fmt.Printf("[%d]type=%v CanAddr=%v\n", i, ft, fv.CanAddr())
}
}

func main() {
b := []byte{1, 2, 3}
var t1 = T1{
B: b,
}
var t2 = T2{
B: &b,
}
Check(t1)
Check(&t1)
Check(t2)
Check(&t2)
}
```

输出:
```go
[0]type=[]uint8 CanAddr=false
[0]type=[]uint8 CanAddr=true
[0]type=[]uint8 CanAddr=true
[0]type=[]uint8 CanAddr=true
```

stevewang

赞同来自: 九命猫 niugou

补充说明一下:上面我说到`X.B`要能取址才能执行`RawMessage.Marshal`,是因为是`*json.RawMessage`而不是`json.RawMessage`实现了`json.Marshaler`接口。
因此,我们如果自己实现`json.Marshaler`接口,就没有取址的限制了。
```go
package main

import (
"encoding/json"
"fmt"
)

type RawMessage []byte

func (this RawMessage) MarshalJSON() ([]byte, error) {
return []byte(this), nil
}

type X struct {
B RawMessage `json:"b"`
}

func main() {
x := X{B: []byte(`{"test":"t"}`)}
b, err := json.Marshal(x)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Printf("%s\n", b)
}
```
输出:
```go
{"b":{"test":"t"}}
```

要回复问题请先登录注册