今天应使用方需求,发现在后台 存入json字符串的时候,url再次取出来,&符号被转义为了\u0026,很明显这是我们所不期望的事情,于是这个问题就需要进行解决。
环境:
go(gorm, redigo) + postgres + redis.
列出环境的原因是,我要进行分析具体的问题出在哪一个环节。因为系统相对已经比较复杂了,虽然耦合度已经很低了,但是毕竟模块多,所以要经过严格的debug定位。
可能出问题的地方:
1. json进行Marshal 和 Unmarshal 的地方
2. gorm 进行数据存储时自动进行了转义
于是我们一个一个来查,首先查看json的地方,Marshal函数调用处:
if finalJsonData, err = json.Marshal(originMap); err != nil {
err = libs.NewReportError(err)
}
接下来进入源码查看是否有转义操作:
func Marshal(v interface{}) ([]byte, error) {
e := newEncodeState()
err := e.marshal(v, encOpts{escapeHTML: true})
if err != nil {
return nil, err
}
buf := append([]byte(nil), e.Bytes()...)
e.Reset()
encodeStatePool.Put(e)
return buf, nil
}
一下就可以发现,源码里有一句:
err := e.marshal(v, encOpts{escapeHTML: true})
进入encOpts结构体进行查看
type encOpts struct {
// quoted causes primitive fields to be encoded inside JSON strings.
quoted bool
// escapeHTML causes '<', '>', and '&' to be escaped in JSON strings.
escapeHTML bool
}
很明显,别人都打了注释了
// escapeHTML causes '<', '>', and '&' to be escaped in JSON strings.
escapeHTML这个成员会决定是否转义 <, >, & 这三个符号。
问题这个时候就找到了,就是Marshal函数的问题。
但是源码里写死了,我能怎么办呢,我也没办法。
只有自力更生了,打开libs文件夹,新建了一个json.go文件
然后,仔细看了看json包的源码,没错,从头翻到尾,其实也就一千多行,建议童鞋们如果时间充足读一读,会很有收获的。
翻完之后,我的函数查看顺序 => Marshal => marshal => valueEncoder => typeEncoder => newTypeEncoder => (map.go)Store方法存入sets。然后我就放弃了
。到了这里我的确梳理了一遍json.Marshal的实现过程,但是这并没什么卵用,如果照着写一遍那完全就没意义了。于是另外想办法,还是求助了万能的网友,json.Marshl的功能还可以通过json包的stream.go里面的 Encoder结构体实现,突然开心。然后马上跑去看源码。
// An Encoder writes JSON values to an output stream.
type Encoder struct {
w io.Writer
err error
escapeHTML bool
indentBuf *bytes.Buffer
indentPrefix string
indentValue string
}
很明显的有一个escapeHTML成员属性,可以设置转义。
然后继续看源码,发现有个SetEscapeHTML方法
func (enc *Encoder) SetEscapeHTML(on bool) {
enc.escapeHTML = on
}
于是到这里就可以进行代码书写了:
func Marshal(v interface{}, isEscape ...bool) (data []byte, err error) {
bf := bytes.NewBuffer([]byte{})
jsonEncoder := json.NewEncoder(bf)
if len(isEscape) > 0 {
jsonEncoder.SetEscapeHTML(isEscape[0])
}
err = jsonEncoder.Encode(v)
data = bf.Bytes()
return
}
这个函数我为了通用性,通过 ...bool方式进行设置参数,即默认为转义,加入false则不转义。
上项目,通过。于是其他的可能性就可以不用再去试了。
这个故事告诉我们,解决问题要从根源开始查探,然后进行落实,必要的时候还可以求助万能的网友,毕竟工作中,不可能直接花时间把整个json包都看了,然后自己花时间实现一个json函数,知道原理,会用reflect,其实只要肯花时间都是能写出来的,但是重点是工作ing,速度最重要。要是你看到了我这篇文章,直接粘贴复制把。