Browse Source

Merge branch 'feature_gxh'

pull/36/head
Sydonian 2 years ago
parent
commit
bd678e8060
10 changed files with 639 additions and 453 deletions
  1. +7
    -292
      pkgs/mq/message.go
  2. +5
    -21
      pkgs/mq/message_test.go
  3. +5
    -0
      pkgs/types/types.go
  4. +7
    -1
      pkgs/types/union.go
  5. +14
    -26
      sdks/scheduler/models.go
  6. +13
    -9
      sdks/unifyops/models.go
  7. +55
    -73
      utils/serder/serder.go
  8. +195
    -31
      utils/serder/serder_test.go
  9. +2
    -0
      utils/serder/types.go
  10. +336
    -0
      utils/serder/union_handler.go

+ 7
- 292
pkgs/mq/message.go View File

@@ -1,16 +1,9 @@
package mq package mq


import ( import (
"bytes"
"fmt"
"reflect"
"strings"
"unsafe"

jsoniter "github.com/json-iterator/go"
"github.com/modern-go/reflect2"
"gitlink.org.cn/cloudream/common/pkgs/types" "gitlink.org.cn/cloudream/common/pkgs/types"
myreflect "gitlink.org.cn/cloudream/common/utils/reflect" myreflect "gitlink.org.cn/cloudream/common/utils/reflect"
"gitlink.org.cn/cloudream/common/utils/serder"
) )


const ( const (
@@ -86,298 +79,20 @@ func MakeHeartbeatMessage() Message {
return msg return msg
} }


type TypeUnionWithTypeName struct {
Union types.TypeUnion
TypeNameToType map[string]myreflect.Type
}

func (u *TypeUnionWithTypeName) Register(typ myreflect.Type) {
u.Union.ElementTypes = append(msgBodyTypeUnion.Union.ElementTypes, typ)
u.TypeNameToType[makeFullTypeName(typ)] = typ
}

var msgBodyTypeUnion *TypeUnionWithTypeName
var msgBodyTypeUnion = serder.UseTypeUnionExternallyTagged(types.Ref(types.NewTypeUnion[MessageBody]()))


// 所有新定义的Message都需要在init中调用此函数 // 所有新定义的Message都需要在init中调用此函数
func RegisterMessage[T MessageBody]() { func RegisterMessage[T MessageBody]() {
msgBodyTypeUnion.Register(myreflect.TypeOf[T]())
}

// 在序列化结构体中包含的UnionType类型字段时,会将字段值的实际类型保存在序列化后的结果中。
// 在反序列化时,会根据类型信息重建原本的字段值。
// 注:TypeUnion.UnionType必须是一个interface
func RegisterUnionType(union types.TypeUnion) *TypeUnionWithTypeName {
myUnion := &TypeUnionWithTypeName{
Union: union,
TypeNameToType: make(map[string]reflect.Type),
}

for _, typ := range union.ElementTypes {
myUnion.TypeNameToType[makeFullTypeName(typ)] = typ
}

if union.UnionType.NumMethod() == 0 {
registerForEFace(myUnion)
} else {
registerForIFace(myUnion)
}

return myUnion
}

// 无方法的interface类型
func registerForEFace(myUnion *TypeUnionWithTypeName) {
jsoniter.RegisterTypeEncoderFunc(myUnion.Union.UnionType.String(),
func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
// 无方法的interface底层数据结构都是eface类型,所以可以直接转*any
val := *(*any)(ptr)
if val != nil {
stream.WriteArrayStart()

valType := myreflect.TypeOfValue(val)
if !myUnion.Union.Include(valType) {
stream.Error = fmt.Errorf("type %v is not in union %v", valType, myUnion.Union.UnionType)
return
}

stream.WriteString(makeFullTypeName(valType))
stream.WriteRaw(",")
stream.WriteVal(val)
stream.WriteArrayEnd()
} else {
stream.WriteNil()
}
},
func(p unsafe.Pointer) bool {
return false
})

jsoniter.RegisterTypeDecoderFunc(myUnion.Union.UnionType.String(),
func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
// 无方法的interface底层都是eface结构体,所以可以直接转*any
vp := (*any)(ptr)

nextTkType := iter.WhatIsNext()
if nextTkType == jsoniter.NilValue {
iter.ReadNil()
*vp = nil

} else if nextTkType == jsoniter.ArrayValue {
iter.ReadArray()
typeStr := iter.ReadString()
iter.ReadArray()

typ, ok := myUnion.TypeNameToType[typeStr]
if !ok {
iter.ReportError("decode UnionType", fmt.Sprintf("unknow type string %s under %v", typeStr, myUnion.Union.UnionType))
return
}

// 如果目标类型已经是个指针类型*T,那么在New的时候就需要使用T,
// 否则New出来的是会是**T,这将导致后续的反序列化出问题
if typ.Kind() == reflect.Pointer {
val := reflect.New(typ.Elem())
iter.ReadVal(val.Interface())
*vp = val.Interface()

} else {
val := reflect.New(typ)
iter.ReadVal(val.Interface())
*vp = val.Elem().Interface()
}

iter.ReadArray()
} else {
iter.ReportError("decode UnionType", fmt.Sprintf("unknow next token type %v", nextTkType))
return
}
})
}

// 有方法的interface类型
func registerForIFace(myUnion *TypeUnionWithTypeName) {
jsoniter.RegisterTypeEncoderFunc(myUnion.Union.UnionType.String(),
func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
// 有方法的interface底层都是iface结构体,可以将其转成eface,转换后不损失类型信息
val := reflect2.IFaceToEFace(ptr)
if val != nil {
stream.WriteArrayStart()

valType := myreflect.TypeOfValue(val)
if !myUnion.Union.Include(valType) {
stream.Error = fmt.Errorf("type %v is not in union %v", valType, myUnion.Union.UnionType)
return
}

stream.WriteString(makeFullTypeName(valType))
stream.WriteRaw(",")
stream.WriteVal(val)
stream.WriteArrayEnd()
} else {
stream.WriteNil()
}
},
func(p unsafe.Pointer) bool {
return false
})

jsoniter.RegisterTypeDecoderFunc(myUnion.Union.UnionType.String(),
func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {

nextTkType := iter.WhatIsNext()
if nextTkType == jsoniter.NilValue {
iter.ReadNil()

} else if nextTkType == jsoniter.ArrayValue {
iter.ReadArray()
typeStr := iter.ReadString()
iter.ReadArray()

typ, ok := myUnion.TypeNameToType[typeStr]
if !ok {
iter.ReportError("decode UnionType", fmt.Sprintf("unknow type string %s under %v", typeStr, myUnion.Union.UnionType))
return
}

// 如果目标类型已经是个指针类型*T,那么在New的时候就需要使用T,
// 否则New出来的是会是**T,这将导致后续的反序列化出问题
if typ.Kind() == reflect.Pointer {
val := reflect.New(typ.Elem())
iter.ReadVal(val.Interface())

retVal := reflect.NewAt(myUnion.Union.UnionType, ptr)
retVal.Elem().Set(val)

} else {
val := reflect.New(typ)
iter.ReadVal(val.Interface())

retVal := reflect.NewAt(myUnion.Union.UnionType, ptr)
retVal.Elem().Set(val.Elem())
}

iter.ReadArray()
} else {
iter.ReportError("decode UnionType", fmt.Sprintf("unknow next token type %v", nextTkType))
return
}
})
}

func makeFullTypeName(typ myreflect.Type) string {
refs := 0

realType := typ
for realType.Kind() == reflect.Pointer {
refs++
realType = realType.Elem()
}

return fmt.Sprintf("%s%s.%s", strings.Repeat("*", refs), realType.PkgPath(), realType.Name())
}

/*
// 如果对一个类型T调用了此函数,那么在序列化结构体中包含的T类型字段时,
// 会将字段值的实际类型保存在序列化后的结果中
// 在反序列化时,会根据类型信息重建原本的字段值。
//
// 只会处理types指定的类型。
func RegisterTypeSet[T any](types ...myreflect.Type) *serder.UnionTypeInfo {
eleTypes := serder.NewTypeNameResolver(true)
set := serder.UnionTypeInfo{
UnionType: myreflect.TypeOf[T](),
ElementTypes: eleTypes,
}

for _, t := range types {
eleTypes.Register(t)
err := msgBodyTypeUnion.Add(myreflect.TypeOf[T]())
if err != nil {
panic(err)
} }

TODO 暂时保留这一段代码,如果RegisterUnionType中的非泛型版本出了问题,则重新使用这一部分的代码
unionTypes[set.UnionType] = set

jsoniter.RegisterTypeEncoderFunc(myreflect.TypeOf[T]().String(),
func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
val := *((*T)(ptr))
var ifVal any = val

if ifVal != nil {
stream.WriteArrayStart()
typeStr, err := set.ElementTypes.TypeToString(myreflect.TypeOfValue(val))
if err != nil {
stream.Error = err
return
}
stream.WriteString(typeStr)
stream.WriteRaw(",")
stream.WriteVal(val)
stream.WriteArrayEnd()
} else {
stream.WriteNil()
}
},
func(p unsafe.Pointer) bool {
return false
})

jsoniter.RegisterTypeDecoderFunc(myreflect.TypeOf[T]().String(),
func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
vp := (*T)(ptr)

nextTkType := iter.WhatIsNext()
if nextTkType == jsoniter.NilValue {
iter.ReadNil()
var zero T
*vp = zero
} else if nextTkType == jsoniter.ArrayValue {
iter.ReadArray()
typeStr := iter.ReadString()
iter.ReadArray()

typ, err := set.ElementTypes.StringToType(typeStr)
if err != nil {
iter.ReportError("get type from string", err.Error())
return
}

val := reflect.New(typ)
iter.ReadVal(val.Interface())
*vp = val.Elem().Interface().(T)

iter.ReadArray()
} else {
iter.ReportError("parse TypeSet field", fmt.Sprintf("unknow next token type %v", nextTkType))
return
}
})
RegisterUnionType(serder.NewTypeUnion[T]("", serder.NewTypeNameResolver(true)))
return &set
} }
*/


func Serialize(msg Message) ([]byte, error) { func Serialize(msg Message) ([]byte, error) {
buf := bytes.NewBuffer(nil)
enc := jsoniter.NewEncoder(buf)
err := enc.Encode(msg)
if err != nil {
return nil, err
}

return buf.Bytes(), nil
return serder.ObjectToJSONEx(msg)
} }


func Deserialize(data []byte) (*Message, error) { func Deserialize(data []byte) (*Message, error) {
dec := jsoniter.NewDecoder(bytes.NewBuffer(data))

var msg Message
err := dec.Decode(&msg)
if err != nil {
return nil, err
}

return &msg, nil
}

func init() {
msgBodyTypeUnion = RegisterUnionType(types.NewTypeUnion[MessageBody]())
return serder.JSONToObjectEx[*Message](data)
} }

+ 5
- 21
pkgs/mq/message_test.go View File

@@ -3,7 +3,6 @@ package mq
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"reflect"
"testing" "testing"
"unsafe" "unsafe"


@@ -11,6 +10,7 @@ import (
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
"gitlink.org.cn/cloudream/common/pkgs/types" "gitlink.org.cn/cloudream/common/pkgs/types"
myreflect "gitlink.org.cn/cloudream/common/utils/reflect" myreflect "gitlink.org.cn/cloudream/common/utils/reflect"
"gitlink.org.cn/cloudream/common/utils/serder"
) )


func TestMessage(t *testing.T) { func TestMessage(t *testing.T) {
@@ -146,7 +146,7 @@ func TestMessage(t *testing.T) {
Value MyTypeUnion Value MyTypeUnion
} }
RegisterMessage[*Body]() RegisterMessage[*Body]()
RegisterUnionType(types.NewTypeUnion[MyTypeUnion]((*EleType1)(nil)))
serder.UseTypeUnionExternallyTagged(types.Ref(types.NewTypeUnion[MyTypeUnion]((*EleType1)(nil))))


msg := MakeAppDataMessage(&Body{Value: &EleType1{ msg := MakeAppDataMessage(&Body{Value: &EleType1{
Value: 1, Value: 1,
@@ -174,7 +174,7 @@ func TestMessage(t *testing.T) {
Value MyTypeUnion Value MyTypeUnion
} }
RegisterMessage[*Body]() RegisterMessage[*Body]()
RegisterUnionType(types.NewTypeUnion[MyTypeUnion]((*EleType1)(nil)))
serder.UseTypeUnionExternallyTagged(types.Ref(types.NewTypeUnion[MyTypeUnion]((*EleType1)(nil))))


msg := MakeAppDataMessage(&Body{Value: &EleType1{ msg := MakeAppDataMessage(&Body{Value: &EleType1{
Value: 1, Value: 1,
@@ -200,7 +200,7 @@ func TestMessage(t *testing.T) {
Value MyTypeUnion Value MyTypeUnion
} }
RegisterMessage[*Body]() RegisterMessage[*Body]()
RegisterUnionType(types.NewTypeUnion[MyTypeUnion]())
serder.UseTypeUnionExternallyTagged(types.Ref(types.NewTypeUnion[MyTypeUnion]()))


msg := MakeAppDataMessage(&Body{Value: nil}) msg := MakeAppDataMessage(&Body{Value: nil})
data, err := Serialize(msg) data, err := Serialize(msg)
@@ -220,26 +220,10 @@ func TestMessage(t *testing.T) {
Value MyTypeUnion Value MyTypeUnion
} }
RegisterMessage[*Body]() RegisterMessage[*Body]()
RegisterUnionType(types.NewTypeUnion[MyTypeUnion]())
serder.UseTypeUnionExternallyTagged(types.Ref(types.NewTypeUnion[MyTypeUnion]()))


msg := MakeAppDataMessage(&Body{Value: &struct{}{}}) msg := MakeAppDataMessage(&Body{Value: &struct{}{}})
_, err := Serialize(msg) _, err := Serialize(msg)
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
}) })

}

func TestMakeFullTypeName(t *testing.T) {
Convey("指针类型", t, func() {
type St struct{}

typeName := makeFullTypeName(myreflect.TypeOf[St]())
So(typeName, ShouldEqual, "gitlink.org.cn/cloudream/common/pkgs/mq.St")

typeName = makeFullTypeName(reflect.PointerTo(myreflect.TypeOf[St]()))
So(typeName, ShouldEqual, "*gitlink.org.cn/cloudream/common/pkgs/mq.St")

typeName = makeFullTypeName(reflect.PointerTo(reflect.PointerTo(myreflect.TypeOf[St]())))
So(typeName, ShouldEqual, "**gitlink.org.cn/cloudream/common/pkgs/mq.St")
})
} }

+ 5
- 0
pkgs/types/types.go View File

@@ -0,0 +1,5 @@
package types

func Ref[T any](val T) *T {
return &val
}

+ 7
- 1
pkgs/types/union.go View File

@@ -1,6 +1,7 @@
package types package types


import ( import (
"fmt"
"reflect" "reflect"


myreflect "gitlink.org.cn/cloudream/common/utils/reflect" myreflect "gitlink.org.cn/cloudream/common/utils/reflect"
@@ -37,6 +38,11 @@ func (u *TypeUnion) Include(typ myreflect.Type) bool {
return false return false
} }


func (u *TypeUnion) Add(typ myreflect.Type) {
func (u *TypeUnion) Add(typ myreflect.Type) error {
if !typ.AssignableTo(u.UnionType) {
return fmt.Errorf("type is not assignable to union type")
}

u.ElementTypes = append(u.ElementTypes, typ) u.ElementTypes = append(u.ElementTypes, typ)
return nil
} }

+ 14
- 26
sdks/scheduler/models.go View File

@@ -1,7 +1,6 @@
package schsdk package schsdk


import ( import (
"gitlink.org.cn/cloudream/common/pkgs/mq"
"gitlink.org.cn/cloudream/common/pkgs/types" "gitlink.org.cn/cloudream/common/pkgs/types"
cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
"gitlink.org.cn/cloudream/common/utils/serder" "gitlink.org.cn/cloudream/common/utils/serder"
@@ -38,8 +37,7 @@ var JobInfoTypeUnion = types.NewTypeUnion[JobInfo](
(*NormalJobInfo)(nil), (*NormalJobInfo)(nil),
(*ResourceJobInfo)(nil), (*ResourceJobInfo)(nil),
) )
var _ = serder.RegisterNewTaggedTypeUnion(JobInfoTypeUnion, "Type", "type")
var _ = mq.RegisterUnionType(JobInfoTypeUnion)
var _ = serder.UseTypeUnionInternallyTagged(&JobInfoTypeUnion, "type")


type JobInfoBase struct { type JobInfoBase struct {
LocalJobID string `json:"localJobID"` LocalJobID string `json:"localJobID"`
@@ -50,16 +48,18 @@ func (i *JobInfoBase) GetLocalJobID() string {
} }


type NormalJobInfo struct { type NormalJobInfo struct {
serder.Metadata `union:"Normal"`
JobInfoBase JobInfoBase
Type string `json:"type" union:"Normal"`
Type string `json:"type"`
Files JobFilesInfo `json:"files"` Files JobFilesInfo `json:"files"`
Runtime JobRuntimeInfo `json:"runtime"` Runtime JobRuntimeInfo `json:"runtime"`
Resources JobResourcesInfo `json:"resources"` Resources JobResourcesInfo `json:"resources"`
} }


type ResourceJobInfo struct { type ResourceJobInfo struct {
serder.Metadata `union:"Resource"`
JobInfoBase JobInfoBase
Type string `json:"type" union:"Resource"`
Type string `json:"type"`
BucketID int64 `json:"bucketID"` BucketID int64 `json:"bucketID"`
Redundancy cdssdk.TypedRedundancyInfo `json:"redundancy"` Redundancy cdssdk.TypedRedundancyInfo `json:"redundancy"`
TargetLocalJobID string `json:"targetLocalJobID"` TargetLocalJobID string `json:"targetLocalJobID"`
@@ -81,34 +81,37 @@ var FileInfoTypeUnion = types.NewTypeUnion[JobFileInfo](
(*ResourceJobFileInfo)(nil), (*ResourceJobFileInfo)(nil),
(*ImageJobFileInfo)(nil), (*ImageJobFileInfo)(nil),
) )
var _ = serder.RegisterNewTaggedTypeUnion(FileInfoTypeUnion, "Type", "type")
var _ = mq.RegisterUnionType(FileInfoTypeUnion)
var _ = serder.UseTypeUnionInternallyTagged(&FileInfoTypeUnion, "type")


type JobFileInfoBase struct{} type JobFileInfoBase struct{}


func (i *JobFileInfoBase) Noop() {} func (i *JobFileInfoBase) Noop() {}


type PackageJobFileInfo struct { type PackageJobFileInfo struct {
serder.Metadata `union:"Package"`
JobFileInfoBase JobFileInfoBase
Type string `json:"type" union:"Package"`
Type string `json:"type"`
PackageID int64 `json:"packageID"` PackageID int64 `json:"packageID"`
} }


type LocalJobFileInfo struct { type LocalJobFileInfo struct {
serder.Metadata `union:"LocalFile"`
JobFileInfoBase JobFileInfoBase
Type string `json:"type" union:"LocalFile"`
Type string `json:"type"`
LocalPath string `json:"localPath"` LocalPath string `json:"localPath"`
} }


type ResourceJobFileInfo struct { type ResourceJobFileInfo struct {
serder.Metadata `union:"Resource"`
JobFileInfoBase JobFileInfoBase
Type string `json:"type" union:"Resource"`
Type string `json:"type"`
ResourceLocalJobID string `json:"resourceLocalJobID"` ResourceLocalJobID string `json:"resourceLocalJobID"`
} }


type ImageJobFileInfo struct { type ImageJobFileInfo struct {
serder.Metadata `union:"Image"`
JobFileInfoBase JobFileInfoBase
Type string `json:"type" union:"Image"`
Type string `json:"type"`
ImageID ImageID `json:"imageID"` ImageID ImageID `json:"imageID"`
} }


@@ -133,21 +136,6 @@ type JobResourcesInfo struct {
Memory int64 `json:"memory"` Memory int64 `json:"memory"`
} }


func JobSetInfoFromJSON(data []byte) (*JobSetInfo, error) {
mp := make(map[string]any)
if err := serder.JSONToObject(data, &mp); err != nil {
return nil, err
}

var ret JobSetInfo
err := serder.MapToObject(mp, &ret)
if err != nil {
return nil, err
}

return &ret, nil
}

type JobSetFilesUploadScheme struct { type JobSetFilesUploadScheme struct {
LocalFileSchemes []LocalFileUploadScheme `json:"localFileUploadSchemes"` LocalFileSchemes []LocalFileUploadScheme `json:"localFileUploadSchemes"`
} }


+ 13
- 9
sdks/unifyops/models.go View File

@@ -1,7 +1,6 @@
package uopsdk package uopsdk


import ( import (
"gitlink.org.cn/cloudream/common/pkgs/mq"
"gitlink.org.cn/cloudream/common/pkgs/types" "gitlink.org.cn/cloudream/common/pkgs/types"
"gitlink.org.cn/cloudream/common/utils/serder" "gitlink.org.cn/cloudream/common/utils/serder"
) )
@@ -37,8 +36,7 @@ var ResourceDataTypeUnion = types.NewTypeUnion[ResourceData](
(*StorageResourceData)(nil), (*StorageResourceData)(nil),
(*MemoryResourceData)(nil), (*MemoryResourceData)(nil),
) )
var _ = serder.RegisterNewTaggedTypeUnion(ResourceDataTypeUnion, "Name", "name")
var _ = mq.RegisterUnionType(ResourceDataTypeUnion)
var _ = serder.UseTypeUnionInternallyTagged(&ResourceDataTypeUnion, "name")


type ResourceDataBase struct{} type ResourceDataBase struct{}


@@ -50,8 +48,9 @@ type UnitValue[T any] struct {
} }


type CPUResourceData struct { type CPUResourceData struct {
serder.Metadata `union:"CPU"`
ResourceDataBase ResourceDataBase
Name ResourceType `json:"name" union:"CPU"`
Name ResourceType `json:"name"`
Total UnitValue[int64] `json:"total"` Total UnitValue[int64] `json:"total"`
Available UnitValue[int64] `json:"available"` Available UnitValue[int64] `json:"available"`
} }
@@ -65,8 +64,9 @@ func NewCPUResourceData(total UnitValue[int64], available UnitValue[int64]) *CPU
} }


type NPUResourceData struct { type NPUResourceData struct {
serder.Metadata `union:"NPU"`
ResourceDataBase ResourceDataBase
Name ResourceType `json:"name" union:"NPU"`
Name ResourceType `json:"name"`
Total UnitValue[int64] `json:"total"` Total UnitValue[int64] `json:"total"`
Available UnitValue[int64] `json:"available"` Available UnitValue[int64] `json:"available"`
} }
@@ -80,8 +80,9 @@ func NewNPUResourceData(total UnitValue[int64], available UnitValue[int64]) *NPU
} }


type GPUResourceData struct { type GPUResourceData struct {
serder.Metadata `union:"GPU"`
ResourceDataBase ResourceDataBase
Name ResourceType `json:"name" union:"GPU"`
Name ResourceType `json:"name"`
Total UnitValue[int64] `json:"total"` Total UnitValue[int64] `json:"total"`
Available UnitValue[int64] `json:"available"` Available UnitValue[int64] `json:"available"`
} }
@@ -95,8 +96,9 @@ func NewGPUResourceData(total UnitValue[int64], available UnitValue[int64]) *GPU
} }


