Lei*_*Cao 4 string json go unmarshalling
这是Go playground链接.
基本上'\u0000'我的JSON字符串键中有一些特殊字符():
var j = []byte(`{"Page":1,"Fruits":["5","6"],"\u0000*\u0000_errorMessages":{"x":"123"},"*_successMessages":{"ok":"hi"}}`)
Run Code Online (Sandbox Code Playgroud)
我想将它解组成一个结构:
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"`
}
Run Code Online (Sandbox Code Playgroud)
我尝试了很多,但它没有用.此链接可能有助于golang.org/src/encoding/json/encode_test.go.
简短回答:使用当前的json实现,不可能只使用struct标签.
注意:这是一个实现限制,而不是规范限制.(这是json包实现的限制,而不是struct标签规范的限制.)
一些背景知识:您使用原始字符串文字指定了标记:
原始字符串文字的值是由引号之间的未解释(隐式UTF-8编码)字符组成的字符串...
因此,编译器在原始字符串文字的内容中不会发生任何转义或取消引用.
结构标记值的约定引自reflect.StructTag:
按照惯例,标记字符串是可选的空格分隔的键:"值"对的串联.每个键都是一个非空字符串,由空格(U + 0020''),引号(U + 0022'"')和冒号(U + 003A':')以外的非控制字符组成.每个值都被引用使用U + 0022'"'字符和Go字符串文字语法.
这意味着按惯例标记值是由空格分隔的(键:"值")对的列表.键有很多限制,但值可能是任何值,值(应该)使用"Go string literal syntax",这意味着这些值在运行时将从代码中取消引用(通过调用strconv.Unquote(),调用from StructTag.Get(),在源代码中)文件reflect/type.go,目前行#809).
所以不需要双引号.查看简化示例:
type Response1 struct {
Page int
Fruits []string
Msg interface{} `json:"\u0000_abc"`
}
Run Code Online (Sandbox Code Playgroud)
现在以下代码:
t := reflect.TypeOf(Response1{})
fmt.Printf("%#v\n", t.Field(2).Tag)
fmt.Printf("%#v\n", t.Field(2).Tag.Get("json"))
Run Code Online (Sandbox Code Playgroud)
打印:
"json:\"\\u0000_abc\""
"\x00_abc"
Run Code Online (Sandbox Code Playgroud)
如您所见,json键的值部分是"\x00_abc"正确包含零字符.
但是这个json包怎么用呢?
该json包使用StructTag.Get()(来自reflect包)返回的值,正是我们所做的.您可以在json/encode.go源文件中找到它,typeFields()函数,当前行#1032.到现在为止还挺好.
然后它json.parseTag()在json/tags.go源文件中调用未导出的函数,当前行#17.这会在逗号后面切割部分(成为"标记选项").
最后json.isValidTag(),在源文件中使用前一个值调用函数json/encode.go,当前行#731.此函数检查传递的符文string,并且(除了一组预定义的允许字符"!#$%&()*+-./:<=>?@[]^_{|}~ ")拒绝所有不是unicode字母或数字(由unicode.IsLetter()and 定义unicode.IsDigit()):
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
return false
}
Run Code Online (Sandbox Code Playgroud)
'\u0000' 不是预定义允许字符的一部分,正如您现在可以猜到的那样,它既不是字母也不是数字:
// Following code prints "INVALID":
c := '\u0000'
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
fmt.Println("INVALID")
}
Run Code Online (Sandbox Code Playgroud)
并且由于isValidTag()返回false,name(json不是"标签选项"部分的键的值)将被丢弃(name = "")并且不被使用.因此,不会找到包含unicode零的struct字段的匹配项.
对于替代解决方案,请使用a map或自定义json.Unmarshaler或使用json.RawMessage.
但我非常不鼓励使用这种丑陋的json键.我理解你可能只是试图解析这样的json响应,它可能超出你的范围,但是你应该反对使用这些键,因为它们稍后会引起更多问题(例如,如果存储在db中,通过检查记录它将会很难发现它们中有'\u0000'字符,因为它们可能显示为什么都没有.
| 归档时间: |
|
| 查看次数: |
1745 次 |
| 最近记录: |