Go Sad Limitation : Preserving order of JSON Unmarshal and other thing that Need Order
This is shocking to me.
The other library suffer from this too like YAML but they solved it with some method implemented
Source of story:
https://github.com/golang/go/issues/27179
Quick Demo of the Problem
We are going to unmarshal hard code simple JSON.
I assume unstructured one in this example.
package main
import (
"encoding/json"
"fmt"
)
func main() {
fmt.Println("Start")
jsonString := "[{\"code\":\"001\", \"name\":\"Manhattan\",\"country\":\"US\",\"fee\":\"200\"},{\"code\":\"002\", \"name\":\"New Jersey\",\"country\":\"US\",\"fee\":\"250\"}]"
var collections []map[string]string
err := json.Unmarshal([]byte(jsonString), &collections)
if err != nil {
fmt.Println("Invalid JSON")
fmt.Println(err)
return
}
fmt.Println(collections)
for i := 0; i < len(collections); i++ {
container := collections[i]
for key, value := range container {
fmt.Println(key, value)
}
}
}
The order we are expecting are
- Code
- Name
- Country
- Fee
But every run of the program make the different output — unstable result on the very simple program
Why? The Hash Map Data Structure
Because map
or Hash Map is Key-Value store with randomly assigned when created in the memory and Go standard library is built on top of this.
Solution Hard Code the Order
Everything that you put into Golang map
no matter it is request, response, body, payload, any file format, protocol etc.
MUST BE DETERMINISTIC
So that you can hard code the its order like this
package main
import (
"encoding/json"
"fmt"
)
func main() {
fmt.Println("Start")
jsonString := "[{\"code\":\"001\", \"name\":\"Manhattan\",\"country\":\"US\",\"fee\":\"200\"},{\"code\":\"002\", \"name\":\"New Jersey\",\"country\":\"US\",\"fee\":\"250\"}]" var collections []map[string]string
err := json.Unmarshal([]byte(jsonString), &collections)
if err != nil {
fmt.Println("Invalid JSON")
fmt.Println(err)
return
}
fmt.Println(collections)
headers := []string{
"code",
"name",
"country",
"fee"
}
for i := 0; i < len(collections); i++ {
container := collections[i]
for j := 0; j < len(headers); j++ {
header := headers[j]
value := container[header]
fmt.Println(header, value)
}
}
}
As long as Go lang core library match the output to map. It will always cause this problem.
Discussion on Spec
There are Google Group and people on in the internet specify that JSON spec RFC does not guarantee the order.
Okay, do you think that it’s make sense that the simple program that parse JSON and print it out cannot print it in order and always get an unpredictable output?
I know everyone know the answer. So why don’t you fix it?