Files
mihomo/common/orderedmap/json.go

140 lines
3.4 KiB
Go

package orderedmap
import (
"bytes"
"encoding"
"encoding/json"
"errors"
"fmt"
"reflect"
)
var (
_ json.Marshaler = &OrderedMap[int, any]{}
_ json.Unmarshaler = &OrderedMap[int, any]{}
)
// MarshalJSON implements the json.Marshaler interface.
func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error) { //nolint:funlen
if om == nil || om.list == nil {
return []byte("null"), nil
}
var buf bytes.Buffer
buf.WriteByte('{')
enc := json.NewEncoder(&buf)
for pair, firstIteration := om.Oldest(), true; pair != nil; pair = pair.Next() {
if firstIteration {
firstIteration = false
} else {
buf.WriteByte(',')
}
switch key := any(pair.Key).(type) {
case string, encoding.TextMarshaler:
if err := enc.Encode(pair.Key); err != nil {
return nil, err
}
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
buf.WriteByte('"')
buf.WriteString(fmt.Sprint(key))
buf.WriteByte('"')
default:
// this switch takes care of wrapper types around primitive types, such as
// type myType string
switch keyValue := reflect.ValueOf(key); keyValue.Type().Kind() {
case reflect.String:
if err := enc.Encode(pair.Key); err != nil {
return nil, err
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
buf.WriteByte('"')
buf.WriteString(fmt.Sprint(key))
buf.WriteByte('"')
default:
return nil, fmt.Errorf("unsupported key type: %T", key)
}
}
buf.WriteByte(':')
if err := enc.Encode(pair.Value); err != nil {
return nil, err
}
}
buf.WriteByte('}')
return buf.Bytes(), nil
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error {
if om.list == nil {
om.initialize(0)
}
d := json.NewDecoder(bytes.NewReader(data))
tok, err := d.Token()
if err != nil {
return err
}
if tok != json.Delim('{') {
return errors.New("expect JSON object open with '{'")
}
for d.More() {
// key
tok, err = d.Token()
if err != nil {
return err
}
keyStr, ok := tok.(string)
if !ok {
return fmt.Errorf("key must be a string, got %T\n", tok)
}
var key K
switch typedKey := any(&key).(type) {
case *string:
*typedKey = keyStr
case encoding.TextUnmarshaler:
err = typedKey.UnmarshalText([]byte(keyStr))
case *int, *int8, *int16, *int32, *int64, *uint, *uint8, *uint16, *uint32, *uint64:
err = json.Unmarshal([]byte(keyStr), typedKey)
default:
// this switch takes care of wrapper types around primitive types, such as
// type myType string
switch reflect.TypeOf(key).Kind() {
case reflect.String:
convertedKeyData := reflect.ValueOf(keyStr).Convert(reflect.TypeOf(key))
reflect.ValueOf(&key).Elem().Set(convertedKeyData)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
err = json.Unmarshal([]byte(keyStr), &key)
default:
err = fmt.Errorf("unsupported key type: %T", key)
}
}
if err != nil {
return err
}
// value
value, _ := om.Get(key)
err = d.Decode(&value)
if err != nil {
return err
}
om.Set(key, value)
}
tok, err = d.Token()
if err != nil {
return err
}
if tok != json.Delim('}') {
return errors.New("expect JSON object close with '}'")
}
return nil
}