使用\ u0000\x00转到json.Unmarshal键

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.

icz*_*cza 5

简短回答:使用当前的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'字符,因为它们可能显示为什么都没有.

  • 精彩的调查! (4认同)