为了账号安全,请及时绑定邮箱和手机立即绑定

使用 \u0000 \x00 去 json.Unmarshal 键

使用 \u0000 \x00 去 json.Unmarshal 键

Go
收到一只叮咚 2021-11-08 10:31:36
这是 Go游乐场链接。基本上'\u0000',我的 JSON 字符串键中有一些特殊字符 ( ):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有所帮助。
查看完整描述

3 回答

?
哈士奇WWW

TA贡献1799条经验 获得超6个赞

简短回答:在当前的json实现中,不可能只使用struct tags。


注意:这是一个实现限制,而不是规范限制。(这是json包实现的限制,而不是结构标签规范的限制。)


一些背景:您使用原始字符串文字指定了标签:


原始字符串文字的值是由引号之间的未解释(隐式 UTF-8 编码)字符组成的字符串...


因此,编译器不会在原始字符串文字的内容中进行转义或取消引用。


引用自 struct 标记值的约定reflect.StructTag:


按照惯例,标签字符串是可选的空格分隔键:“值”对的串联。每个键都是一个非空字符串,由除空格 (U+0020 ' ')、引号 (U+0022 '"') 和冒号 (U+003A ':') 以外的非控制字符组成。每个值都用引号引起来使用 U+0022 '"' 字符和 Go 字符串文字语法。


这意味着按照惯例标签值是由空格分隔的 (key:"value") 对列表。有钥匙不少限制,但值可以是任何东西,和值(应该)使用“去串文字语法”,这意味着这些值将在运行时从代码中加引号(通过一个电话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.parseTag()函数json/tags.go,当前行#17。这会剪切逗号后面的部分(成为“标签选项”)。


最后json.isValidTag()使用源文件中的前一个值调用函数json/encode.go,当前行#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键的值,没有“标签选项”部分)将被丢弃(name = "")并且不被使用。因此,将找不到包含 unicode 零的 struct 字段的匹配项。


对于替代解决方案,请使用map、自定义json.Unmarshaler或使用json.RawMessage。


但我非常不鼓励使用这种丑陋的 json 键。我知道您可能只是想解析这样的 json 响应,它可能超出您的范围,但是您应该反对使用这些键,因为它们只会在以后引起更多问题(例如,如果存储在 db 中,通过检查记录它会很难发现其中有'\u0000'字符,因为它们可能会显示为空)。


查看完整回答
反对 回复 2021-11-08
?
largeQ

TA贡献2039条经验 获得超7个赞

我认为 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"])

游乐场:http : //play.golang.org/p/RtS7Nst0d7。


查看完整回答
反对 回复 2021-11-08
?
青春有我

TA贡献1784条经验 获得超8个赞

由于以下原因,您不能这样做:http : //golang.org/ref/spec#Struct_types

但是您可以解组,map[string]interface{}然后通过regexp检查该对象的字段名称。


查看完整回答
反对 回复 2021-11-08
  • 3 回答
  • 0 关注
  • 366 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信