diff --git a/orderedmap.go b/orderedmap.go index 61587d9..a951421 100644 --- a/orderedmap.go +++ b/orderedmap.go @@ -28,30 +28,43 @@ func (a ByPair) Len() int { return len(a.Pairs) } func (a ByPair) Swap(i, j int) { a.Pairs[i], a.Pairs[j] = a.Pairs[j], a.Pairs[i] } func (a ByPair) Less(i, j int) bool { return a.LessFunc(a.Pairs[i], a.Pairs[j]) } -type OrderedMap struct { +type OrderedMapImpl struct { keys []string values map[string]interface{} escapeHTML bool } -func New() *OrderedMap { - o := OrderedMap{} +type OrderedMap interface { + SetEscapeHTML(on bool) + Get(key string) (interface{}, bool) + Set(key string, value interface{}) + Delete(key string) + Keys() []string + Values() map[string]interface{} + SortKeys(sortFunc func(keys []string)) + Sort(lessFunc func(a *Pair, b *Pair) bool) + UnmarshalJSON(b []byte) error + MarshalJSON() ([]byte, error) +} + +func New() OrderedMap { + o := OrderedMapImpl{} o.keys = []string{} o.values = map[string]interface{}{} o.escapeHTML = true return &o } -func (o *OrderedMap) SetEscapeHTML(on bool) { +func (o *OrderedMapImpl) SetEscapeHTML(on bool) { o.escapeHTML = on } -func (o *OrderedMap) Get(key string) (interface{}, bool) { +func (o *OrderedMapImpl) Get(key string) (interface{}, bool) { val, exists := o.values[key] return val, exists } -func (o *OrderedMap) Set(key string, value interface{}) { +func (o *OrderedMapImpl) Set(key string, value interface{}) { _, exists := o.values[key] if !exists { o.keys = append(o.keys, key) @@ -59,7 +72,7 @@ func (o *OrderedMap) Set(key string, value interface{}) { o.values[key] = value } -func (o *OrderedMap) Delete(key string) { +func (o *OrderedMapImpl) Delete(key string) { // check key is in use _, ok := o.values[key] if !ok { @@ -76,21 +89,21 @@ func (o *OrderedMap) Delete(key string) { delete(o.values, key) } -func (o *OrderedMap) Keys() []string { +func (o *OrderedMapImpl) Keys() []string { return o.keys } -func (o *OrderedMap) Values() map[string]interface{} { +func (o *OrderedMapImpl) Values() map[string]interface{} { return o.values } // SortKeys Sort the map keys using your sort func -func (o *OrderedMap) SortKeys(sortFunc func(keys []string)) { +func (o *OrderedMapImpl) SortKeys(sortFunc func(keys []string)) { sortFunc(o.keys) } // Sort Sort the map using your sort func -func (o *OrderedMap) Sort(lessFunc func(a *Pair, b *Pair) bool) { +func (o *OrderedMapImpl) Sort(lessFunc func(a *Pair, b *Pair) bool) { pairs := make([]*Pair, len(o.keys)) for i, key := range o.keys { pairs[i] = &Pair{key, o.values[key]} @@ -103,7 +116,7 @@ func (o *OrderedMap) Sort(lessFunc func(a *Pair, b *Pair) bool) { } } -func (o *OrderedMap) UnmarshalJSON(b []byte) error { +func (o *OrderedMapImpl) UnmarshalJSON(b []byte) error { if o.values == nil { o.values = map[string]interface{}{} } @@ -119,7 +132,7 @@ func (o *OrderedMap) UnmarshalJSON(b []byte) error { return decodeOrderedMap(dec, o) } -func decodeOrderedMap(dec *json.Decoder, o *OrderedMap) error { +func decodeOrderedMap(dec *json.Decoder, o *OrderedMapImpl) error { hasKey := make(map[string]bool, len(o.values)) for { token, err := dec.Token() @@ -152,26 +165,26 @@ func decodeOrderedMap(dec *json.Decoder, o *OrderedMap) error { switch delim { case '{': if values, ok := o.values[key].(map[string]interface{}); ok { - newMap := OrderedMap{ + newMap := &OrderedMapImpl{ keys: make([]string, 0, len(values)), values: values, escapeHTML: o.escapeHTML, } - if err = decodeOrderedMap(dec, &newMap); err != nil { + if err = decodeOrderedMap(dec, newMap); err != nil { return err } o.values[key] = newMap - } else if oldMap, ok := o.values[key].(OrderedMap); ok { - newMap := OrderedMap{ + } else if oldMap, ok := o.values[key].(*OrderedMapImpl); ok { + newMap := &OrderedMapImpl{ keys: make([]string, 0, len(oldMap.values)), values: oldMap.values, escapeHTML: o.escapeHTML, } - if err = decodeOrderedMap(dec, &newMap); err != nil { + if err = decodeOrderedMap(dec, newMap); err != nil { return err } o.values[key] = newMap - } else if err = decodeOrderedMap(dec, &OrderedMap{}); err != nil { + } else if err = decodeOrderedMap(dec, &OrderedMapImpl{}); err != nil { return err } case '[': @@ -198,29 +211,29 @@ func decodeSlice(dec *json.Decoder, s []interface{}, escapeHTML bool) error { case '{': if index < len(s) { if values, ok := s[index].(map[string]interface{}); ok { - newMap := OrderedMap{ + newMap := &OrderedMapImpl{ keys: make([]string, 0, len(values)), values: values, escapeHTML: escapeHTML, } - if err = decodeOrderedMap(dec, &newMap); err != nil { + if err = decodeOrderedMap(dec, newMap); err != nil { return err } s[index] = newMap - } else if oldMap, ok := s[index].(OrderedMap); ok { - newMap := OrderedMap{ + } else if oldMap, ok := s[index].(*OrderedMapImpl); ok { + newMap := &OrderedMapImpl{ keys: make([]string, 0, len(oldMap.values)), values: oldMap.values, escapeHTML: escapeHTML, } - if err = decodeOrderedMap(dec, &newMap); err != nil { + if err = decodeOrderedMap(dec, newMap); err != nil { return err } s[index] = newMap - } else if err = decodeOrderedMap(dec, &OrderedMap{}); err != nil { + } else if err = decodeOrderedMap(dec, &OrderedMapImpl{}); err != nil { return err } - } else if err = decodeOrderedMap(dec, &OrderedMap{}); err != nil { + } else if err = decodeOrderedMap(dec, &OrderedMapImpl{}); err != nil { return err } case '[': @@ -242,7 +255,7 @@ func decodeSlice(dec *json.Decoder, s []interface{}, escapeHTML bool) error { } } -func (o OrderedMap) MarshalJSON() ([]byte, error) { +func (o OrderedMapImpl) MarshalJSON() ([]byte, error) { var buf bytes.Buffer buf.WriteByte('{') encoder := json.NewEncoder(&buf) diff --git a/orderedmap_test.go b/orderedmap_test.go index 5b89ef6..278c5b8 100644 --- a/orderedmap_test.go +++ b/orderedmap_test.go @@ -69,10 +69,10 @@ func TestOrderedMap(t *testing.T) { // Values method values := o.Values() expectedValues := map[string]interface{}{ - "number": 4, - "string": "x", + "number": 4, + "string": "x", "strings": []string{"t", "u"}, - "mixed": []interface{}{ 1, "1" }, + "mixed": []interface{}{1, "1"}, } if !reflect.DeepEqual(values, expectedValues) { t.Error("Values method returned unexpected map") @@ -493,7 +493,7 @@ func TestUnmarshalJSONArrayOfMaps(t *testing.T) { func TestUnmarshalJSONStruct(t *testing.T) { var v struct { - Data *OrderedMap `json:"data"` + Data OrderedMapImpl `json:"data"` } err := json.Unmarshal([]byte(`{ "data": { "x": 1 } }`), &v) @@ -595,3 +595,47 @@ func TestOrderedMap_empty_map(t *testing.T) { t.Error("Got", marshalledStr) } } + +func TestMutableAfterUnmarshal(t *testing.T) { + const input = `{ + "foo": "bar", + "prop": { + "v1": 1, + "v2": "v1" + } +}` + out := New() + err := json.Unmarshal([]byte(input), &out) + if err != nil { + t.Fatal(err) + } + + iout, _ := out.Get("prop") + iout.(OrderedMap).Set("v3", "blabla") + + if out.Keys()[0] != "foo" { + t.Fatal("key order changed") + } + if out.Keys()[1] != "prop" { + t.Fatal("key order changed") + } + inPropX, _ := out.Get("prop") + inProp := inPropX.(OrderedMap) + + v1, _ := inProp.Get("v1") + if inProp.Keys()[0] != "v1" || v1.(float64) != 1 { + t.Fatal("key order changed") + } + v2, _ := inProp.Get("v2") + if inProp.Keys()[1] != "v2" || v2.(string) != "v1" { + t.Fatal("key order changed") + } + + if inProp.Keys()[2] != "v3" { + t.Fatal("key order changed") + } + v3, _ := inProp.Get("v3") + if v3.(string) != "blabla" { + t.Fatal("expect blabla") + } +}