diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d08a464 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/Pexeso/orderedmap + +go 1.17 diff --git a/orderedmap.go b/orderedmap.go index cf8db2a..3ce83a2 100644 --- a/orderedmap.go +++ b/orderedmap.go @@ -3,19 +3,19 @@ package orderedmap import ( "bytes" "encoding/json" - "errors" "sort" "strings" ) -var NoValueError = errors.New("No value for this key") - type KeyIndex struct { Key string Index int } + type ByIndex []KeyIndex +var _ sort.Interface = &ByIndex{} + func (a ByIndex) Len() int { return len(a) } func (a ByIndex) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a ByIndex) Less(i, j int) bool { return a[i].Index < a[j].Index } @@ -38,6 +38,8 @@ type ByPair struct { LessFunc func(a *Pair, j *Pair) bool } +var _ sort.Interface = &ByPair{} + 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]) } @@ -94,12 +96,12 @@ func (o *OrderedMap) Keys() []string { return o.keys } -// SortKeys Sort the map keys using your sort func +// SortKeys sorts the map keys using your sort func func (o *OrderedMap) SortKeys(sortFunc func(keys []string)) { sortFunc(o.keys) } -// Sort Sort the map using your sort func +// Sort sorts the map using your sort func func (o *OrderedMap) Sort(lessFunc func(a *Pair, b *Pair) bool) { pairs := make([]*Pair, len(o.keys)) for i, key := range o.keys { @@ -113,25 +115,32 @@ func (o *OrderedMap) Sort(lessFunc func(a *Pair, b *Pair) bool) { } } +var _ json.Unmarshaler = &OrderedMap{} + +// UnmarshalJSON implements the json.Unmarshaler interface for OrderedMap. func (o *OrderedMap) UnmarshalJSON(b []byte) error { if o.values == nil { o.values = map[string]interface{}{} } - var err error - err = mapStringToOrderedMap(string(b), o) - if err != nil { - return err - } - return nil + return mapStringToOrderedMap(string(b), o) +} + +func unmarshalJSON(s string, v interface{}) error { + r := strings.NewReader(s) + decoder := json.NewDecoder(r) + // This option ensures that + decoder.UseNumber() + return decoder.Decode(v) } func mapStringToOrderedMap(s string, o *OrderedMap) error { // parse string into map m := map[string]interface{}{} - err := json.Unmarshal([]byte(s), &m) + err := unmarshalJSON(s, &m) if err != nil { return err } + // Get the order of the keys orderedKeys := []KeyIndex{} for k, _ := range m { @@ -270,14 +279,16 @@ func sliceStringToSliceWithOrderedMaps(valueStr string, newSlice *[]interface{}) endItem = endItem + 1 continue } + // if this substring compiles to json, it's the next item possibleItemStr := strings.TrimSpace(itemsStr[startItem:endItem]) var possibleItem interface{} - err := json.Unmarshal([]byte(possibleItemStr), &possibleItem) + err := unmarshalJSON(possibleItemStr, &possibleItem) if err != nil { endItem = endItem + 1 continue } + if possibleItemStr[0] == '{' { // if item is map, convert to orderedmap oo := New() @@ -314,6 +325,9 @@ func sliceStringToSliceWithOrderedMaps(valueStr string, newSlice *[]interface{}) return nil } +var _ json.Marshaler = &OrderedMap{} + +// MarshalJSON implements the json.Marshaler interface for OrderedMap. func (o OrderedMap) MarshalJSON() ([]byte, error) { s := "{" for _, k := range o.keys { diff --git a/orderedmap_test.go b/orderedmap_test.go index 6a06899..07b469d 100644 --- a/orderedmap_test.go +++ b/orderedmap_test.go @@ -1,8 +1,10 @@ package orderedmap import ( + "bytes" "encoding/json" "fmt" + "math" "sort" "strings" "testing" @@ -395,11 +397,31 @@ func TestUnmarshalJSONStruct(t *testing.T) { x, ok := v.Data.Get("x") if !ok { t.Errorf("missing expected key") - } else if x != float64(1) { + } else if x != json.Number("1") { t.Errorf("unexpected value: %#v", x) } } +func TestRemarshalJSON(t *testing.T) { + const input = `{"data":{"x":9007199254740993}}` + + o := New() + + err := json.Unmarshal([]byte(input), &o) + if err != nil { + t.Fatalf("JSON unmarshal error: %v", err) + } + + marshalled, err := json.Marshal(o) + if err != nil { + t.Errorf("Marshal failed: %v", err) + } + + if string(marshalled) != input { + t.Errorf("unexpected value: %s instead of %s", marshalled, input) + } +} + func TestOrderedMap_SortKeys(t *testing.T) { s := ` { @@ -438,7 +460,15 @@ func TestOrderedMap_Sort(t *testing.T) { o := New() json.Unmarshal([]byte(s), &o) o.Sort(func(a *Pair, b *Pair) bool { - return a.value.(float64) > b.value.(float64) + na, err := a.value.(json.Number).Float64() + if err != nil { + panic(err) + } + nb, err := b.value.(json.Number).Float64() + if err != nil { + panic(err) + } + return na > nb }) // Check the root keys @@ -486,3 +516,23 @@ func TestOrderedMap_empty_map(t *testing.T) { t.Error("Got", marshalledStr) } } + +func TestOrderedMap_uint64_slice_serialization(t *testing.T) { + uints := []uint64{math.MaxUint64, math.MaxUint64-100, math.MaxUint64-1000} + + str1 := []byte(fmt.Sprintf(`{"key":[%d,%d,%d]}`, uints[0], uints[1], uints[2])) + om := New() + + if err := json.Unmarshal(str1, om); err != nil { + t.Fatal(err) + } + + str2, err := json.Marshal(om) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(str1, str2) { + t.Fatalf("%s != %s", str1, str2) + } +}