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代码补全插值
  • ¥15 Typegoose 中如何使用 arrayFilters 筛选并更新深度嵌套的子文档数组信息
  • ¥15 前后端分离的学习疑问?
  • ¥15 stata实证代码答疑
  • ¥50 husky+jaco2实现在gazebo与rviz中联合仿真
  • ¥15 dpabi预处理报错:Error using y_ExtractROISignal (line 251)
  • ¥15 在虚拟机中配置flume,无法将slave1节点的文件采集到master节点中
  • ¥15 husky+kinova jaco2 仿真
  • ¥15 zigbee终端设备入网失败
  • ¥15 金融监管系统怎么对7+4机构进行监管的