type MLUResourceData struct { type MLUResourceData struct {
serder.Metadata `union:"MLU"`
ResourceDataBase ResourceDataBase
Name ResourceType `json:"name" union:"MLU"`
Name ResourceType `json:"name"`
Total UnitValue[int64] `json:"total"` Total UnitValue[int64] `json:"total"`
Available UnitValue[int64] `json:"available"` Available UnitValue[int64] `json:"available"`
} }
@@ -110,8 +112,9 @@ func NewMLUResourceData(total UnitValue[int64], available UnitValue[int64]) *MLU
} }


type StorageResourceData struct { type StorageResourceData struct {
serder.Metadata `union:"STORAGE"`
ResourceDataBase ResourceDataBase
Name ResourceType `json:"name" union:"STORAGE"`
Name ResourceType `json:"name"`
Total UnitValue[float64] `json:"total"` Total UnitValue[float64] `json:"total"`
Available UnitValue[float64] `json:"available"` Available UnitValue[float64] `json:"available"`
} }
@@ -125,8 +128,9 @@ func NewStorageResourceData(total UnitValue[float64], available UnitValue[float6
} }


type MemoryResourceData struct { type MemoryResourceData struct {
serder.Metadata `union:"MEMORY"`
ResourceDataBase ResourceDataBase
Name ResourceType `json:"name" union:"MEMORY"`
Name ResourceType `json:"name"`
Total UnitValue[float64] `json:"total"` Total UnitValue[float64] `json:"total"`
Available UnitValue[float64] `json:"available"` Available UnitValue[float64] `json:"available"`
} }


