package serder import ( "encoding/json" "fmt" "io" "reflect" "strings" "gitlink.org.cn/cloudream/common/pkgs/types" ) 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 TaggedUnionType struct { Union types.TypeUnion StrcutTagField string JSONTagField string TagToType map[string]reflect.Type } // 根据指定的字段的值来区分不同的类型。值可以通过在字段增加“union”Tag来指定。如果没有指定,则使用类型名。 func NewTaggedTypeUnion(union types.TypeUnion, structTagField string, jsonTagField string) TaggedUnionType { tagToType := make(map[string]reflect.Type) for _, typ := range union.ElementTypes { if structTagField == "" { tagToType[typ.Name()] = typ continue } field, ok := typ.FieldByName(structTagField) if !ok { tagToType[typ.Name()] = typ continue } tag := field.Tag.Get("union") if tag == "" { tagToType[typ.Name()] = typ continue } tagToType[tag] = typ } return TaggedUnionType{ Union: union, StrcutTagField: structTagField, JSONTagField: jsonTagField, TagToType: tagToType, } } type MapToObjectOption struct { UnionTypes []TaggedUnionType // 转换过程中遇到这些类型时,会依据指定的字段的值,来决定转换后的实际类型 } 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]*TaggedUnionType) for _, u := range op.UnionTypes { uu := u unionTypeMapping[u.Union.UnionType] = &uu } convs := []Converter{ func(from reflect.Value, to reflect.Value) (interface{}, error) { toType := to.Type() info, ok := unionTypeMapping[toType] if !ok { return from.Interface(), nil } mp := from.Interface().(map[string]any) tag, ok := mp[info.JSONTagField] if !ok { return nil, fmt.Errorf("converting to %v: no tag field %s in map", toType, info.JSONTagField) } tagStr, ok := tag.(string) if !ok { return nil, fmt.Errorf("converting to %v: tag field %s value is %v, which is not a string", toType, info.JSONTagField, tag) } eleType, ok := info.TagToType[tagStr] if !ok { return nil, fmt.Errorf("converting to %v: unknow type tag %s", toType, tagStr) } to.Set(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 }