json.go

  1package orderedmap
  2
  3import (
  4	"bytes"
  5	"encoding"
  6	"encoding/json"
  7	"fmt"
  8	"reflect"
  9	"unicode/utf8"
 10
 11	"github.com/buger/jsonparser"
 12	"github.com/mailru/easyjson/jwriter"
 13)
 14
 15var (
 16	_ json.Marshaler   = &OrderedMap[int, any]{}
 17	_ json.Unmarshaler = &OrderedMap[int, any]{}
 18)
 19
 20// MarshalJSON implements the json.Marshaler interface.
 21func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error) { //nolint:funlen
 22	if om == nil || om.list == nil {
 23		return []byte("null"), nil
 24	}
 25
 26	writer := jwriter.Writer{}
 27	writer.RawByte('{')
 28
 29	for pair, firstIteration := om.Oldest(), true; pair != nil; pair = pair.Next() {
 30		if firstIteration {
 31			firstIteration = false
 32		} else {
 33			writer.RawByte(',')
 34		}
 35
 36		switch key := any(pair.Key).(type) {
 37		case string:
 38			writer.String(key)
 39		case encoding.TextMarshaler:
 40			writer.RawByte('"')
 41			writer.Raw(key.MarshalText())
 42			writer.RawByte('"')
 43		case int:
 44			writer.IntStr(key)
 45		case int8:
 46			writer.Int8Str(key)
 47		case int16:
 48			writer.Int16Str(key)
 49		case int32:
 50			writer.Int32Str(key)
 51		case int64:
 52			writer.Int64Str(key)
 53		case uint:
 54			writer.UintStr(key)
 55		case uint8:
 56			writer.Uint8Str(key)
 57		case uint16:
 58			writer.Uint16Str(key)
 59		case uint32:
 60			writer.Uint32Str(key)
 61		case uint64:
 62			writer.Uint64Str(key)
 63		default:
 64
 65			// this switch takes care of wrapper types around primitive types, such as
 66			// type myType string
 67			switch keyValue := reflect.ValueOf(key); keyValue.Type().Kind() {
 68			case reflect.String:
 69				writer.String(keyValue.String())
 70			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 71				writer.Int64Str(keyValue.Int())
 72			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 73				writer.Uint64Str(keyValue.Uint())
 74			default:
 75				return nil, fmt.Errorf("unsupported key type: %T", key)
 76			}
 77		}
 78
 79		writer.RawByte(':')
 80		// the error is checked at the end of the function
 81		writer.Raw(json.Marshal(pair.Value)) //nolint:errchkjson
 82	}
 83
 84	writer.RawByte('}')
 85
 86	return dumpWriter(&writer)
 87}
 88
 89func dumpWriter(writer *jwriter.Writer) ([]byte, error) {
 90	if writer.Error != nil {
 91		return nil, writer.Error
 92	}
 93
 94	var buf bytes.Buffer
 95	buf.Grow(writer.Size())
 96	if _, err := writer.DumpTo(&buf); err != nil {
 97		return nil, err
 98	}
 99
100	return buf.Bytes(), nil
101}
102
103// UnmarshalJSON implements the json.Unmarshaler interface.
104func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error {
105	if om.list == nil {
106		om.initialize(0)
107	}
108
109	return jsonparser.ObjectEach(
110		data,
111		func(keyData []byte, valueData []byte, dataType jsonparser.ValueType, offset int) error {
112			if dataType == jsonparser.String {
113				// jsonparser removes the enclosing quotes; we need to restore them to make a valid JSON
114				valueData = data[offset-len(valueData)-2 : offset]
115			}
116
117			var key K
118			var value V
119
120			switch typedKey := any(&key).(type) {
121			case *string:
122				s, err := decodeUTF8(keyData)
123				if err != nil {
124					return err
125				}
126				*typedKey = s
127			case encoding.TextUnmarshaler:
128				if err := typedKey.UnmarshalText(keyData); err != nil {
129					return err
130				}
131			case *int, *int8, *int16, *int32, *int64, *uint, *uint8, *uint16, *uint32, *uint64:
132				if err := json.Unmarshal(keyData, typedKey); err != nil {
133					return err
134				}
135			default:
136				// this switch takes care of wrapper types around primitive types, such as
137				// type myType string
138				switch reflect.TypeOf(key).Kind() {
139				case reflect.String:
140					s, err := decodeUTF8(keyData)
141					if err != nil {
142						return err
143					}
144
145					convertedKeyData := reflect.ValueOf(s).Convert(reflect.TypeOf(key))
146					reflect.ValueOf(&key).Elem().Set(convertedKeyData)
147				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
148					reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
149					if err := json.Unmarshal(keyData, &key); err != nil {
150						return err
151					}
152				default:
153					return fmt.Errorf("unsupported key type: %T", key)
154				}
155			}
156
157			if err := json.Unmarshal(valueData, &value); err != nil {
158				return err
159			}
160
161			om.Set(key, value)
162			return nil
163		})
164}
165
166func decodeUTF8(input []byte) (string, error) {
167	remaining, offset := input, 0
168	runes := make([]rune, 0, len(remaining))
169
170	for len(remaining) > 0 {
171		r, size := utf8.DecodeRune(remaining)
172		if r == utf8.RuneError && size <= 1 {
173			return "", fmt.Errorf("not a valid UTF-8 string (at position %d): %s", offset, string(input))
174		}
175
176		runes = append(runes, r)
177		remaining = remaining[size:]
178		offset += size
179	}
180
181	return string(runes), nil
182}