encoder.go raw
1 package xmlrpc
2
3 import (
4 "bytes"
5 "encoding/xml"
6 "fmt"
7 "reflect"
8 "sort"
9 "strconv"
10 "strings"
11 "time"
12 )
13
14 // Base64 represents value in base64 encoding
15 type Base64 string
16
17 type encodeFunc func(reflect.Value) ([]byte, error)
18
19 func marshal(v interface{}) ([]byte, error) {
20 if v == nil {
21 return []byte{}, nil
22 }
23
24 val := reflect.ValueOf(v)
25 return encodeValue(val)
26 }
27
28 func encodeValue(val reflect.Value) ([]byte, error) {
29 var b []byte
30 var err error
31
32 if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface {
33 if val.IsNil() {
34 return []byte("<value/>"), nil
35 }
36
37 val = val.Elem()
38 }
39
40 switch val.Kind() {
41 case reflect.Struct:
42 switch val.Interface().(type) {
43 case time.Time:
44 t := val.Interface().(time.Time)
45 b = []byte(fmt.Sprintf("<dateTime.iso8601>%s</dateTime.iso8601>", t.Format(iso8601)))
46 default:
47 b, err = encodeStruct(val)
48 }
49 case reflect.Map:
50 b, err = encodeMap(val)
51 case reflect.Slice:
52 b, err = encodeSlice(val)
53 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
54 b = []byte(fmt.Sprintf("<int>%s</int>", strconv.FormatInt(val.Int(), 10)))
55 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
56 b = []byte(fmt.Sprintf("<i4>%s</i4>", strconv.FormatUint(val.Uint(), 10)))
57 case reflect.Float32, reflect.Float64:
58 b = []byte(fmt.Sprintf("<double>%s</double>",
59 strconv.FormatFloat(val.Float(), 'f', -1, val.Type().Bits())))
60 case reflect.Bool:
61 if val.Bool() {
62 b = []byte("<boolean>1</boolean>")
63 } else {
64 b = []byte("<boolean>0</boolean>")
65 }
66 case reflect.String:
67 var buf bytes.Buffer
68
69 xml.Escape(&buf, []byte(val.String()))
70
71 if _, ok := val.Interface().(Base64); ok {
72 b = []byte(fmt.Sprintf("<base64>%s</base64>", buf.String()))
73 } else {
74 b = []byte(fmt.Sprintf("<string>%s</string>", buf.String()))
75 }
76 default:
77 return nil, fmt.Errorf("xmlrpc encode error: unsupported type")
78 }
79
80 if err != nil {
81 return nil, err
82 }
83
84 return []byte(fmt.Sprintf("<value>%s</value>", string(b))), nil
85 }
86
87 func encodeStruct(value reflect.Value) ([]byte, error) {
88 var b bytes.Buffer
89
90 b.WriteString("<struct>")
91
92 vals := []reflect.Value{value}
93 for j := 0; j < len(vals); j++ {
94 val := vals[j]
95 t := val.Type()
96 for i := 0; i < t.NumField(); i++ {
97 f := t.Field(i)
98 tag := f.Tag.Get("xmlrpc")
99 name := f.Name
100 fieldVal := val.FieldByName(f.Name)
101 fieldValKind := fieldVal.Kind()
102
103 // Omit unexported fields
104 if !fieldVal.CanInterface() {
105 continue
106 }
107
108 // Omit fields who are structs that contain no fields themselves
109 if fieldValKind == reflect.Struct && fieldVal.NumField() == 0 {
110 continue
111 }
112
113 // Omit empty slices
114 if fieldValKind == reflect.Slice && fieldVal.Len() == 0 {
115 continue
116 }
117
118 // Omit empty fields (defined as nil pointers)
119 if tag != "" {
120 parts := strings.Split(tag, ",")
121 name = parts[0]
122 if len(parts) > 1 && parts[1] == "omitempty" {
123 if fieldValKind == reflect.Ptr && fieldVal.IsNil() {
124 continue
125 }
126 }
127 }
128
129 // Drill down into anonymous/embedded structs and do not expose the
130 // containing embedded struct in request.
131 // This will effectively pull up fields in embedded structs to look
132 // as part of the original struct in the request.
133 if f.Anonymous {
134 vals = append(vals, fieldVal)
135 continue
136 }
137
138 b.WriteString("<member>")
139 b.WriteString(fmt.Sprintf("<name>%s</name>", name))
140
141 p, err := encodeValue(fieldVal)
142 if err != nil {
143 return nil, err
144 }
145 b.Write(p)
146
147 b.WriteString("</member>")
148 }
149 }
150
151 b.WriteString("</struct>")
152
153 return b.Bytes(), nil
154 }
155
156 var sortMapKeys bool
157
158 func encodeMap(val reflect.Value) ([]byte, error) {
159 var t = val.Type()
160
161 if t.Key().Kind() != reflect.String {
162 return nil, fmt.Errorf("xmlrpc encode error: only maps with string keys are supported")
163 }
164
165 var b bytes.Buffer
166
167 b.WriteString("<struct>")
168
169 keys := val.MapKeys()
170
171 if sortMapKeys {
172 sort.Slice(keys, func(i, j int) bool { return keys[i].String() < keys[j].String() })
173 }
174
175 for i := 0; i < val.Len(); i++ {
176 key := keys[i]
177 kval := val.MapIndex(key)
178
179 b.WriteString("<member>")
180 b.WriteString(fmt.Sprintf("<name>%s</name>", key.String()))
181
182 p, err := encodeValue(kval)
183
184 if err != nil {
185 return nil, err
186 }
187
188 b.Write(p)
189 b.WriteString("</member>")
190 }
191
192 b.WriteString("</struct>")
193
194 return b.Bytes(), nil
195 }
196
197 func encodeSlice(val reflect.Value) ([]byte, error) {
198 var b bytes.Buffer
199
200 b.WriteString("<array><data>")
201
202 for i := 0; i < val.Len(); i++ {
203 p, err := encodeValue(val.Index(i))
204 if err != nil {
205 return nil, err
206 }
207
208 b.Write(p)
209 }
210
211 b.WriteString("</data></array>")
212
213 return b.Bytes(), nil
214 }
215