-
Notifications
You must be signed in to change notification settings - Fork 0
/
marshal.go
83 lines (75 loc) · 2.25 KB
/
marshal.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package urlqstr
import (
"fmt"
"reflect"
"net/url"
)
func (uqstr *UQueryString) Marshal(DataIntf interface{}) (string,error){
uqstr.init()
uqstr.DataIntf = DataIntf
uqstr.extractDataFromStruct(DataIntf)
}
// extractDataFromStruct is an internal function which used to recursively extract
// data from any structure and parse the tag value. Since HTTP QueryString is only a single level,
// we use coroutine to speed up.
func (uqstr *UQueryString) extractDataFromStruct(DataIntf interface{}){
// make channel as signal to avoid use sync.WaitGroup
// https://golang.org/ref/spec#Receive_operator
sig1 := make(chan interface{})
sig2 := make(chan interface{})
ifaceTyps := reflect.TypeOf(DataIntf)
ifaceVals := reflect.ValueOf(DataIntf)
// dereference Ptr to avoid have an address introduced
if ifaceTyps.Kind() == reflect.Ptr {
ifaceTyps = ifaceTyps.Elem()
ifaceVals = ifaceVals.Elem()
}
// get name of all structs fields and its corresponding tag names
// gkd gkd
go func() {
defer close(sig1)
for i := 0; i < ifaceTyps.NumField(); i++ {
curfd := ifaceTyps.Field(i)
dataA, exists := curfd.Tag.Lookup(TAGKEY)
if exists {
var afterProcDataA = dataA
// invalid symbols and delimiters should not be directly put into the querystring
// url encode first
if !isValidTag(dataA) {
afterProcDataA = url.QueryEscape(dataA)
}
uqstr.f2Tbn.mu.Lock()
uqstr.f2Tbn.val[curfd.Name] = afterProcDataA
uqstr.f2Tbn.mu.Unlock()
}
}
}()
// recursive resolve the struct value and corresponding fields
// gkd gkd
go func() {
defer close(sig2)
typeOfDts := ifaceVals.Type()
for i:= 0; i < ifaceVals.NumField(); i++ {
curfd := ifaceVals.Field(i)
// dereference if any ptr exists
if curfd.Kind() == reflect.Ptr {
curfd = curfd.Elem()
}
switch curfd.Kind(){
case reflect.Struct:
uqstr.extractDataFromStruct(curfd.Interface())
default:
fdname := typeOfDts.Field(i).Name
// transform interface to string, DO NOT USE `.String()` directly!
// which will result in "<int value>" not "2"
fdval := fmt.Sprintf("%v", curfd.Interface())
uqstr.tn2Tv.mu.Lock()
uqstr.tn2Tv.val[fdname] = fdval
uqstr.tn2Tv.mu.Unlock()
}
}
}()
// wait until all coroutine finished to return
<-sig1
<-sig2
}