prl900
prl900

Reputation: 4179

Marshaling JSON []byte as strings in Go

I have a struct containing strings as []byte fields which I'd like to encode into JSON. However, the generated JSON contains a non expected string representation of the slice contents. Here is an example of what I refer:

package main

import (
    "fmt"
    "encoding/json"
    )

type Msg struct {
    Content []byte
}

func main() {
    helloStr := "Hello"
    helloSlc := []byte(helloStr)
    fmt.Println(helloStr, helloSlc)

    obj := Msg{helloSlc}
    json, _ := json.Marshal(obj)
    fmt.Println(string(json))
}

This produces the following output:

Hello [72 101 108 108 111]
{"Content":"SGVsbG8="}

What kind of conversion is the json.Marshal() method performing to the []byte encoded string. How can I generate a JSON with the original content of my string {"Content":"Hello"}?

Upvotes: 20

Views: 40563

Answers (3)

boy9527
boy9527

Reputation: 1

You can use a third party library to achieve what you want

package main

import (
    "fmt"
    "strconv"
    "strings"
    "time"
    "unsafe"

    jsoniter "github.com/json-iterator/go"
)

func main() {
    err := test()
    if err != nil {
        panic(err)
    }
}

func test() error {
    jsoniter.RegisterTypeEncoder("[]uint8", &uint8Enc{})

    var a struct {
        A []byte `json:"a"`
        B []byte `json:"b"`
    }
    a.A = []byte{'1', 1, 2, 3, '9'}
    a.B = []byte(time.Now().String())

    s, err := json.MarshalIndent(a, "", "  ")
    if err != nil {
        return err
    }
    fmt.Println(string(s))
    return nil
}

var json = jsoniter.ConfigCompatibleWithStandardLibrary

type uint8Enc struct{}

func (ue *uint8Enc) IsEmpty(ptr unsafe.Pointer) bool {
    data := *((*[]uint8)(ptr))
    return len(data) == 0
}
func (ue *uint8Enc) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
    var (
        data = *((*[]uint8)(ptr))
        sb   strings.Builder
    )
    const hexTable = "0123456789abcdef"
    for _, v := range data {
        if strconv.IsPrint(rune(v)) {
            sb.WriteByte(v)
        } else {
            sb.WriteString(`\x`)
            sb.WriteByte(hexTable[v>>4])
            sb.WriteByte(hexTable[v&0x0f])
        }
    }
    stream.WriteString(sb.String())
}

The result is as follows

{
  "a": "1\\x01\\x02\\x039",
  "b": "2023-06-05 09:15:38.551518 +0800 CST m=+0.003193401"
}

Upvotes: 0

silverfighter
silverfighter

Reputation: 6882

I came a cross the same thing and even if this is a rather old question and already answered there is another option.

If you use json.RawMessage (which internaly is a []byte) as a type instead of []byte the Marshaling works into a Json String as expected.

Upvotes: 5

nemo
nemo

Reputation: 57757

A []byte is marshalled as base64 encoded string. From the documentation:

Array and slice values encode as JSON arrays, except that []byte encodes as a base64-encoded string, and a nil slice encodes as the null JSON object.

These values are decoded properly when unmarshalled.

The reason why this is done is that JSON does not have a native representation for raw bytes. See this question for a detailed explanation.

Upvotes: 20

Related Questions