Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

encoding/json: Wrong []uint8 marshal #16815

Closed
mr4x opened this issue Aug 21, 2016 · 2 comments

Comments

Projects
None yet
3 participants
@mr4x
Copy link

commented Aug 21, 2016

Please answer these questions before submitting your issue. Thanks!

  1. What version of Go are you using (go version)?
    go1.7
  2. What operating system and processor architecture are you using (go env)?
    darwin/amd64
  3. What did you do?
    play.golang.org
  4. What did you expect to see?
    []uint8{42, 42} or at least "**"
  5. What did you see instead?
    [34 75 105 111 61 34] and "Kio="
@odeke-em

This comment has been minimized.

Copy link
Member

commented Aug 21, 2016

Hello there @mr4x.
This isn't a bug, perhaps it is an misinterpretation of type equality and expectations.

TL;DR string([]byte{42, 42}) and []byte{42, 42} are not of the same type.
When a string value is marshaled or unmarshaled, its representation stays the same, this doesn't apply for other types always

Analysis

Your code sample at https://play.golang.org/p/gQ7NbCR7dQ, and inlined below:

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    b, err := json.Marshal([]byte{42, 42})
    if err != nil {
        fmt.Println("error:", err)
    }
    fmt.Println(b, string(b))
}

which when run gives

[34 75 105 111 61 34] "Kio="
[]byte(42, 42) != string([]byte(42, 42))
where string([]byte(42, 42)) == "**".

string([]byte{42, 42}) contains a read-only copy of the contents of []byte{42, 42} so content wise those two are the same. However, their types are different -- the JSON marshaler interpretes them differently too.

When the string value is marshaled, it still gives "**" which is what you were expecting, since the marshaled value of a string is itself and so is a directly converted string value, with no transformations. But that only applies when we are dealing with strings and not byte slices.

Therefore: if you then unmarshaled the originally encoded bytes as a []byte and printed out the value, you'd get back []byte{42, 42} and then if printed as a string gives "**". However, if you unmarshaled the originally encoded bytes to a string, you'd get the direct string conversion which is "Kio=".
Please see https://play.golang.org/p/cdXWIPD5ix or inlined below:

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
)


func main() {
    origB := []byte{42, 42}
    marshaledB, err := json.Marshal(origB)
    if err != nil {
        fmt.Println("error:", err)
    }
    fmt.Println(marshaledB, string(marshaledB))

    var liveB []byte
    var s string
    if err := json.Unmarshal(marshaledB, &liveB); err != nil {
        fmt.Printf("err: %v\n", err)
    }
    _ = json.Unmarshal(marshaledB, &s)
    if !bytes.Equal(liveB, origB) {
        fmt.Printf("failed to get back original value liveB: %v(%s) origB: %v(%s)\n", liveB, origB)
    }
    fmt.Printf("liveB:: raw: %v string: %s stringFromOriginallyEncodedBytes: %s\n", liveB, liveB, s)
}

Giving

[34 75 105 111 61 34] "Kio="
liveB:: raw: [42 42] string: ** stringFromOriginallyEncodedBytes: Kio=
@mr4x

This comment has been minimized.

Copy link
Author

commented Aug 21, 2016

@odeke-em thanks for the explanation

@mr4x mr4x closed this Aug 21, 2016

@golang golang locked and limited conversation to collaborators Aug 21, 2017

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
You can’t perform that action at this time.