I’m replacing a PHP process at work that updates Solr indexes. These index names are dynamically created from different attributes, so at the end, I had to use a map[string]interface{} to build these values.
The issue comes at the PHP side of the process (I know this isn’t a PHP forum, but please bear with me) as the consumer is coded in the following way:
// for loop here with $item declared
$key = array_shift($item)
// end for loop
So, as you can see, this code is expecting the first value in the document to be a specific value. And that is because the process that builds and send data to Solr in PHP prepends that value in the array to be encoded as JSON as the first key/value pair and Solr respects that ordering when storing the documents, and, somehow, PHP encodes that object into JSON respecting the order of the keys, that’s why these guys are assuming on the other end that the value will be there.
According to the JSON RFC specification there’s no restriction on the order of the keys, which is why I assume json.Marshal([]byte, interface{}) doesn’t care about the order of the keys and you always get a different ordering.
Has anyone faced a situation where the order of the keys in a JSON object is important and, if so, how can you ensure key ordering?
I know that this can be achieved using a struct, but as I was saying, these attributes can change over time and it would be pretty difficult to maintain hundreds of structs with different structures.
Thanks in advance and I hope have made myself clear!
Sorry guys for the delay and thank you for your answers.
I think I didn’t formulate the question correctly. The whole issue is keeping the desired ordering when marshaling an object to JSON:
package main
import (
"fmt"
"encoding/json"
)
func main() {
// Clipped document
document := map[string]interface{}{
"facet_is_special_price":[]string{"1"},
"score_outlet":"0",
"skus":[]string{"DI139BE71WDWDFMX", "DI139BE71WDWDFMX-519406"},
"facet_novelty_two_days":[]string{"0"},
"facet_brand":[]string{"139"},
"sku":[]string{"DI139BE71WDWDFMX"},
}
// The order here appears to be random everytime, which is OK
fmt.Printf("Document (random order)\n%#v\n", document)
encoded, err := json.Marshal(&document)
if err != nil {
panic(err)
}
// Right, marshaling the object into JSON orders the keys alphabetically
fmt.Printf("JSON (alphatical order)\n%s", encoded)
// There's a special attribute on the PHP side is expecting to be the first key in the object (array)
if sku, found := document["sku"]; found {
newDocument := make(map[string]interface{}, len(document))
newDocument["sku"] = sku
delete(document, "sku")
for key, value := range document {
newDocument[key] = value
}
encoded, err := json.Marshal(&newDocument)
if err != nil {
panic(err)
}
// Still the the keys are order alphabetically, which I understand is the expected behaviour
fmt.Printf("JSON\n%s", encoded)
}
}
Don’t get me wrong, I know the PHP code is not what it should be, it will fail when the expected value is not at the first place or when it isn’t there at all. I just wanted to know if there’s a way to keep the ordering or the position of the keys when converting the object into JSON so it is not needed to touch the legacy PHP code (even though it isn’t optimal).
My goal is to achieve the following:
// Given:
document := make(map[string]interface{})
document["sku"] = "ABC123"
// ...
document["a_last_attribute"] = "some value"
// and when
encoded, _ := json.Marshal(&document)
// get the following: {"sku": "ABC123", "a_last_attribute"}
You cannot, using Go a map, as it does not have ordered keys. You would need to create your own ordered map type, internally being something like a list of key/value pairs, and implement JSON marshalling/unmarshalling for it.