Skip to content

Instantly share code, notes, and snippets.

@frobware
Last active March 8, 2024 22:09
Show Gist options
  • Save frobware/b1e94fb6a1944d9aa16197e987f9fc34 to your computer and use it in GitHub Desktop.
Save frobware/b1e94fb6a1944d9aa16197e987f9fc34 to your computer and use it in GitHub Desktop.
Sorts elements within a JSON document
// The JSON Order Utility sorts the elements within a JSON document,
// ensuring a consistent order of keys in JSON objects and sorting
// string arrays alphabetically. It operates on a generic JSON input,
// dynamically handling both JSON objects (maps) and arrays, including
// nested structures. For JSON objects, it orders the keys
// alphabetically. For arrays containing solely strings, it sorts the
// elements in ascending alphabetical order. Otherwise, it recursively
// processes each element in the array or object to achieve a fully
// ordered structure.
package main
import (
"encoding/json"
"fmt"
"io"
"os"
"sort"
"strings"
)
func orderJSON(input interface{}) interface{} {
switch val := input.(type) {
case map[string]interface{}:
orderedMap := make(map[string]interface{}, len(val))
keys := make([]string, 0, len(val))
for k := range val {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
orderedMap[k] = orderJSON(val[k])
}
return orderedMap
case []interface{}:
// Check if all elements are strings.
allStrings := true
for i := 0; i < len(val); i++ {
if _, ok := val[i].(string); !ok {
allStrings = false
break
}
}
// If all elements are strings, sort them.
if allStrings {
sort.Slice(val, func(i, j int) bool {
return val[i].(string) < val[j].(string)
})
} else {
// Otherwise, recursively order each element.
for i, v := range val {
val[i] = orderJSON(v)
}
}
return val
default:
return input
}
}
func main() {
var inputBytes []byte
var err error
if len(os.Args) == 2 {
filePath := os.Args[1]
inputBytes, err = os.ReadFile(filePath)
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading JSON file: %v\n", err)
os.Exit(1)
}
} else {
inputBytes, err = io.ReadAll(os.Stdin)
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading from stdin: %v\n", err)
os.Exit(1)
}
}
inputString := strings.TrimSpace(string(inputBytes))
if inputString == "" {
os.Exit(0)
}
var jsonObj interface{}
if err := json.Unmarshal([]byte(inputString), &jsonObj); err != nil {
fmt.Fprintf(os.Stderr, "Error unmarshalling JSON: %v\n", err)
os.Exit(1)
}
orderedJSON := orderJSON(jsonObj)
orderedJSONBytes, err := json.MarshalIndent(orderedJSON, "", " ")
if err != nil {
fmt.Fprintf(os.Stderr, "Error marshalling JSON: %v\n", err)
os.Exit(1)
}
if _, err := fmt.Println(string(orderedJSONBytes)); err != nil {
fmt.Fprintf(os.Stderr, "Error writing JSON: %v\n", err)
os.Exit(1)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment