解决Go json.Marshal() 将HTML字符串转义的问题
文章目录
- 解决Go json.Marshal() 将HTML字符串转义的问题
- 0. 前言
- 1. 问题发现
- 2. 问题分析
- 3. 问题解决
- 4.总结
# 阅读本文,你将了解
# 1. json.Marshal()实现
# 2. HTML字符串被转义的解决方案
0. 前言
最近在做项目的时候遇到一个问题,做MQTT设备鉴权是,有一个token算法,而这个token算法将会输出一个嵌套有&
、/
的HTML字符串。我在采用fmt.Println()
打印输出,发现上述特殊字符并没有被转义,但当我通过json.Marshal()
json序列化输出到文件中时,发现特殊字符被转义了,便有了本文。本文仅记录该问题定位和解决思路。
1. 问题发现
在将设备 device struct结构体进行token算法计算之后,通过json序列化写入文件中时,发现部分字符串被转义了。
type Devices struct {
Id int `json:"id"`
ApiKey string `json:"apiKey"`
AuthToken string `json:"auth_token"`
}
func main() {
var outDevices []Devices
// todo 封装device 相关的token信息
log.Println("outDevices:",outDevices)
bytes, err := json.Marshal(outDevices)
if err != nil {
log.Fatalf("json marshal is fail %+v", err)
}
WriteFile(bytes) //输出到文件中
}
未被序列化的输出--> outDevices: [{10022143 OTlmZWUyN2E0NTY5ZTZhNGE3YjQ= version=2020-12-16&res=products%2F102425%2Fdevices%2F10022143&et=1908125107&method=sha1&sign=O6Ba%2FG%2BTj0JPq3o9i5A8vw4iPIA%3D}]
通过查看输出的文件内容,发现&
被转义为了\u0026
,如下图:
2. 问题分析
- 1.怀疑json.Marshal() 序列化,将
&
被转义了; - 2.ioutil.WriteFile()将json序列化之后的
[]byte
转义了。
打印输出json.Marshal()
之后的[]byte
数组,发现&
被转义为了\u0026
.
json.Marshal: [{"id":10022143,"apiKey":"OTlmZWUyN2E0NTY5ZTZhNGE3YjQ=","auth_token":"version=2020-12-16\u0026res=products%2F102425%2Fdevices%2F10022143\u0026et=1908125107\u0026method=sha1\u0026sign=O6Ba%2FG%2BTj0JPq3o9i5A8vw4iPIA%3D"}]
//结合上文,这里可以看见 `&` 被转义成了 `\u0026`
查看 json.Marshal()
源码,在注解上找到了线索。
google翻译一下:
字符串值编码为强制转换为有效UTF-8的JSON字符串,用Unicode替换符文替换无效字节。
这样就可以安全地将JSON嵌入HTML <script>标记中,并使用HTMLEscape对字符串进行编码,
HTMLEscape会将“ <”,““>”,“&”,U + 2028和U + 2029
替换为“ \” u003c”,“ \ u003e”,“ \ u0026”,“ \ u2028”和“ \ u2029”。
使用编码器时可以禁用此替换,通过调用SetEscapeHTML(false)。
在看json.Marshal()
源码,发现escapeHTML:true
,由此可见json.Marshal()
默认会将字符串值编码为强制转换为有效UTF-8的JSON字符串,用Unicode替换符文替换无效字节。
3. 问题解决
问题已经很清楚了,就是被json.Marshal()
方法将字符串转义了。接下来就是怎么想法不被字符串转义了。
注意最后一句:
// This replacement can be disabled when using an Encoder,
// by calling SetEscapeHTML(false)
这里已经说了解决方案了: 使用 Encoder
生成字符串,并调用SetEscapeHTML(false)
type Devices struct {
Id int `json:"id"`
ApiKey string `json:"apiKey"`
AuthToken string `json:"auth_token"`
}
func main() {
var outDevices []Devices
// todo 封装device 相关的token信息
log.Println("outDevices:",outDevices)
// 解决问题
byteBuf := bytes.NewBuffer([]byte{})
encoder := json.NewEncoder(byteBuf)
encoder.SetEscapeHTML(false) // 不转义 特殊字符
err := encoder.Encode(outDevices)
if err != nil {
panic(err)
}
log.Println("json.Marshal:",byteBuf)
WriteFile(byteBuf.Bytes()) //输出到文件中
}
未被序列化的输出--> outDevices: [{10022143 OTlmZWUyN2E0NTY5ZTZhNGE3YjQ= version=2020-12-16&res=products%2F102425%2Fdevices%2F10022143&et=1908126970&method=sha1&sign=1%2BlnRyK%2FItthV%2FwI%2Fuj%2Bkt%2FhPf8%3D}]
Encoder 编码--> Encoder: [{"id":10022143,"apiKey":"OTlmZWUyN2E0NTY5ZTZhNGE3YjQ=","auth_token":"version=2020-1216&res=products%2F102425%2Fdevices%2F10022143&et=1908126970&method=sha1&sign=1%2BlnRyK%2FItthV%2FwI%2Fuj%2Bkt%2FhPf8%3D"}]
// 发现改问题得到了解决。
4.总结
- 遇到问题,先定位,找到问题的根源
- 要培养自己追查源码的能力,读懂了源码,基本上能解决一半以上的问题
- 合理利用搜索引擎,英语很重要