golang自定json序列化实现对非ASCII字符进行转义

作者: veaxen 分类: Golang 发布时间: 2018-10-15 19:22

问题

最近接手了一个Golang的项目,说实话,这个项目的坑点太多了,这里就不吐槽了。在改这个项目的一个bug时,发现导致这个bug的其中一个原因是Golang的json序列化与PHP的json序列化结果是不同的,这里举一个简单的例子。

对于PHP的json序列化:

$data = array(
                "num"=>123456,
                "key"=>"PHP是世界上最好用的语言"
             );
$jsonStr = json_encode($data);
echo $jsonStr;

输出的结果是:

{"num":123456,"key":"PHP\u662f\u4e16\u754c\u4e0a\u6700\u597d\u7528\u7684\u8bed\u8a00"}

对于Go的json序列化:

package main
import (
    "encoding/json"
    "fmt"
)

func main() {
    data := map[string]interface{}{
        "num":123456,
        "key":"Go是世界上最好用的语言",
    }
    jsonData, _ :=json.Marshal(data)
    fmt.Println(string(jsonData))
}

输出结果是:

{"key":"Go是世界上最好用的语言","num":123456}

很明显可以看出来,php在序列化时,默认对Unicode编码的字符进行了转义,而Go没有进行转义,这种问题在不同语言中都有存在,比如Python在序列化是,也跟php一样,默认进行了这种转义。

那么问题来了,php能不能不要进行转义,答案是YES。只需要:

//PHP 5.4.0起才有JSON_UNESCAPED_UNICODE这个选项
$jsonStr = json_encode($data, JSON_UNESCAPED_UNICODE);

如果能这样改,让Go和PHP产生的json一样,那个当然是很开心的一件事,但是现在考虑各种各样的问题,有时候我们需要让Go来进行转义(例如用Go重构PHP项目,又要考虑兼容以前的旧客户端的解析),但很可惜,Go并没有像PHP一样提供类似的选项让我们决定要不要进行转义。解决的办法是自定义Go的json序列化过程,Go的json包提供了这样的支持

Go自定义Json序列化

查看json包文档,我们可以发现有关于自定义序列化的例子,当执行json序列化时,如果对应的类型实现了Marshaler接口:

type Marshaler interface {
    MarshalJSON() ([]byte, error)
}

那么就会执行其MarshalJSON方法,并将返回的字节数组作为该值的序列化结果。

回到我们的问题上来,我们可以通过这种自定义序列化来解决我们的问题,实现对非ASCII编码的字符进行转义,具体代码如下。

package main
import (
    "encoding/json"
    "fmt"
    "strconv"
)

type escapeString string

func (esc escapeString) MarshalJSON() ([]byte, error) {
    return []byte(strconv.QuoteToASCII(string(esc))), nil
}

func main() {
    data := map[string]interface{}{
        "num":123456,
        "key":escapeString("Go是世界上最好用的语言"),
    }
    jsonData, _ :=json.Marshal(data)
    fmt.Println(string(jsonData))
}

strconv.QuoteToASCII()方法将非ASCII编码的字符进行了转义,并返回字符串。

那么既然有这个方法,还需要自定义序列化过程吗?直接对正常序列化的结果进行转义不就好了吗?

data := map[string]interface{}{
    "num":123456,
    "key":"Go是世界上最好用的语言",
}
jsonData, _ :=json.Marshal(data)
fmt.Println(strconv.QuoteToASCII(string(jsonData)))

这样得到的结果是这样的:

"{\"key\":\"Go\u662f\u4e16\u754c\u4e0a\u6700\u597d\u7528\u7684\u8bed\u8a00\",\"num\":123456}"

显然是不符合预期的。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.