|
- package serder
-
- import (
- "encoding/json"
- "fmt"
- "io"
- "reflect"
- "strings"
-
- myreflect "gitlink.org.cn/cloudream/common/utils/reflect"
- )
-
- func ObjectToJSON(obj any) ([]byte, error) {
- return json.Marshal(obj)
- }
-
- func ObjectToJSONStream(obj any) io.ReadCloser {
- pr, pw := io.Pipe()
- enc := json.NewEncoder(pw)
-
- go func() {
- err := enc.Encode(obj)
- if err != nil && err != io.EOF {
- pw.CloseWithError(err)
- } else {
- pw.Close()
- }
- }()
-
- return pr
- }
-
- func JSONToObject(data []byte, obj any) error {
- return json.Unmarshal(data, obj)
- }
-
- func JSONToObjectStream(str io.Reader, obj any) error {
- dec := json.NewDecoder(str)
- err := dec.Decode(obj)
- if err != io.EOF {
- return err
- }
-
- return nil
- }
-
- type TypeResolver interface {
- TypeToString(typ reflect.Type) (string, error)
- StringToType(typeStr string) (reflect.Type, error)
- }
-
- type UnionTypeInfo struct {
- UnionType reflect.Type
- TypeFieldName string
- ElementTypes TypeResolver
- }
-
- func NewTypeUnion[TU any](typeField string, eleTypes TypeResolver) UnionTypeInfo {
- return UnionTypeInfo{
- UnionType: myreflect.TypeOf[TU](),
- TypeFieldName: typeField,
- ElementTypes: eleTypes,
- }
- }
-
- type MapToObjectOption struct {
- UnionTypes []UnionTypeInfo // 转换过程中遇到这些类型时,会依据指定的字段的值,来决定转换后的实际类型
- }
-
- func MapToObject(m map[string]any, obj any, opt ...MapToObjectOption) error {
- var op MapToObjectOption
- if len(opt) > 0 {
- op = opt[0]
- }
-
- unionTypeMapping := make(map[reflect.Type]*UnionTypeInfo)
-
- for _, u := range op.UnionTypes {
- unionTypeMapping[u.UnionType] = &u
- }
-
- convs := []Converter{
- func(from reflect.Value, to reflect.Value) (interface{}, error) {
- info, ok := unionTypeMapping[to.Type()]
- if !ok {
- return from.Interface(), nil
- }
-
- mp := from.Interface().(map[string]any)
- tag, ok := mp[info.TypeFieldName]
- if !ok {
- return nil, fmt.Errorf("converting to %v: no tag field %s in map", to.Type(), info.TypeFieldName)
- }
-
- tagStr, ok := tag.(string)
- if !ok {
- return nil, fmt.Errorf("converting to %v: tag field %s value is %v, which is not a string", to.Type(), info.TypeFieldName, tag)
- }
-
- eleType, err := info.ElementTypes.StringToType(tagStr)
- if err != nil {
- return nil, fmt.Errorf("converting to %v: %w", to.Type(), err)
- }
-
- to.Set(reflect.Indirect(reflect.New(eleType)))
-
- return from.Interface(), nil
- },
- }
-
- return AnyToAny(m, obj, AnyToAnyOption{
- Converters: convs,
- })
- }
-
- func ObjectToMap(obj any) (map[string]any, error) {
- ctx := WalkValue(obj, func(ctx *WalkContext, event WalkEvent) WalkingOp {
- switch e := event.(type) {
- case StructBeginEvent:
- mp := make(map[string]any)
- ctx.StackPush(mp)
-
- case StructArriveFieldEvent:
- if !WillWalkInto(e.Value) {
- ctx.StackPush(e.Value.Interface())
- }
- case StructLeaveFieldEvent:
- val := ctx.StackPop()
- mp := ctx.StackPeek().(map[string]any)
- jsonTag := e.Info.Tag.Get("json")
- if jsonTag == "-" {
- break
- }
-
- opts := strings.Split(jsonTag, ",")
- keyName := opts[0]
- if keyName == "" {
- keyName = e.Info.Name
- }
-
- if contains(opts, "string", 1) {
- val = fmt.Sprintf("%v", val)
- }
-
- mp[keyName] = val
-
- case StructEndEvent:
-
- case MapBeginEvent:
- ctx.StackPush(make(map[string]any))
- case MapArriveEntryEvent:
- if !WillWalkInto(e.Value) {
- ctx.StackPush(e.Value.Interface())
- }
- case MapLeaveEntryEvent:
- val := ctx.StackPop()
- mp := ctx.StackPeek().(map[string]any)
- mp[fmt.Sprintf("%v", e.Key)] = val
- case MapEndEvent:
-
- case ArrayBeginEvent:
- ctx.StackPush(make([]any, e.Value.Len()))
- case ArrayArriveElementEvent:
- if !WillWalkInto(e.Value) {
- ctx.StackPush(e.Value.Interface())
- }
- case ArrayLeaveElementEvent:
- val := ctx.StackPop()
- arr := ctx.StackPeek().([]any)
- arr[e.Index] = val
- case ArrayEndEvent:
- }
-
- return Next
-
- }, WalkOption{
- StackValues: []any{make(map[string]any)},
- })
-
- return ctx.StackPop().(map[string]any), nil
- }
-
- func contains(arr []string, ele string, startIndex int) bool {
- for i := startIndex; i < len(arr); i++ {
- if arr[i] == ele {
- return true
- }
- }
-
- return false
- }
|