这是JSON sometimes array sometimes object
的后续行动在最初的问题中,我问过如何处理:"我正在使用一个json API,它可能会为变量返回一个字符串,或者可能会为变量返回一个数组"
我有一个解决方案,但我想知道,有没有办法修改json.RawMessage?
而不是if / then查看RawMessage的[或{char来确定对象是数组还是字符串,如果我总是使用RawMessage变量字符串并将其转换为数组怎么办?
这样,我就不必为BOTH字符串和数组编写所有访问器。我可以简单地处理数组。
所以我的问题是:有没有办法修改json.RawMessage?
例如:
转过来:
{
"net": {
"comment": {
"line":
{
"$": "All abuse issues will only be responded to by the Abuse",
"@number": "0"
}
}
}
进入这个:
{
"net": {
"comment": {
"line": [
{
"$": "All abuse issues will only be responded to by the Abuse",
"@number": "0"
}
]
}
}
所以,这样,当我解组到我的结构中时,只有一种类型的comment.line,Just line [] vs line [] AND line。
提前致谢。
我是一个golang新手,我只是把我的脑袋缠绕在一个强有力的语言解组的困难上。
答案 0 :(得分:2)
是的,您可以编辑json.RawMessage类型,因为它只是[] byte的别名。
那就是说,你不需要保持原始类型,只需要自己实现数组类型,并在自定义Unmarshal函数中,使标量成为一个数组。
这是一个例子(在Play上)。
我们在这里做的就是看看MagicArray的字节是否以'['开头,如果是这样,只是正常解组。否则,Unmarshal并附加到切片。
你必须为你想要工作的每种类型实现自定义数组,但这仍然可能比尝试安全地操纵json二进制文件以尝试将标量强制转换为数组更好。
这种方法的另一个好处是你可以使用流式json解码器,如json.NewDecoder(reader).Decode(&obj)
package main
import "encoding/json"
import "log"
type MagicArray []interface{}
func (ma *MagicArray) UnmarshalJSON(b []byte) error {
if b[0] == '[' {
return json.Unmarshal(b, (*[]interface{})(ma))
} else {
var obj interface{}
if err := json.Unmarshal(b, &obj); err != nil {
return err
}
*ma = append(*ma, obj)
}
return nil
}
func main() {
myStruct := struct {
A MagicArray
B MagicArray
}{}
err := json.Unmarshal(jsonToDecode, &myStruct)
if err != nil {
log.Println("Fail:", err)
} else {
log.Println(myStruct)
}
}
var jsonToDecode = []byte(`
{
"A": "I am not an array",
"B":["I am an array"]
}
`)
答案 1 :(得分:2)
我认为David有一个好的(更好的)答案,但是直接回答你的问题:是的,如果你小心的话,你可以修改json.RawMessage
。它被声明为type json.RawMessage []byte
,这意味着它只是引擎盖下[]byte
的另一个名称。您可以将其投射到[]byte
或string
,修改它,然后再投回。
对序列化数据执行字符串选项并不是在不考虑后果的情况下应该做的事情,但是在围绕JSON对象包装[
和]
的情况下,它不是太多很难证明它应该是安全的。如果msg
是表示对象的json.RawMessage
,那么
json.RawMessage("[" + string(msg) + "]")
是我认为一种可读方法,使RawMessage
表示包含该对象的数组:)