It seems that if I have an interface{} in Go in a particular order and I Marshal it into a []byte and then Unmarshal it back into an interface{}, it should retain the original order. It does not. The result is that DeepEqual fails to match the two as you can see in the program below.
package main
import (
"encoding/json"
"fmt"
"reflect"
)
func main() {
var jd interface{}
j := map[string]interface{}{
"A": map[string]interface{}{"a": 1, "b": 2, "c": 3},
"B": map[string]interface{}{"a": 1, "b": 2, "c": 3},
"C": map[string]interface{}{
"a": map[string]interface{}{"a": 1, "b": 2, "c": 3},
"b": map[string]interface{}{"a": 1, "b": 2, "c": 3},
"c": map[string]interface{}{"a": 1, "b": 2, "c": 3},
},
}
s, _ := json.Marshal(j)
_ = json.Unmarshal(s, &jd)
fmt.Println(string(s))
fmt.Println(j)
fmt.Println(jd)
if !reflect.DeepEqual(j, jd) {
fmt.Println("Fail!")
}
}
The results from this program are random, but this is a typical result:
{"A":{"a":1,"b":2,"c":3},"B":{"a":1,"b":2,"c":3},"C":{"a":{"a":1,"b":2,"c":3},"b":{"a":1,"b":2,"c":3},"c":{"a":1,"b":2,"c":3}}}
map[B:map[a:1 b:2 c:3] C:map[c:map[a:1 b:2 c:3] a:map[b:2 c:3 a:1] b:map[a:1 b:2 c:3]] A:map[c:3 a:1 b:2]]
map[A:map[a:1 b:2 c:3] B:map[a:1 b:2 c:3] C:map[b:map[c:3 a:1 b:2] c:map[a:1 b:2 c:3] a:map[a:1 b:2 c:3]]]
Fail!
As you can see, the values Marshal into the original order, but Unmarshal into a random order (multiple runs of this program will produce different results). The result is that the DeepEqual comparison fails every time (so far).
Is this a bug in Go? Does anyone have a workaround that they can suggest?
The Go Programming Language Specification
The iteration order over maps is not specified and is not guaranteed to be the same from one iteration to the next.
By design, the iteration order over maps is pseudorandom.
The order of the map isn't why it is not equal. (If you unmarshall twice you will get two deeply equal maps that still differ randomly in order.)
Unmarshalling a json number is float64:
float64, for JSON numbers
It will work if you change your code as follows:
var num float64 = 0;
j := map[string]interface{}{
"A": map[string]interface{}{"a": num+1, "b": num+2, "c": num+3},
"B": map[string]interface{}{"a": num+1, "b": num+2, "c": num+3},
"C": map[string]interface{}{
"a": map[string]interface{}{"a": num+1, "b": num+2, "c": num+3},
"b": map[string]interface{}{"a": num+1, "b": num+2, "c": num+3},
"c": map[string]interface{}{"a": num+1, "b": num+2, "c": num+3},
},
}
You can then change the type of num to anything else and it will fail.