douyang2530 2019-01-14 04:27
浏览 92
已采纳

根据将对象转换为指针(有时是指针,有时是映射)的时间来改变编组功能

I am implementing some methods for serializing objects (which probably already exists in the standard library, but I want the experience). I was able to get both json.Marshal and json.Unmarshal to correctly convert my data to a string and then read back into an object, but I noticed some interesting behavior that I want to understand better.

To test the functions of the json package, I created a test struct:

type Test struct{
    T int
    S string
    F float32
}

Running

o := Test{32, "struct", 12.07}
b, e := json.Marshal(o)
fmt.Println(string(b), e)

prints

{"T":32,"S":"struct","F":12.07} <nil>

which is what I would expect. However, I am able to get two different results when unmarshalling, depending on when I convert an object to a pointer:

// Test 1
var o2 interface{} = &o
e = json.Unmarshal(b, o2)
fmt.Println(o2, e)

prints

// Output 1
&{32 struct 12.07} <nil>

while defining o2 as a value rather than a pointer and then calling Unmarshal with &o2, ie.

// Test 2
var o2 interface{} = o
e = json.Unmarshal(b, &o2)
fmt.Println(o2, e)

prints

// Output 2
map[T:32 S:struct F:12.07] <nil>

Interestingly enough, all four of

// Test 3
var o2 Test = o
e = json.Unmarshal(b, &o2)
fmt.Println(o2, e)

,

// Test 4
var o2 *Test = &o
e = json.Unmarshal(b, &o2)
fmt.Println(*o2, e)

,

// Test 5
var o2 *Test = &o
e = json.Unmarshal(b, o2)
fmt.Println(o2, e)

, and

// Test 6
var o2 *Test = &o
e = json.Unmarshal(b, &o2)
fmt.Println(o2, e)

print one of the following:

// Output 3, 4
{32 struct 12.07} <nil>

// Output 5, 6
&{32 struct 12.07} <nil>

Tests 1 and 2 lead me to believe that some runtime information is lost when something is assigned to a "superclass"... it appears that assigning a pointer to my struct to o2 gives the Unmarshal function more type data about what it should be reading than does assigning my struct to o2 and then passing a pointer to o2 to Unmarshal. This makes a little bit of sense; at compile-time, Test 1 passes an interface{} to Unmarshal, while Test 2 passes a pointer to an interface{}. Even still, I would have thought that the runtime type of the argument would have been the same (a *Test). Can someone explain why Go works this way?

Supporting my conclusion is the fact that declaring o2 as either a Test or a *Test allows Unmarshal (which thus receives a *Test as its argument) to always read my data as a struct rather than a map.

Strangely, Test 4 and Test 6 show that passing a pointer to a pointer to my struct is totally acceptable, and Unmarshal correctly(?) sets the value of the struct. I would have expected a runtime error for those tests, as Unmarshal should have tried to dereference the double pointer and set the resulting pointer to the serialized struct it was reading. Automatic multi-pointer dereferencing might just be a feature of this function, but it wasn't mentioned in the official documentation. I'm not too concerned about these latter tests. I'm mostly just interested in what is going on to cause the difference between Test 1 and Test 2.

**Edited 14 Jan 2018 to change second json.Marshal to json.Unmarshal, remove improper brackets from copy/pasted code

  • 写回答

1条回答 默认 最新

  • dourao1877 2019-01-14 06:07
    关注

    The unmarshal function walks through pointers and interfaces containing pointer values to find the destination value. Non-pointer values in interfaces are ignored because these values are not addressable. (This description omits details that are unimportant to the question).

    If the destination is an interface{} and the JSON is an object, then the JSON is unmarshaled to a map[string]interface{} and that value is stored in the interface. (This is described in the json.Unmarshal documentation).

    Test 1: The argument to unmarshal is *Test. The unmarshal function walks through the pointer and decodes to the Test struct.

    Test 2: The argument to unmarshal is pointer to interface{} containing a Test. The unmarshal function walks through the pointer to get an interface{}. The Test value in the interface{} is ignored because it's not addressable. Because the destination is an interface{} and the JSON is an object, the JSON is decoded to a map[string]interface{}.

    The code in 3, 4, 5 and 6 does not compile. I'll proceed assuming that the {} following Test is removed.

    var o2 Test = o // 3
    var o2 *Test = &o // 4
    var o2 *Test = &o // 5
    var o2 *Test = &o // 6
    

    Test 3 and 5: The argument to unmarshal is a *Test. This is the same as #1.

    Test 4 and 6: The argument is a **Test. The unmarshal function walks through the pointers and decodes to the Test struct.

    Because Go does not have any anything like a "superclass", that's not the issue.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 无线电能传输系统MATLAB仿真问题
  • ¥50 如何用脚本实现输入法的热键设置
  • ¥20 我想使用一些网络协议或者部分协议也行,主要想实现类似于traceroute的一定步长内的路由拓扑功能
  • ¥30 深度学习,前后端连接
  • ¥15 孟德尔随机化结果不一致
  • ¥15 apm2.8飞控罗盘bad health,加速度计校准失败
  • ¥15 求解O-S方程的特征值问题给出边界层布拉休斯平行流的中性曲线
  • ¥15 谁有desed数据集呀
  • ¥20 手写数字识别运行c仿真时,程序报错错误代码sim211-100
  • ¥15 关于#hadoop#的问题