解码Marshalled JSON unicode

时间:2017-06-04 10:35:00

标签: go unicode utf-8

我认为解释我问题的最快方法是使用example

package main

import (
    "fmt"
    "encoding/json"
)

type JSON struct {
    Body string
}

func main() {
    body := "<html><body>Hello World</body></html>"

    obj := JSON{body}

    result, _ := json.Marshal(obj)
    fmt.Println(string(result))
}

输出:

  

{&#34; Body&#34;:&#34; \ u003chtml \ u003e \ u003cbody \ u003eHello World \ u003c / body \ u003e \ u003c / html \ u003e&#34;}

我希望结果是一个utf8编码的字符串,其读取内容相同。我怎样才能实现这一目标?我试图在循环中使用utf8.DecodeRune,

str := ""

for _, res := range result {
    decoded, _ := utf8.DecodeRune(res)
    str += string(decoded)
}

但这会导致compilation error

  

main.go:21:不能使用res(类型字节)作为utf8.DecodeRune的参数中的类型[]字节

在编组对象上调用DecodeRune会返回first character,因为您需要

  

{

编辑:我使用的是Go 1.6.2,由于某种原因,显然没有SetEscapeHTML

3 个答案:

答案 0 :(得分:5)

这是预期的行为。来自docs

  

字符串值编码为强制转换为有效UTF-8的JSON字符串,替换   Unicode替换符号的无效字节。尖括号   &#34;&LT;&#34;和&#34;&gt;&#34;被转移到&#34; \ u003c&#34;和&#34; \ u003e&#34;保留一些浏览器   从错误解释JSON输出为HTML。 &符&#34;&amp;&#34;也是   逃到了#34; \#34; \#34;出于同样的原因。可以禁用此转义   使用调用了SetEscapeHTML(false)的编码器。

您可以使用Encoder并在其上调用SetEscapeHTML(false)来获得所需的结果:

func main() {
    body := "<html><body>Hello World</body></html>"

    obj := JSON{body}

    enc := json.NewEncoder(os.Stdout)
    enc.SetEscapeHTML(false)
    enc.Encode(obj)
}

工作示例:https://play.golang.org/p/lMNCJ16dIo

答案 1 :(得分:2)

另一种实现此目的的方法是将那些转义的字符替换为未转义的UTF-8字符。 (我曾经这样做是为了使非英语字母在JSON中易于人读。)

您可以使用strconv.Quote()strconv.Unquote()进行转换。

func _UnescapeUnicodeCharactersInJSON(_jsonRaw json.RawMessage) (json.RawMessage, error) {
    str, err := strconv.Unquote(strings.Replace(strconv.Quote(string(_jsonRaw)), `\\u`, `\u`, -1))
    if err != nil {
        return nil, err
    }
    return []byte(str), nil
}

func main() {
    // Both are valid JSON.
    var jsonRawEscaped json.RawMessage   // json raw with escaped unicode chars
    var jsonRawUnescaped json.RawMessage // json raw with unescaped unicode chars

    // '\u263a' == '☺'
    jsonRawEscaped = []byte(`{"HelloWorld": "\uC548\uB155, \uC138\uC0C1(\u4E16\u4E0A). \u263a"}`) // "\\u263a"
    jsonRawUnescaped, _ = _UnescapeUnicodeCharactersInJSON(jsonRawEscaped)                        // "☺"

    fmt.Println(string(jsonRawEscaped))   // {"HelloWorld": "\uC548\uB155, \uC138\uC0C1(\u4E16\u4E0A). \u263a"}
    fmt.Println(string(jsonRawUnescaped)) // {"HelloWorld": "안녕, 세상(世上). ☺"}
}

https://play.golang.org/p/pUsrzrrcDG-

我希望这会有所帮助。

答案 2 :(得分:0)

顺便说一句,这就是编译错误的原因。

json.Marshal返回一个字节切片([]byte),而不是字符串。

当您使用range迭代字节切片时,您不会迭代其符文,而是一次超过单个字节。您不能在字节值上使用DecodeRune() - 它需要一个符文,这是一个32位整数值,表示Unicode代码点。如果您在字符串上使用range进行迭代,这就是您所获得的。

现在,根据您想要实现的目标,看起来您根本不想要DecodeRune。

另一个答案充分描述了如何告诉JSON编码不要转义<>字符,即

enc := json.NewEncoder(os.Stdout)
enc.SetEscapeHTML(false)