+ 55
- 73
utils/serder/serder.go View File

@@ -1,19 +1,64 @@
package serder package serder


import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
"strings" "strings"


"gitlink.org.cn/cloudream/common/pkgs/types"
jsoniter "github.com/json-iterator/go"
) )


var unionHandler = UnionHandler{
internallyTagged: make(map[reflect.Type]*TypeUnionInternallyTagged),
externallyTagged: make(map[reflect.Type]*TypeUnionExternallyTagged),
}

var defaultAPI = func() jsoniter.API {
api := jsoniter.Config{
EscapeHTML: true,
}.Froze()

api.RegisterExtension(&unionHandler)
return api
}()

// 将对象转为JSON字符串。支持TypeUnion。
func ObjectToJSONEx[T any](obj T) ([]byte, error) {
buf := new(bytes.Buffer)
enc := defaultAPI.NewEncoder(buf)
// 这里使用&obj而直接不使用obj的原因是,Encode的形参类型为any,
// 如果T是一个interface类型,将obj传递进去后,内部拿到的类型将会是obj的实际类型,
// 使用&obj,那么内部拿到的将会是*T类型,通过一层一层解引用查找Encoder时,能找到T对应的TypeUnion
err := enc.Encode(&obj)

if err != nil {
return nil, err
}

return buf.Bytes(), nil
}

// 将JSON字符串转为对象。支持TypeUnion。
func JSONToObjectEx[T any](data []byte) (T, error) {
var ret T
dec := defaultAPI.NewDecoder(bytes.NewReader(data))
err := dec.Decode(&ret)
if err != nil {
return ret, err
}

return ret, nil
}

// 将对象转为JSON字符串。如果需要支持解析TypeUnion类型,则使用"Ex"结尾的同名函数。
func ObjectToJSON(obj any) ([]byte, error) { func ObjectToJSON(obj any) ([]byte, error) {
return json.Marshal(obj) return json.Marshal(obj)
} }


// 将对象转为JSON字符串。如果需要支持解析TypeUnion类型,则使用"Ex"结尾的同名函数。
func ObjectToJSONStream(obj any) io.ReadCloser { func ObjectToJSONStream(obj any) io.ReadCloser {
pr, pw := io.Pipe() pr, pw := io.Pipe()
enc := json.NewEncoder(pw) enc := json.NewEncoder(pw)
@@ -30,10 +75,12 @@ func ObjectToJSONStream(obj any) io.ReadCloser {
return pr return pr
} }


// 将JSON字符串转为对象。如果需要支持解析TypeUnion类型,则使用"Ex"结尾的同名函数。
func JSONToObject(data []byte, obj any) error { func JSONToObject(data []byte, obj any) error {
return json.Unmarshal(data, obj) return json.Unmarshal(data, obj)
} }


// 将JSON字符串转为对象。如果需要支持解析TypeUnion类型,则使用"Ex"结尾的同名函数。
func JSONToObjectStream(str io.Reader, obj any) error { func JSONToObjectStream(str io.Reader, obj any) error {
dec := json.NewDecoder(str) dec := json.NewDecoder(str)
err := dec.Decode(obj) err := dec.Decode(obj)
@@ -49,86 +96,21 @@ type TypeResolver interface {
StringToType(typeStr string) (reflect.Type, error) StringToType(typeStr string) (reflect.Type, error)
} }


var registeredTaggedTypeUnions []*TaggedUnionType

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
}

// 如果ElementType是一个指向结构体的指针,那么就遍历结构体的字段(解引用)
structType := typ
for structType.Kind() == reflect.Pointer {
structType = structType.Elem()
}

field, ok := structType.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,
}
}

// 注册一个TaggedTypeUnion
func RegisterTaggedTypeUnion(union *TaggedUnionType) *TaggedUnionType {
registeredTaggedTypeUnions = append(registeredTaggedTypeUnions, union)
return union
}

// 创建并注册一个TaggedTypeUnion
func RegisterNewTaggedTypeUnion(union types.TypeUnion, structTagField string, jsonTagField string) *TaggedUnionType {
taggedUnion := NewTaggedTypeUnion(union, structTagField, jsonTagField)
RegisterTaggedTypeUnion(taggedUnion)
return taggedUnion
}

type MapToObjectOption struct { type MapToObjectOption struct {
UnionTypes []*TaggedUnionType // 转换过程中遇到这些类型时,会依据指定的字段的值,来决定转换后的实际类型
NoRegisteredUnionTypes bool // 是否不使用全局注册的UnionType
NoRegisteredUnionTypes bool // 是否不使用全局注册的UnionType
} }


