-1

I am writing code that will check if data changed based on a comparison of json.Marshaled hashes of maps. I've created small code to produce what I am doing in abstracted way (available also in playground)

package main

import (
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "fmt"
)

func main() {
    fmt.Println("Hello, playground")
    a := make(map[string]string)
    a["a"] = "a1"
    a["b"] = "b2"
    sa, _ := json.Marshal(a)
    ha := GenerateSHA256Hash(string(sa))

    b := make(map[string]string)
    b["a"] = "a1"
    b["b"] = "b2"
    sb, _ := json.Marshal(b)
    hb := GenerateSHA256Hash(string(sb))

    fmt.Println(ha)
    fmt.Println(hb)
    fmt.Println(ha == hb)
}

func GenerateSHA256Hash(s string) string {
    hasher := sha256.New()
    hasher.Write([]byte(s))
    return hex.EncodeToString(hasher.Sum(nil))
}

But I recall that order of maps are unordered and in Golang spec it's written that

The iteration order over maps is not specified and is not guaranteed to be the same from one iteration to the next. If a map entry that has not yet been reached is removed during iteration, the corresponding iteration value will not be produced. If a map entry is created during iteration, that entry may be produced during the iteration or may be skipped. The choice may vary for each entry created and from one iteration to the next. If the map is nil, the number of iterations is 0.

So, in the code above I am building map, in the same way, each time and not accessing it concurrently during json.Marshalling.

Question: Will the hashes, produced in such manner, be always equal? Or will this approach be stable?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
zmii
  • 4,123
  • 3
  • 40
  • 64

1 Answers1

3

Go spec in this case is irrelevant since it's a details of the Go standard library (the encoding/json module)

As of this very moment it's implemented as

// Extract and sort the keys.
keys := v.MapKeys()
sv := make([]reflectWithString, len(keys))
for i, v := range keys {
    sv[i].v = v
    if err := sv[i].resolve(); err != nil {
        e.error(fmt.Errorf("json: encoding error for type %q: %q", v.Type().String(), err.Error()))
    }
}
sort.Slice(sv, func(i, j int) bool { return sv[i].s < sv[j].s })

Additionally, given the encoding/json documentation says

The map keys are sorted and used as JSON object keys by applying the following rules, subject to the UTF-8 coercion described for string values above:

it's safe to expect the same hash until at least Go 2.

zerkms
  • 249,484
  • 69
  • 436
  • 539