-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathprop_fmt.go
More file actions
127 lines (104 loc) · 3.24 KB
/
Copy pathprop_fmt.go
File metadata and controls
127 lines (104 loc) · 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package main
import (
"encoding/json"
"fmt"
"log"
"strings"
)
// FormatJSONMixed handles two top-level JSON structures:
// 1. A single-key object containing an array: {"key": [elements]}
// 2. A pure array of objects: [elements]
// It formats the output with compact, single-line array elements.
func FormatJSONMixed(input interface{}) (string, error) {
var rawJSON []byte
var err error
// 1. Ensure we have raw JSON bytes to work with.
switch v := input.(type) {
case []byte:
rawJSON = v
case string:
rawJSON = []byte(v)
default:
rawJSON, err = json.Marshal(v)
if err != nil {
return "", fmt.Errorf("failed to marshal input: %v", err)
}
}
// 2. Unmarshal into a generic interface{} to check its type.
var genericData interface{}
if err := json.Unmarshal(rawJSON, &genericData); err != nil {
return "", fmt.Errorf("failed to unmarshal JSON: %v", err)
}
var sb strings.Builder
// Determine the array source based on the top-level structure
switch v := genericData.(type) {
case map[string]interface{}:
// --- Case 1: Root is a single-key object ---
// Validation check ensures only one key exists
if len(v) != 1 {
return "", fmt.Errorf("input JSON object must have exactly one root key, found %d", len(v))
}
sb.WriteString("{ ")
// Use a composite loop/assignment to get the single key/value pair
// because Go maps are unordered and cannot be accessed by index or
// a known key without knowing the key name first
var key string
var value interface{}
for k, val := range v {
key = k
value = val
break // geet the 1st (and only) pair, see earlier strong validation check
}
// Validate the value is an array (slice)
elementArray, ok := value.([]interface{})
if !ok {
return "", fmt.Errorf("value for key %q is not a JSON array", key)
}
keyBytes, _ := json.Marshal(key)
// Write the indented key and array start
//sb.WriteString(" ") // 2-space indent
sb.Write(keyBytes)
sb.WriteString(": [\n")
// Format the elements
if err := formatArrayElements(&sb, elementArray, 4); err != nil {
return "", err
}
// Write the closing array and object
sb.WriteString(" ]\n")
sb.WriteString("}")
case []interface{}:
// --- Case 2: Root is a pure array ---
sb.WriteString("[\n")
// Format the elements
if err := formatArrayElements(&sb, v, 2); err != nil {
return "", err
}
// Write the closing array bracket
sb.WriteString("]")
default:
return "", fmt.Errorf("unsupported root JSON structure. Must be an array object or an array")
}
return sb.String(), nil
}
// formatArrayElements contains the core logic for marshalling and indenting elements.
func formatArrayElements(sb *strings.Builder, elements []interface{}, indentSpaces int) error {
indent := strings.Repeat(" ", indentSpaces)
numElements := len(elements)
for i, element := range elements {
// Marshal the single element (compact, one-line string)
elemBytes, err := json.Marshal(element)
if err != nil {
return fmt.Errorf("failed to marshal element %d: %v", i, err)
}
// Write the indented element
sb.WriteString(indent)
sb.Write(elemBytes)
// Add comma and newline
if i < numElements-1 {
sb.WriteString(",")
}
sb.WriteString("\n")
}
log.Println(numElements, "elements.")
return nil
}