Reputation: 4179
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
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
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
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