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 js,页面2返回页面1时定位进入的设备
  • ¥50 导入文件到网吧的电脑并且在重启之后不会被恢复
  • ¥15 (希望可以解决问题)ma和mb文件无法正常打开,打开后是空白,但是有正常内存占用,但可以在打开Maya应用程序后打开场景ma和mb格式。
  • ¥15 绘制多分类任务的roc曲线时只画出了一类的roc,其它的auc显示为nan
  • ¥20 ML307A在使用AT命令连接EMQX平台的MQTT时被拒绝
  • ¥20 腾讯企业邮箱邮件可以恢复么
  • ¥15 有人知道怎么将自己的迁移策略布到edgecloudsim上使用吗?
  • ¥15 错误 LNK2001 无法解析的外部符号
  • ¥50 安装pyaudiokits失败
  • ¥15 计组这些题应该咋做呀