以下是Go playground链接。
基本上我的JSON字符串键中有一些特殊字符('\u0000'
):
var j = []byte(`{"Page":1,"Fruits":["5","6"],"\u0000*\u0000_errorMessages":{"x":"123"},"*_successMessages":{"ok":"hi"}}`)
我想把它解组成一个结构:
type Response1 struct {
Page int
Fruits []string
Msg interface{} `json:"*_errorMessages"`
Msg1 interface{} `json:"\\u0000*\\u0000_errorMessages"`
Msg2 interface{} `json:"\u0000*\u0000_errorMessages"`
Msg3 interface{} `json:"\0*\0_errorMessages"`
Msg4 interface{} `json:"\\0*\\0_errorMessages"`
Msg5 interface{} `json:"\x00*\x00_errorMessages"`
Msg6 interface{} `json:"\\x00*\\x00_errorMessages"`
SMsg interface{} `json:"*_successMessages"`
}
我尝试了很多,但它没有用。 此链接可能有助于golang.org/src/encoding/json/encode_test.go。
答案 0 :(得分:5)
简答:使用当前的json
实施,不能仅使用struct tags。
注意:这是实施限制,而不是规范限制。 (它是json
包实施的限制,而不是struct tags specification的限制。)
某些背景信息:您使用raw string literal指定了代码:
原始字符串文字的值是由引号之间未解释的(隐式UTF-8编码)字符组成的字符串...
因此编译器在原始字符串文字的内容中不会发生任何转义或取消引用。
reflect.StructTag
引用的struct tag值的约定:
按照惯例,标记字符串是可选的以空格分隔的键的串联:" value"对。每个键都是一个非空字符串,由空格以外的非控制字符组成(U + 0020''),引用(U + 0022'"'),和冒号(U + 003A':')。每个值都使用U + 0022'"'字符和Go字符串文字语法。
这意味着按惯例标记值是由空格分隔的(键:"值")对的列表。键有很多限制,但值可能是任何值,值(应该)使用" Go字符串文字语法",这意味着这些值将在运行时从代码中取消引用(通过调用strconv.Unquote()
,名为from StructTag.Get()
,在源文件reflect/type.go
中,当前行#809)。
所以不需要双引号。请参阅简化示例:
type Response1 struct {
Page int
Fruits []string
Msg interface{} `json:"\u0000_abc"`
}
现在输入以下代码:
t := reflect.TypeOf(Response1{})
fmt.Printf("%#v\n", t.Field(2).Tag)
fmt.Printf("%#v\n", t.Field(2).Tag.Get("json"))
打印:
"json:\"\\u0000_abc\""
"\x00_abc"
如您所见,json
键的值部分为"\x00_abc"
,因此它正确包含零字符。
但json
包将如何使用此功能?
json
包使用StructTag.Get()
(来自reflect
包)返回的值,正是我们所做的。您可以在json/encode.go
源文件typeFields()
函数中看到它,当前行#1032。到目前为止一切都很好。
然后它在json/tags.go
源文件中调用未导出的json.parseTag()
函数,当前行#17。这会在逗号后面切换部分(它变为"标记选项")。
最后在源文件json/encode.go
中使用前一个值调用json.isValidTag()
函数,当前行#731。此函数检查传递的string
的符文,并且(除了一组预定义的允许字符"!#$%&()*+-./:<=>?@[]^_{|}~ "
)拒绝所有不是unicode字母或数字的内容(由unicode.IsLetter()
定义)和unicode.IsDigit()
):
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
return false
}
'\u0000'
不是预定义允许字符的一部分,正如您现在可以猜到的那样,它既不是字母也不是数字:
// Following code prints "INVALID":
c := '\u0000'
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
fmt.Println("INVALID")
}
由于isValidTag()
返回false
,name
(json
键的值,没有&#34;标记选项&#34;部分)将被丢弃(name = ""
)而不使用。因此,不会找到包含unicode零的struct字段的匹配项。
对于替代解决方案,请使用map
或自定义json.Unmarshaler
或使用json.RawMessage
。
但我非常不鼓励使用这种丑陋的json键。我理解你可能只是试图解析这样的json响应,它可能超出你的范围,但是你应该反对使用这些键,因为它们稍后会引起更多的问题(例如,如果存储在db中,通过检查记录它将会很难发现其中有'\u0000'
个字符,因为它们可能显示为无效。
答案 1 :(得分:0)
由于:http://golang.org/ref/spec#Struct_types
,您不能这样做但您可以解组到map[string]interface{}
,然后通过regexp检查该对象的字段名称。
答案 2 :(得分:0)
我不认为使用struct标签是可行的。您可以做的最好的事情是将其解组为map[string]interface{}
,然后手动获取值:
var b = []byte(`{"\u0000abc":42}`)
var m map[string]interface{}
err := json.Unmarshal(b, &m)
if err != nil {
panic(err)
}
fmt.Println(m, m["\x00abc"])