// TODO 使用这个函数来处理TypeUnion的地方都可以直接使用Ex系列的函数
func MapToObject(m map[string]any, obj any, opt ...MapToObjectOption) error { func MapToObject(m map[string]any, obj any, opt ...MapToObjectOption) error {
var op MapToObjectOption var op MapToObjectOption
if len(opt) > 0 { if len(opt) > 0 {
op = opt[0] op = opt[0]
} }


unionTypeMapping := make(map[reflect.Type]*TaggedUnionType)

for _, u := range op.UnionTypes {
unionTypeMapping[u.Union.UnionType] = u
}
unionTypeMapping := make(map[reflect.Type]*TypeUnionInternallyTagged)


if !op.NoRegisteredUnionTypes { if !op.NoRegisteredUnionTypes {
for _, u := range registeredTaggedTypeUnions {
for _, u := range unionHandler.internallyTagged {
unionTypeMapping[u.Union.UnionType] = u unionTypeMapping[u.Union.UnionType] = u
} }
} }
@@ -142,14 +124,14 @@ func MapToObject(m map[string]any, obj any, opt ...MapToObjectOption) error {
} }


mp := from.Interface().(map[string]any) mp := from.Interface().(map[string]any)
tag, ok := mp[info.JSONTagField]
tag, ok := mp[info.TagField]
if !ok { if !ok {
return nil, fmt.Errorf("converting to %v: no tag field %s in map", toType, info.JSONTagField)
return nil, fmt.Errorf("converting to %v: no tag field %s in map", toType, info.TagField)
} }


tagStr, ok := tag.(string) tagStr, ok := tag.(string)
if !ok { if !ok {
return nil, fmt.Errorf("converting to %v: tag field %s value is %v, which is not a string", toType, info.JSONTagField, tag)
return nil, fmt.Errorf("converting to %v: tag field %s value is %v, which is not a string", toType, info.TagField, tag)
} }


eleType, ok := info.TagToType[tagStr] eleType, ok := info.TagToType[tagStr]


+ 195
- 31
utils/serder/serder_test.go View File

@@ -363,13 +363,15 @@ func Test_MapToObject(t *testing.T) {
type UnionType interface{} type UnionType interface{}


type EleType1 struct { type EleType1 struct {
Type EleType `json:"type" union:"1"`
Value1 string `json:"value1"`
Metadata `union:"1"`
Type EleType `json:"type"`
Value1 string `json:"value1"`
} }


type EleType2 struct { type EleType2 struct {
Type EleType `json:"type" union:"2"`
Value2 int `json:"value2"`
Metadata `union:"2"`
Type EleType `json:"type"`
Value2 int `json:"value2"`
} }


type St struct { type St struct {
@@ -390,17 +392,12 @@ func Test_MapToObject(t *testing.T) {
} }


var ret St var ret St
err := MapToObject(mp, &ret, MapToObjectOption{
UnionTypes: []*TaggedUnionType{
NewTaggedTypeUnion(types.NewTypeUnion[UnionType](
(*EleType1)(nil),
(*EleType2)(nil),
),
"Type",
"type",
),
},
})
union := types.NewTypeUnion[UnionType](
(*EleType1)(nil),
(*EleType2)(nil),
)
UseTypeUnionInternallyTagged(&union, "type")
err := MapToObject(mp, &ret)


So(err, ShouldBeNil) So(err, ShouldBeNil)


@@ -414,13 +411,15 @@ func Test_MapToObject(t *testing.T) {
type UnionType interface{} type UnionType interface{}


type EleType1 struct { type EleType1 struct {
Type string `json:"type" union:"1"`
Value1 string `json:"value1"`
Metadata `union:"1"`
Type string `json:"type"`
Value1 string `json:"value1"`
} }


type EleType2 struct { type EleType2 struct {
Type string `json:"type" union:"2"`
Value2 int `json:"value2"`
Metadata `union:"2"`
Type string `json:"type"`
Value2 int `json:"value2"`
} }


mp := map[string]any{ mp := map[string]any{
@@ -429,17 +428,12 @@ func Test_MapToObject(t *testing.T) {
} }


var ret UnionType var ret UnionType
err := MapToObject(mp, &ret, MapToObjectOption{
UnionTypes: []*TaggedUnionType{
NewTaggedTypeUnion(types.NewTypeUnion[UnionType](
(*EleType1)(nil),
(*EleType2)(nil),
),
"Type",
"type",
),
},
})
union := types.NewTypeUnion[UnionType](
(*EleType1)(nil),
(*EleType2)(nil),
)
UseTypeUnionInternallyTagged(&union, "type")
err := MapToObject(mp, &ret)


So(err, ShouldBeNil) So(err, ShouldBeNil)


@@ -464,7 +458,27 @@ func Test_MapToObject(t *testing.T) {
So(string(ret.Str), ShouldEqual, "1") So(string(ret.Str), ShouldEqual, "1")
}) })
} }
func Test_JSON(t *testing.T) {

type Base interface {
Noop()
}
type St1 struct {
Metadata `union:"St1"`
Type string
Val string
}

func (*St1) Noop() {}

type St2 struct {
Metadata `union:"St2"`
Type string
Val int
}

func (St2) Noop() {}

func Test_ObjectToJSON2(t *testing.T) {
Convey("NewType", t, func() { Convey("NewType", t, func() {
type Str string type Str string


@@ -484,4 +498,154 @@ func Test_JSON(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(string(ret.Str), ShouldEqual, "1") So(string(ret.Str), ShouldEqual, "1")
}) })

Convey("UnionType ExternallyTagged", t, func() {
type Base interface{}
type St1 struct {
Val string
}
type St2 struct {
Val int64
}
type Outter struct {
B []Base
}

union := types.NewTypeUnion[Base](St1{}, &St2{})
UseTypeUnionExternallyTagged(&union)

val := Outter{B: []Base{St1{Val: "asd"}, &St2{Val: 123}}}
data, err := ObjectToJSONEx(val)
So(err, ShouldBeNil)

ret, err := JSONToObjectEx[Outter](data)
So(err, ShouldBeNil)
So(ret, ShouldResemble, val)
})

Convey("UnionType InternallyTagged", t, func() {
type Base interface{}
type St1 struct {
Metadata `union:"St1"`
Type string
Val string
}
type St2 struct {
Metadata `union:"St2"`
Type string
Val int64
}
type Outter struct {
B []Base
}

union := types.NewTypeUnion[Base](St1{}, &St2{})
UseTypeUnionInternallyTagged(&union, "Type")

val := Outter{B: []Base{St1{Val: "asd", Type: "St1"}, &St2{Val: 123, Type: "St2"}}}
data, err := ObjectToJSONEx(val)
So(err, ShouldBeNil)

ret, err := JSONToObjectEx[Outter](data)
So(err, ShouldBeNil)
So(ret, ShouldResemble, val)
})

Convey("实参类型和目标类型本身就是UnionType ExternallyTagged", t, func() {
type Base interface{}
type St1 struct {
Val string
}
union := types.NewTypeUnion[Base](St1{})
UseTypeUnionExternallyTagged(&union)

var val Base = St1{Val: "asd"}
data, err := ObjectToJSONEx(val)
So(err, ShouldBeNil)

ret, err := JSONToObjectEx[Base](data)
So(err, ShouldBeNil)
So(ret, ShouldResemble, val)
})

Convey("实参类型和目标类型本身就是UnionType InternallyTagged", t, func() {
type Base interface{}
type St1 struct {
Metadata `union:"St1"`
Type string
Val string
}
union := types.NewTypeUnion[Base](St1{})
UseTypeUnionInternallyTagged(&union, "Type")

var val Base = St1{Val: "asd", Type: "St1"}
data, err := ObjectToJSONEx(val)
So(err, ShouldBeNil)

ret, err := JSONToObjectEx[Base](data)
So(err, ShouldBeNil)
So(ret, ShouldResemble, val)
})

Convey("UnionType带有函数 ExternallyTagged", t, func() {
union := types.NewTypeUnion[Base](&St1{}, St2{})
UseTypeUnionExternallyTagged(&union)

var val []Base = []Base{
&St1{Val: "asd", Type: "St1"},
St2{Val: 123, Type: "St2"},
}
data, err := ObjectToJSONEx(val)
So(err, ShouldBeNil)

ret, err := JSONToObjectEx[[]Base](data)
So(err, ShouldBeNil)
So(ret, ShouldResemble, val)
})

Convey("UnionType带有函数 InternallyTagged", t, func() {
union := types.NewTypeUnion[Base](&St1{}, St2{})
UseTypeUnionInternallyTagged(&union, "Type")

var val []Base = []Base{
&St1{Val: "asd", Type: "St1"},
St2{Val: 123, Type: "St2"},
}
data, err := ObjectToJSONEx(val)
So(err, ShouldBeNil)

ret, err := JSONToObjectEx[[]Base](data)
So(err, ShouldBeNil)
So(ret, ShouldResemble, val)
})

Convey("UnionType,但实际值为nil ExternallyTagged", t, func() {
union := types.NewTypeUnion[Base](&St1{}, St2{})
UseTypeUnionExternallyTagged(&union)

var val []Base = []Base{
nil,
}
data, err := ObjectToJSONEx(val)
So(err, ShouldBeNil)

ret, err := JSONToObjectEx[[]Base](data)
So(err, ShouldBeNil)
So(ret, ShouldResemble, val)
})

Convey("UnionType,但实际值为nil InternallyTagged", t, func() {
union := types.NewTypeUnion[Base](&St1{}, St2{})
UseTypeUnionInternallyTagged(&union, "Type")

var val []Base = []Base{
nil,
}
data, err := ObjectToJSONEx(val)
So(err, ShouldBeNil)

ret, err := JSONToObjectEx[[]Base](data)
So(err, ShouldBeNil)
So(ret, ShouldResemble, val)
})
} }

+ 2
- 0
utils/serder/types.go View File

@@ -6,6 +6,8 @@ import (
"time" "time"
) )


