Marshal Unmarshal JSON 结构问题导致 REST 请求失败
Marshal Unmarshal JSON struct problems causing REST request to fail
自从 nipuna 解决了问题后,我正在更新问题陈述。这个问题更好地描述为,“为什么解组,然后编组 JSON 导致对 JSON 的更改与 REST API 请求不兼容?”发现的原因是 JSON 数组中的文本字段有时需要包含有时需要省略,而编组数据结构不支持省略它们。该结构确实对这些字段使用了术语“omitempty”,因此可以认为该指令会省略它们,但这还不够。我还需要将文本字段作为指针。原始问题描述如下。 最终的数据结构将“omitempty”用于文本字段,并将匿名结构设为指针。(请参阅已接受的答案)
我有一个 REST 请求在我使用 Go Unmarshal / Marshal 时失败了,但它对文件中的相同 JSON 数据有效。我已经验证,如果我采用那个确切的 []byte 并往返 UnMarshal / Marshal 它,那么读取文件的结果 []byte 是不同的。结果,我的 REST 请求失败了。这是图解法:file json -> []byte = set A of bytes。 file json -> []byte -> UnMarshal -> Marshal -> []byte = 字节集 B。设置字节 A != 设置字节 B。我不在乎,只是我需要修改 Go 结构中的数据,然后对其进行 Marshal,但是我不理解的字节更改导致请求失败。
这里是测试代码:
type SlackRequestData struct {
Blocks []struct {
BlockID string `json:"block_id"`
Elements []struct {
ActionID string `json:"action_id"`
Style string `json:"style"`
Text struct {
Text string `json:"text"`
Type string `json:"type"`
} `json:"text"`
Type string `json:"type"`
Value string `json:"value"`
} `json:"elements"`
Text struct {
Text string `json:"text"`
Type string `json:"type"`
} `json:"text"`
Type string `json:"type"`
} `json:"blocks"`
}
func MarshalFailTest(){
bytes_io, err := ioutil.ReadFile("../../slack/slack-message.json")
if err != nil {
fmt.Printf("Error: %+v\n", err) // handle err
}
new_req := SlackRequestData{}
err = json.Unmarshal(bytes_io, &new_req)
if err != nil {
fmt.Printf("Error: %+v\n", err) // handle err
}
new_json, err := json.Marshal(new_req)
if err != nil {
fmt.Printf("Error: %+v\n", err) // handle err
}
fmt.Printf("New req:\n%+v\n", new_req)
fmt.Printf("New Json:\n%+v\n", new_json)
fmt.Printf("Bytes:\n%+v\n", bytes_io)
}
这里是 json 文件数据:
{
"blocks": [{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Service Is Ready. Now?"
}
},
{
"type": "actions",
"block_id": "deploy_id",
"elements": [{
"type": "button",
"action_id": "yes_button",
"text": {
"type": "plain_text",
"text": "Yes"
},
"style": "danger",
"value": "yes"
},
{
"type": "button",
"action_id": "yes_toll_button",
"text": {
"type": "plain_text",
"text": "Yes To All"
},
"style": "danger",
"value": "yes"
},
{
"type": "button",
"action_id": "no_button",
"text": {
"type": "plain_text",
"text": "No"
},
"style": "primary",
"value": "no"
},
{
"type": "button",
"action_id": "no_toall_button",
"text": {
"type": "plain_text",
"text": "No To All"
},
"style": "primary",
"value": "no"
}]
}]
}
请注意,我使用了多个转换器来获取文件 json 并创建了 SlackRequestData 结构,并且 UnMarshal 似乎工作得很好,因为调试器中的结构将所有数据都放在了正确的位置。 Marshal / UnMarshal 也没有错误。那么为什么 []byte 不同呢?
数据集很大,这里只分享每组的第一行。
设置 A:
[123 10 32 32 34 98 108 111 99 107 115 34 58 32 91 123 10 32 32 32 32 34 116 121 112 101 34 58 32 34 115 101 99 116 105 111 110 34 44 10 32 32 32 32 34
B组:
[123 34 98 108 111 99 107 115 34 58 91 123 34 98 108 111 99 107 95 105 105 100 34 58 34 34 34 34 34 34 34 34 34 101 101 101 101 101 101 101 110 116 115 115 34 58 34 58 110 110 110 117 108 108 108 108 108 108 108 108 108 44
下面是[]byte转成字符串的两组,也挺有意思的。集合 A 保留空格,集合 B 没有空格,但元素也已重新排列。我想知道结构是否不正确?然而,我一直在使用这种使用转换器从 json 中创建结构的技术,并且它们在这一点上运行良好。
组A:
{
"blocks": [{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Service Is Ready. Now?"
}
},
{
"type": "actions",
"block_id": "deploy_id",
"elements": [{
"type": "button",
"action_id": "yes_button",
"text": {
"type": "plain_text",
"text": "Yes"
},
"style": "danger",
"value": "yes"
},
{
"type": "button",
"action_id": "yes_toll_button",
"text": {
"type": "plain_text",
"text": "Yes To All"
},
"style": "danger",
"value": "yes"
},
{
"type": "button",
"action_id": "no_button",
"text": {
"type": "plain_text",
"text": "No"
},
"style": "primary",
"value": "no"
},
{
"type": "button",
"action_id": "no_toall_button",
"text": {
"type": "plain_text",
"text": "No To All"
},
"style": "primary",
"value": "no"
}]
}]
}
B组:
{"blocks":[{"block_id":"","elements":null,"text":{"text":"Service Is Ready. Now?","type":"mrkdwn"},"type":"section"},{"block_id":"deploy_id","elements":[{"action_id":"yes_button","style":"danger","text":{"text":"Yes","type":"plain_text"},"type":"button","value":"yes"},{"action_id":"yes_toll_button","style":"danger","text":{"text":"Yes To All","type":"plain_text"},"type":"button","value":"yes"},{"action_id":"no_button","style":"primary","text":{"text":"No","type":"plain_text"},"type":"button","value":"no"},{"action_id":"no_toall_button","style":"primary","text":{"text":"No To All","type":"plain_text"},"type":"button","value":"no"}],"text":{"text":"","type":""},"type":"actions"}]}
nipuna 建议我尝试使用 JSON 的美化,所以我这样做了并且每个集合都有 []byte 的字符串版本。
A组:
{
"blocks": [{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Service Is Ready. Now?"
}
},
{
"type": "actions",
"block_id": "deploy_id",
"elements": [{
"type": "button",
"action_id": "yes_button",
"text": {
"type": "plain_text",
"text": "Yes"
},
"style": "danger",
"value": "yes"
},
{
"type": "button",
"action_id": "yes_toll_button",
"text": {
"type": "plain_text",
"text": "Yes To All"
},
"style": "danger",
"value": "yes"
},
{
"type": "button",
"action_id": "no_button",
"text": {
"type": "plain_text",
"text": "No"
},
"style": "primary",
"value": "no"
},
{
"type": "button",
"action_id": "no_toall_button",
"text": {
"type": "plain_text",
"text": "No To All"
},
"style": "primary",
"value": "no"
}]
}]
}
B组:
{
"blocks": [
{
"block_id": "",
"elements": null,
"text": {
"text": "Service Is Ready. Now?",
"type": "mrkdwn"
},
"type": "section"
},
{
"block_id": "deploy_id",
"elements": [
{
"action_id": "yes_button",
"style": "danger",
"text": {
"text": "Yes",
"type": "plain_text"
},
"type": "button",
"value": "yes"
},
{
"action_id": "yes_toll_button",
"style": "danger",
"text": {
"text": "Yes To All",
"type": "plain_text"
},
"type": "button",
"value": "yes"
},
{
"action_id": "no_button",
"style": "primary",
"text": {
"text": "No",
"type": "plain_text"
},
"type": "button",
"value": "no"
},
{
"action_id": "no_toall_button",
"style": "primary",
"text": {
"text": "No To All",
"type": "plain_text"
},
"type": "button",
"value": "no"
}
],
"text": {
"text": "",
"type": ""
},
"type": "actions"
}
]
}
我可以看到以下差异,并提出了一些建议。
可选字段
您的数据由 blocks
数组组成,在第一个块中,只有两个字段 text
和 type
,在第二个块中有所有其他字段,但没有 text
。所以 text
是可选的,当它是一个空结构时需要省略它。因此,您需要使用 omitempty
和指向 Text
字段的指针类型来定义 SlackRequestData
。
建议的 SlackRequestData
结构如下所示。
type Text struct {
Type string `json:"type,omitempty"`
Text string `json:"text,omitempty"`
}
type SlackRequestData struct {
Blocks []struct {
BlockID string `json:"block_id,omitempty"`
Elements []struct {
ActionID string `json:"action_id,omitempty"`
Style string `json:"style,omitempty"`
Text *Text `json:"text,omitempty"`
Type string `json:"type,omitempty"`
Value string `json:"value,omitempty"`
} `json:"elements,omitempty"`
Text *Text `json:"text,omitempty"`
Type string `json:"type,omitempty"`
} `json:"blocks,omitempty"`
}
编组数据顺序
你的文件json数据的字段顺序和SlackRequestData
的字段顺序不同。很明显,编组字符串和文件字符串是不同的。 json 编组不保证 here 中提到的编组时的顺序。因此,如果您需要比较这两者,请以某种方式对这些字节进行排序并进行比较。
Json 缩进
在您的文件数据中有些 json 美化数据和 json 编组 return 压缩输出。如果您需要获得相同的编组输出,则需要使用 MarshalIndent 并缩进您在文件中使用的代码。
因此,如果您需要比较两者,请使用 omitempty
编组,对两个结果进行排序(文件字节数组和 json 编组字节数组)。并删除空格,然后进行比较。你可以得到正确的相同结果。
自从 nipuna 解决了问题后,我正在更新问题陈述。这个问题更好地描述为,“为什么解组,然后编组 JSON 导致对 JSON 的更改与 REST API 请求不兼容?”发现的原因是 JSON 数组中的文本字段有时需要包含有时需要省略,而编组数据结构不支持省略它们。该结构确实对这些字段使用了术语“omitempty”,因此可以认为该指令会省略它们,但这还不够。我还需要将文本字段作为指针。原始问题描述如下。 最终的数据结构将“omitempty”用于文本字段,并将匿名结构设为指针。(请参阅已接受的答案)
我有一个 REST 请求在我使用 Go Unmarshal / Marshal 时失败了,但它对文件中的相同 JSON 数据有效。我已经验证,如果我采用那个确切的 []byte 并往返 UnMarshal / Marshal 它,那么读取文件的结果 []byte 是不同的。结果,我的 REST 请求失败了。这是图解法:file json -> []byte = set A of bytes。 file json -> []byte -> UnMarshal -> Marshal -> []byte = 字节集 B。设置字节 A != 设置字节 B。我不在乎,只是我需要修改 Go 结构中的数据,然后对其进行 Marshal,但是我不理解的字节更改导致请求失败。
这里是测试代码:
type SlackRequestData struct {
Blocks []struct {
BlockID string `json:"block_id"`
Elements []struct {
ActionID string `json:"action_id"`
Style string `json:"style"`
Text struct {
Text string `json:"text"`
Type string `json:"type"`
} `json:"text"`
Type string `json:"type"`
Value string `json:"value"`
} `json:"elements"`
Text struct {
Text string `json:"text"`
Type string `json:"type"`
} `json:"text"`
Type string `json:"type"`
} `json:"blocks"`
}
func MarshalFailTest(){
bytes_io, err := ioutil.ReadFile("../../slack/slack-message.json")
if err != nil {
fmt.Printf("Error: %+v\n", err) // handle err
}
new_req := SlackRequestData{}
err = json.Unmarshal(bytes_io, &new_req)
if err != nil {
fmt.Printf("Error: %+v\n", err) // handle err
}
new_json, err := json.Marshal(new_req)
if err != nil {
fmt.Printf("Error: %+v\n", err) // handle err
}
fmt.Printf("New req:\n%+v\n", new_req)
fmt.Printf("New Json:\n%+v\n", new_json)
fmt.Printf("Bytes:\n%+v\n", bytes_io)
}
这里是 json 文件数据:
{
"blocks": [{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Service Is Ready. Now?"
}
},
{
"type": "actions",
"block_id": "deploy_id",
"elements": [{
"type": "button",
"action_id": "yes_button",
"text": {
"type": "plain_text",
"text": "Yes"
},
"style": "danger",
"value": "yes"
},
{
"type": "button",
"action_id": "yes_toll_button",
"text": {
"type": "plain_text",
"text": "Yes To All"
},
"style": "danger",
"value": "yes"
},
{
"type": "button",
"action_id": "no_button",
"text": {
"type": "plain_text",
"text": "No"
},
"style": "primary",
"value": "no"
},
{
"type": "button",
"action_id": "no_toall_button",
"text": {
"type": "plain_text",
"text": "No To All"
},
"style": "primary",
"value": "no"
}]
}]
}
请注意,我使用了多个转换器来获取文件 json 并创建了 SlackRequestData 结构,并且 UnMarshal 似乎工作得很好,因为调试器中的结构将所有数据都放在了正确的位置。 Marshal / UnMarshal 也没有错误。那么为什么 []byte 不同呢?
数据集很大,这里只分享每组的第一行。 设置 A: [123 10 32 32 34 98 108 111 99 107 115 34 58 32 91 123 10 32 32 32 32 34 116 121 112 101 34 58 32 34 115 101 99 116 105 111 110 34 44 10 32 32 32 32 34 B组: [123 34 98 108 111 99 107 115 34 58 91 123 34 98 108 111 99 107 95 105 105 100 34 58 34 34 34 34 34 34 34 34 34 101 101 101 101 101 101 101 110 116 115 115 34 58 34 58 110 110 110 117 108 108 108 108 108 108 108 108 108 44
下面是[]byte转成字符串的两组,也挺有意思的。集合 A 保留空格,集合 B 没有空格,但元素也已重新排列。我想知道结构是否不正确?然而,我一直在使用这种使用转换器从 json 中创建结构的技术,并且它们在这一点上运行良好。
组A:
{
"blocks": [{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Service Is Ready. Now?"
}
},
{
"type": "actions",
"block_id": "deploy_id",
"elements": [{
"type": "button",
"action_id": "yes_button",
"text": {
"type": "plain_text",
"text": "Yes"
},
"style": "danger",
"value": "yes"
},
{
"type": "button",
"action_id": "yes_toll_button",
"text": {
"type": "plain_text",
"text": "Yes To All"
},
"style": "danger",
"value": "yes"
},
{
"type": "button",
"action_id": "no_button",
"text": {
"type": "plain_text",
"text": "No"
},
"style": "primary",
"value": "no"
},
{
"type": "button",
"action_id": "no_toall_button",
"text": {
"type": "plain_text",
"text": "No To All"
},
"style": "primary",
"value": "no"
}]
}]
}
B组:
{"blocks":[{"block_id":"","elements":null,"text":{"text":"Service Is Ready. Now?","type":"mrkdwn"},"type":"section"},{"block_id":"deploy_id","elements":[{"action_id":"yes_button","style":"danger","text":{"text":"Yes","type":"plain_text"},"type":"button","value":"yes"},{"action_id":"yes_toll_button","style":"danger","text":{"text":"Yes To All","type":"plain_text"},"type":"button","value":"yes"},{"action_id":"no_button","style":"primary","text":{"text":"No","type":"plain_text"},"type":"button","value":"no"},{"action_id":"no_toall_button","style":"primary","text":{"text":"No To All","type":"plain_text"},"type":"button","value":"no"}],"text":{"text":"","type":""},"type":"actions"}]}
nipuna 建议我尝试使用 JSON 的美化,所以我这样做了并且每个集合都有 []byte 的字符串版本。 A组:
{
"blocks": [{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Service Is Ready. Now?"
}
},
{
"type": "actions",
"block_id": "deploy_id",
"elements": [{
"type": "button",
"action_id": "yes_button",
"text": {
"type": "plain_text",
"text": "Yes"
},
"style": "danger",
"value": "yes"
},
{
"type": "button",
"action_id": "yes_toll_button",
"text": {
"type": "plain_text",
"text": "Yes To All"
},
"style": "danger",
"value": "yes"
},
{
"type": "button",
"action_id": "no_button",
"text": {
"type": "plain_text",
"text": "No"
},
"style": "primary",
"value": "no"
},
{
"type": "button",
"action_id": "no_toall_button",
"text": {
"type": "plain_text",
"text": "No To All"
},
"style": "primary",
"value": "no"
}]
}]
}
B组:
{
"blocks": [
{
"block_id": "",
"elements": null,
"text": {
"text": "Service Is Ready. Now?",
"type": "mrkdwn"
},
"type": "section"
},
{
"block_id": "deploy_id",
"elements": [
{
"action_id": "yes_button",
"style": "danger",
"text": {
"text": "Yes",
"type": "plain_text"
},
"type": "button",
"value": "yes"
},
{
"action_id": "yes_toll_button",
"style": "danger",
"text": {
"text": "Yes To All",
"type": "plain_text"
},
"type": "button",
"value": "yes"
},
{
"action_id": "no_button",
"style": "primary",
"text": {
"text": "No",
"type": "plain_text"
},
"type": "button",
"value": "no"
},
{
"action_id": "no_toall_button",
"style": "primary",
"text": {
"text": "No To All",
"type": "plain_text"
},
"type": "button",
"value": "no"
}
],
"text": {
"text": "",
"type": ""
},
"type": "actions"
}
]
}
我可以看到以下差异,并提出了一些建议。
可选字段
您的数据由 blocks
数组组成,在第一个块中,只有两个字段 text
和 type
,在第二个块中有所有其他字段,但没有 text
。所以 text
是可选的,当它是一个空结构时需要省略它。因此,您需要使用 omitempty
和指向 Text
字段的指针类型来定义 SlackRequestData
。
建议的 SlackRequestData
结构如下所示。
type Text struct {
Type string `json:"type,omitempty"`
Text string `json:"text,omitempty"`
}
type SlackRequestData struct {
Blocks []struct {
BlockID string `json:"block_id,omitempty"`
Elements []struct {
ActionID string `json:"action_id,omitempty"`
Style string `json:"style,omitempty"`
Text *Text `json:"text,omitempty"`
Type string `json:"type,omitempty"`
Value string `json:"value,omitempty"`
} `json:"elements,omitempty"`
Text *Text `json:"text,omitempty"`
Type string `json:"type,omitempty"`
} `json:"blocks,omitempty"`
}
编组数据顺序
你的文件json数据的字段顺序和SlackRequestData
的字段顺序不同。很明显,编组字符串和文件字符串是不同的。 json 编组不保证 here 中提到的编组时的顺序。因此,如果您需要比较这两者,请以某种方式对这些字节进行排序并进行比较。
Json 缩进
在您的文件数据中有些 json 美化数据和 json 编组 return 压缩输出。如果您需要获得相同的编组输出,则需要使用 MarshalIndent 并缩进您在文件中使用的代码。
因此,如果您需要比较两者,请使用 omitempty
编组,对两个结果进行排序(文件字节数组和 json 编组字节数组)。并删除空格,然后进行比较。你可以得到正确的相同结果。