Skip to content

Commit 117866c

Browse files
authored
Merge pull request #19 from itchyny/token-based-parsing
Unmarshal using token decoder
2 parents 58e762d + 26fd0ee commit 117866c

File tree

2 files changed

+208
-188
lines changed

2 files changed

+208
-188
lines changed

orderedmap.go

Lines changed: 107 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,10 @@ package orderedmap
33
import (
44
"bytes"
55
"encoding/json"
6-
"errors"
76
"sort"
87
"strings"
98
)
109

11-
var NoValueError = errors.New("No value for this key")
12-
13-
type KeyIndex struct {
14-
Key string
15-
Index int
16-
}
17-
type ByIndex []KeyIndex
18-
19-
func (a ByIndex) Len() int { return len(a) }
20-
func (a ByIndex) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
21-
func (a ByIndex) Less(i, j int) bool { return a[i].Index < a[j].Index }
22-
2310
type Pair struct {
2411
key string
2512
value interface{}
@@ -117,201 +104,134 @@ func (o *OrderedMap) UnmarshalJSON(b []byte) error {
117104
if o.values == nil {
118105
o.values = map[string]interface{}{}
119106
}
120-
var err error
121-
err = mapStringToOrderedMap(string(b), o)
107+
err := json.Unmarshal(b, &o.values)
122108
if err != nil {
123109
return err
124110
}
125-
return nil
126-
}
127-
128-
func mapStringToOrderedMap(s string, o *OrderedMap) error {
129-
// parse string into map
130-
m := map[string]interface{}{}
131-
err := json.Unmarshal([]byte(s), &m)
132-
if err != nil {
111+
dec := json.NewDecoder(bytes.NewReader(b))
112+
if _, err = dec.Token(); err != nil { // skip '{'
133113
return err
134114
}
135-
// Get the order of the keys
136-
orderedKeys := []KeyIndex{}
137-
for k, _ := range m {
138-
kEscaped := strings.Replace(k, `"`, `\"`, -1)
139-
kQuoted := `"` + kEscaped + `"`
140-
// Find how much content exists before this key.
141-
// If all content from this key and after is replaced with a close
142-
// brace, it should still form a valid json string.
143-
sTrimmed := s
144-
for len(sTrimmed) > 0 {
145-
lastIndex := strings.LastIndex(sTrimmed, kQuoted)
146-
if lastIndex == -1 {
147-
break
148-
}
149-
sTrimmed = sTrimmed[0:lastIndex]
150-
sTrimmed = strings.TrimSpace(sTrimmed)
151-
if len(sTrimmed) > 0 && sTrimmed[len(sTrimmed)-1] == ',' {
152-
sTrimmed = sTrimmed[0 : len(sTrimmed)-1]
153-
}
154-
maybeValidJson := sTrimmed + "}"
155-
testMap := map[string]interface{}{}
156-
err := json.Unmarshal([]byte(maybeValidJson), &testMap)
157-
if err == nil {
158-
// record the position of this key in s
159-
ki := KeyIndex{
160-
Key: k,
161-
Index: len(sTrimmed),
162-
}
163-
orderedKeys = append(orderedKeys, ki)
164-
// shorten the string to get the next key
165-
startOfValueIndex := lastIndex + len(kQuoted)
166-
valueStr := s[startOfValueIndex : len(s)-1]
167-
valueStr = strings.TrimSpace(valueStr)
168-
if len(valueStr) > 0 && valueStr[0] == ':' {
169-
valueStr = valueStr[1:len(valueStr)]
115+
o.keys = make([]string, 0, len(o.values))
116+
return decodeOrderedMap(dec, o)
117+
}
118+
119+
func decodeOrderedMap(dec *json.Decoder, o *OrderedMap) error {
120+
hasKey := make(map[string]bool, len(o.values))
121+
for {
122+
token, err := dec.Token()
123+
if err != nil {
124+
return err
125+
}
126+
if delim, ok := token.(json.Delim); ok && delim == '}' {
127+
return nil
128+
}
129+
key := token.(string)
130+
if hasKey[key] {
131+
// duplicate key
132+
for j, k := range o.keys {
133+
if k == key {
134+
copy(o.keys[j:], o.keys[j+1:])
135+
break
170136
}
171-
valueStr = strings.TrimSpace(valueStr)
172-
if valueStr[0] == '{' {
173-
// if the value for this key is a map
174-
// find end of valueStr by removing everything after last }
175-
// until it forms valid json
176-
hasValidJson := false
177-
i := 1
178-
for i < len(valueStr) && !hasValidJson {
179-
if valueStr[i] != '}' {
180-
i = i + 1
181-
continue
182-
}
183-
subTestMap := map[string]interface{}{}
184-
testValue := valueStr[0 : i+1]
185-
err = json.Unmarshal([]byte(testValue), &subTestMap)
186-
if err == nil {
187-
hasValidJson = true
188-
valueStr = testValue
189-
break
190-
}
191-
i = i + 1
137+
}
138+
o.keys[len(o.keys)-1] = key
139+
} else {
140+
hasKey[key] = true
141+
o.keys = append(o.keys, key)
142+
}
143+
144+
token, err = dec.Token()
145+
if err != nil {
146+
return err
147+
}
148+
if delim, ok := token.(json.Delim); ok {
149+
switch delim {
150+
case '{':
151+
if values, ok := o.values[key].(map[string]interface{}); ok {
152+
newMap := OrderedMap{
153+
keys: make([]string, 0, len(values)),
154+
values: values,
155+
escapeHTML: o.escapeHTML,
192156
}
193-
// convert to orderedmap
194-
// this may be recursive it values in the map are also maps
195-
if hasValidJson {
196-
newMap := New()
197-
err := mapStringToOrderedMap(valueStr, newMap)
198-
if err != nil {
199-
return err
200-
}
201-
m[k] = *newMap
157+
if err = decodeOrderedMap(dec, &newMap); err != nil {
158+
return err
202159
}
203-
} else if valueStr[0] == '[' {
204-
// if the value for this key is a slice
205-
// find end of valueStr by removing everything after last ]
206-
// until it forms valid json
207-
hasValidJson := false
208-
i := 1
209-
for i < len(valueStr) && !hasValidJson {
210-
if valueStr[i] != ']' {
211-
i = i + 1
212-
continue
213-
}
214-
subTestSlice := []interface{}{}
215-
testValue := valueStr[0 : i+1]
216-
err = json.Unmarshal([]byte(testValue), &subTestSlice)
217-
if err == nil {
218-
hasValidJson = true
219-
valueStr = testValue
220-
break
221-
}
222-
i = i + 1
160+
o.values[key] = newMap
161+
} else if oldMap, ok := o.values[key].(OrderedMap); ok {
162+
newMap := OrderedMap{
163+
keys: make([]string, 0, len(oldMap.values)),
164+
values: oldMap.values,
165+
escapeHTML: o.escapeHTML,
223166
}
224-
// convert to slice with any map items converted to
225-
// orderedmaps
226-
// this may be recursive if values in the slice are slices
227-
if hasValidJson {
228-
newSlice := []interface{}{}
229-
err := sliceStringToSliceWithOrderedMaps(valueStr, &newSlice)
230-
if err != nil {
231-
return err
232-
}
233-
m[k] = newSlice
167+
if err = decodeOrderedMap(dec, &newMap); err != nil {
168+
return err
234169
}
235-
} else {
236-
o.Set(k, m[k])
170+
o.values[key] = newMap
171+
} else if err = decodeOrderedMap(dec, &OrderedMap{}); err != nil {
172+
return err
173+
}
174+
case '[':
175+
if values, ok := o.values[key].([]interface{}); ok {
176+
if err = decodeSlice(dec, values, o.escapeHTML); err != nil {
177+
return err
178+
}
179+
} else if err = decodeSlice(dec, []interface{}{}, o.escapeHTML); err != nil {
180+
return err
237181
}
238-
break
239182
}
240183
}
241184
}
242-
// Sort the keys
243-
sort.Sort(ByIndex(orderedKeys))
244-
// Convert sorted keys to string slice
245-
k := []string{}
246-
for _, ki := range orderedKeys {
247-
k = append(k, ki.Key)
248-
}
249-
// Set the OrderedMap values
250-
o.values = m
251-
o.keys = k
252-
return nil
253185
}
254186

255-
func sliceStringToSliceWithOrderedMaps(valueStr string, newSlice *[]interface{}) error {
256-
// if the value for this key is a []interface, convert any map items to an orderedmap.
257-
// find end of valueStr by removing everything after last ]
258-
// until it forms valid json
259-
itemsStr := strings.TrimSpace(valueStr)
260-
itemsStr = itemsStr[1 : len(itemsStr)-1]
261-
// get next item in the slice
262-
itemIndex := 0
263-
startItem := 0
264-
endItem := 0
265-
for endItem <= len(itemsStr) {
266-
couldBeItemEnd := false
267-
couldBeItemEnd = couldBeItemEnd || endItem == len(itemsStr)
268-
couldBeItemEnd = couldBeItemEnd || (endItem < len(itemsStr) && itemsStr[endItem] == ',')
269-
if !couldBeItemEnd {
270-
endItem = endItem + 1
271-
continue
272-
}
273-
// if this substring compiles to json, it's the next item
274-
possibleItemStr := strings.TrimSpace(itemsStr[startItem:endItem])
275-
var possibleItem interface{}
276-
err := json.Unmarshal([]byte(possibleItemStr), &possibleItem)
187+
func decodeSlice(dec *json.Decoder, s []interface{}, escapeHTML bool) error {
188+
for index := 0; ; index++ {
189+
token, err := dec.Token()
277190
if err != nil {
278-
endItem = endItem + 1
279-
continue
191+
return err
280192
}
281-
if possibleItemStr[0] == '{' {
282-
// if item is map, convert to orderedmap
283-
oo := New()
284-
err := mapStringToOrderedMap(possibleItemStr, oo)
285-
if err != nil {
286-
return err
287-
}
288-
// add new orderedmap item to new slice
289-
slice := *newSlice
290-
slice = append(slice, *oo)
291-
*newSlice = slice
292-
} else if possibleItemStr[0] == '[' {
293-
// if item is slice, convert to slice with orderedmaps
294-
newItem := []interface{}{}
295-
err := sliceStringToSliceWithOrderedMaps(possibleItemStr, &newItem)
296-
if err != nil {
297-
return err
193+
if delim, ok := token.(json.Delim); ok {
194+
switch delim {
195+
case '{':
196+
if index < len(s) {
197+
if values, ok := s[index].(map[string]interface{}); ok {
198+
newMap := OrderedMap{
199+
keys: make([]string, 0, len(values)),
200+
values: values,
201+
escapeHTML: escapeHTML,
202+
}
203+
if err = decodeOrderedMap(dec, &newMap); err != nil {
204+
return err
205+
}
206+
s[index] = newMap
207+
} else if oldMap, ok := s[index].(OrderedMap); ok {
208+
newMap := OrderedMap{
209+
keys: make([]string, 0, len(oldMap.values)),
210+
values: oldMap.values,
211+
escapeHTML: escapeHTML,
212+
}
213+
if err = decodeOrderedMap(dec, &newMap); err != nil {
214+
return err
215+
}
216+
s[index] = newMap
217+
}
218+
} else if err = decodeOrderedMap(dec, &OrderedMap{}); err != nil {
219+
return err
220+
}
221+
case '[':
222+
if index < len(s) {
223+
values := s[index].([]interface{})
224+
if err = decodeSlice(dec, values, escapeHTML); err != nil {
225+
return err
226+
}
227+
} else if err = decodeSlice(dec, []interface{}{}, escapeHTML); err != nil {
228+
return err
229+
}
230+
case ']':
231+
return nil
298232
}
299-
// replace original slice item with new slice
300-
slice := *newSlice
301-
slice = append(slice, newItem)
302-
*newSlice = slice
303-
} else {
304-
// any non-slice and non-map item, just add json parsed item
305-
slice := *newSlice
306-
slice = append(slice, possibleItem)
307-
*newSlice = slice
308233
}
309-
// remove this item from itemsStr
310-
startItem = endItem + 1
311-
endItem = endItem + 1
312-
itemIndex = itemIndex + 1
313234
}
314-
return nil
315235
}
316236

317237
func (o OrderedMap) MarshalJSON() ([]byte, error) {

0 commit comments

Comments
 (0)