type Metadata struct{}

type TimestampSecond time.Time type TimestampSecond time.Time


func (t *TimestampSecond) MarshalJSON() ([]byte, error) { func (t *TimestampSecond) MarshalJSON() ([]byte, error) {


+ 336
- 0
utils/serder/union_handler.go View File

@@ -0,0 +1,336 @@
package serder

import (
"fmt"
"reflect"
"unsafe"

jsoniter "github.com/json-iterator/go"
"github.com/modern-go/reflect2"
"gitlink.org.cn/cloudream/common/pkgs/types"

myreflect "gitlink.org.cn/cloudream/common/utils/reflect"
)

type TypeUnionExternallyTagged struct {
Union *types.TypeUnion
TypeNameToType map[string]reflect.Type
}

// 遇到TypeUnion的基类(UnionType)的字段时,将其实际值的类型信息也编码到JSON中,反序列化时也会解析出类型信息,还原出真实的类型。
// Externally Tagged的格式是:{ "类型名": {...对象内容...} }
//
// 可以通过内嵌Metadata结构体,并在它身上增加"union"Tag来指定类型名称,如果没有指定,则默认使用系统类型名(包括包路径)。
func UseTypeUnionExternallyTagged(union *types.TypeUnion) *TypeUnionExternallyTagged {
eu := &TypeUnionExternallyTagged{
Union: union,
TypeNameToType: make(map[string]reflect.Type),
}

for _, eleType := range union.ElementTypes {
eu.Add(eleType)
}

unionHandler.externallyTagged[union.UnionType] = eu

return eu
}

func (u *TypeUnionExternallyTagged) Add(typ reflect.Type) error {
err := u.Union.Add(typ)
if err != nil {
return nil
}

u.TypeNameToType[makeDerefFullTypeName(typ)] = typ
return nil
}

type TypeUnionInternallyTagged struct {
Union *types.TypeUnion
TagField string
TagToType map[string]reflect.Type
}

// 遇到TypeUnion的基类(UnionType)的字段时,将其实际值的类型信息也编码到JSON中,反序列化时也会解析出类型信息,还原出真实的类型。
// Internally Tagged的格式是:{ "类型字段": "类型名", ...对象内容...},JSON中的类型字段名需要指定。
// 注:对象定义需要包含类型字段,而且在序列化之前需要手动赋值,目前不支持自动设置。
//
// 可以通过内嵌Metadata结构体,并在它身上增加"union"Tag来指定类型名称,如果没有指定,则默认使用系统类型名(包括包路径)。
func UseTypeUnionInternallyTagged(union *types.TypeUnion, tagField string) *TypeUnionInternallyTagged {
iu := &TypeUnionInternallyTagged{
Union: union,
TagField: tagField,
TagToType: make(map[string]reflect.Type),
}

for _, eleType := range union.ElementTypes {
iu.Add(eleType)
}

unionHandler.internallyTagged[union.UnionType] = iu
return iu
}

func (u *TypeUnionInternallyTagged) Add(typ reflect.Type) error {
err := u.Union.Add(typ)
if err != nil {
return nil
}

// 解引用直到得到结构体类型
structType := typ
for structType.Kind() == reflect.Pointer {
structType = structType.Elem()
}

// 要求内嵌Metadata结构体,那么结构体中的字段名就会是Metadata,
field, ok := structType.FieldByName(myreflect.TypeNameOf[Metadata]())
if !ok {
u.TagToType[makeDerefFullTypeName(structType)] = typ
return nil
}

// 为防同名,检查类型是不是也是Metadata
if field.Type != myreflect.TypeOf[Metadata]() {
u.TagToType[makeDerefFullTypeName(structType)] = typ
return nil
}

tag := field.Tag.Get("union")
if tag == "" {
u.TagToType[makeDerefFullTypeName(structType)] = typ
return nil
}

u.TagToType[tag] = typ
return nil
}

type UnionHandler struct {
internallyTagged map[reflect.Type]*TypeUnionInternallyTagged
externallyTagged map[reflect.Type]*TypeUnionExternallyTagged
}

func (h *UnionHandler) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {

}

func (h *UnionHandler) CreateMapKeyDecoder(typ reflect2.Type) jsoniter.ValDecoder {
return nil
}

func (h *UnionHandler) CreateMapKeyEncoder(typ reflect2.Type) jsoniter.ValEncoder {
return nil
}

func (h *UnionHandler) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder {
typ1 := typ.Type1()
if it, ok := h.internallyTagged[typ1]; ok {
return &InternallyTaggedDecoder{
union: it,
}
}

if et, ok := h.externallyTagged[typ1]; ok {
return &ExternallyTaggedDecoder{
union: et,
}
}

return nil
}

func (h *UnionHandler) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
typ1 := typ.Type1()
if it, ok := h.internallyTagged[typ1]; ok {
return &InternallyTaggedEncoder{
union: it,
}
}

if et, ok := h.externallyTagged[typ1]; ok {
return &ExternallyTaggedEncoder{
union: et,
}
}
return nil
}

func (h *UnionHandler) DecorateDecoder(typ reflect2.Type, decoder jsoniter.ValDecoder) jsoniter.ValDecoder {
return decoder
}

func (h *UnionHandler) DecorateEncoder(typ reflect2.Type, encoder jsoniter.ValEncoder) jsoniter.ValEncoder {
return encoder
}

// 以下Encoder/Decoder都是在传入类型/目标类型是TypeUnion的基类(UnionType)时使用
type InternallyTaggedEncoder struct {
union *TypeUnionInternallyTagged
}

func (e *InternallyTaggedEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return false
}

func (e *InternallyTaggedEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
var val any

if e.union.Union.UnionType.NumMethod() == 0 {
// 无方法的interface底层都是eface结构体,所以可以直接转*any
val = *(*any)(ptr)
} else {
// 有方法的interface底层都是iface结构体,可以将其转成eface,转换后不损失类型信息
val = reflect2.IFaceToEFace(ptr)
}

// 可以考虑检查一下Type字段有没有赋值,没有赋值则将其赋值为union Tag指定的值
stream.WriteVal(val)
}

type InternallyTaggedDecoder struct {
union *TypeUnionInternallyTagged
}

func (e *InternallyTaggedDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
nextTokenKind := iter.WhatIsNext()
if nextTokenKind == jsoniter.NilValue {
iter.Skip()
return
}

raw := iter.ReadAny()
if raw.LastError() != nil {
iter.ReportError("decode TaggedUnionType", "getting object raw:"+raw.LastError().Error())
return
}

tagField := raw.Get(e.union.TagField)
if tagField.LastError() != nil {
iter.ReportError("decode TaggedUnionType", "getting type tag field:"+tagField.LastError().Error())
return
}

typeTag := tagField.ToString()
if typeTag == "" {
iter.ReportError("decode TaggedUnionType", "type tag is empty")
return
}

typ, ok := e.union.TagToType[typeTag]
if !ok {
iter.ReportError("decode TaggedUnionType", fmt.Sprintf("unknow type tag %s in union %s", typeTag, e.union.Union.UnionType.Name()))
return
}

// 如果目标类型已经是个指针类型*T,那么在New的时候就需要使用T,
// 否则New出来的是会是**T,这将导致后续的反序列化出问题
if typ.Kind() == reflect.Pointer {
val := reflect.New(typ.Elem())
raw.ToVal(val.Interface())

retVal := reflect.NewAt(e.union.Union.UnionType, ptr)
retVal.Elem().Set(val)

} else {
val := reflect.New(typ)
raw.ToVal(val.Interface())

retVal := reflect.NewAt(e.union.Union.UnionType, ptr)
retVal.Elem().Set(val.Elem())
}
}

type ExternallyTaggedEncoder struct {
union *TypeUnionExternallyTagged
}

func (e *ExternallyTaggedEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return false
}

func (e *ExternallyTaggedEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
var val any

if e.union.Union.UnionType.NumMethod() == 0 {
// 无方法的interface底层都是eface结构体,所以可以直接转*any
val = *(*any)(ptr)
} else {
// 有方法的interface底层都是iface结构体,可以将其转成eface,转换后不损失类型信息
val = reflect2.IFaceToEFace(ptr)
}

if val == nil {
stream.WriteNil()
return
}

stream.WriteObjectStart()
valType := myreflect.TypeOfValue(val)
if !e.union.Union.Include(valType) {
stream.Error = fmt.Errorf("type %v is not in union %v", valType, e.union.Union.UnionType)
return
}
stream.WriteObjectField(makeDerefFullTypeName(valType))
stream.WriteVal(val)
stream.WriteObjectEnd()
}

type ExternallyTaggedDecoder struct {
union *TypeUnionExternallyTagged
}

func (e *ExternallyTaggedDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
nextTkType := iter.WhatIsNext()

if nextTkType == jsoniter.NilValue {
iter.Skip()
return
}

if nextTkType != jsoniter.ObjectValue {
iter.ReportError("decode UnionType", fmt.Sprintf("unknow next token type %v", nextTkType))
return
}

typeStr := iter.ReadObject()
if typeStr == "" {
iter.ReportError("decode UnionType", "type string is empty")
}

typ, ok := e.union.TypeNameToType[typeStr]
if !ok {
iter.ReportError("decode UnionType", fmt.Sprintf("unknow type string %s in union %v", typeStr, e.union.Union.UnionType))
return
}

// 如果目标类型已经是个指针类型*T,那么在New的时候就需要使用T,
// 否则New出来的是会是**T,这将导致后续的反序列化出问题
if typ.Kind() == reflect.Pointer {
val := reflect.New(typ.Elem())
iter.ReadVal(val.Interface())

retVal := reflect.NewAt(e.union.Union.UnionType, ptr)
retVal.Elem().Set(val)

} else {
val := reflect.New(typ)
iter.ReadVal(val.Interface())

retVal := reflect.NewAt(e.union.Union.UnionType, ptr)
retVal.Elem().Set(val.Elem())
}

if iter.ReadObject() != "" {
iter.ReportError("decode UnionType", "there should be only one fields in the json object")
}
}

func makeDerefFullTypeName(typ reflect.Type) string {
realType := typ
for realType.Kind() == reflect.Pointer {
realType = realType.Elem()
}
return fmt.Sprintf("%s.%s", realType.PkgPath(), realType.Name())
}

Loading…
Cancel
Save