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(structVal reflect.Value) ([]byte, error) {
88 var b bytes.Buffer
89
90 b.WriteString("<struct>")
91
92 structType := structVal.Type()
93 for i := 0; i < structType.NumField(); i++ {
94 fieldVal := structVal.Field(i)
95 fieldType := structType.Field(i)
96
97 name := fieldType.Tag.Get("xmlrpc")
98 // skip ignored fields.
99 if name == "-" {
100 continue
101 }
102 // if the tag has the omitempty property, skip it
103 if strings.HasSuffix(name, ",omitempty") && fieldVal.IsZero() {
104 continue
105 }
106 name = strings.TrimSuffix(name, ",omitempty")
107 if name == "" {
108 name = fieldType.Name
109 }
110
111 p, err := encodeValue(fieldVal)
112 if err != nil {
113 return nil, err
114 }
115
116 b.WriteString("<member>")
117 b.WriteString(fmt.Sprintf("<name>%s</name>", name))
118 b.Write(p)
119 b.WriteString("</member>")
120 }
121
122 b.WriteString("</struct>")
123
124 return b.Bytes(), nil
125 }
126
127 var sortMapKeys bool
128
129 func encodeMap(val reflect.Value) ([]byte, error) {
130 var t = val.Type()
131
132 if t.Key().Kind() != reflect.String {
133 return nil, fmt.Errorf("xmlrpc encode error: only maps with string keys are supported")
134 }
135
136 var b bytes.Buffer
137
138 b.WriteString("<struct>")
139
140 keys := val.MapKeys()
141
142 if sortMapKeys {
143 sort.Slice(keys, func(i, j int) bool { return keys[i].String() < keys[j].String() })
144 }
145
146 for i := 0; i < val.Len(); i++ {
147 key := keys[i]
148 kval := val.MapIndex(key)
149
150 b.WriteString("<member>")
151 b.WriteString(fmt.Sprintf("<name>%s</name>", key.String()))
152
153 p, err := encodeValue(kval)
154
155 if err != nil {
156 return nil, err
157 }
158
159 b.Write(p)
160 b.WriteString("</member>")
161 }
162
163 b.WriteString("</struct>")
164
165 return b.Bytes(), nil
166 }
167
168 func encodeSlice(val reflect.Value) ([]byte, error) {
169 var b bytes.Buffer
170
171 b.WriteString("<array><data>")
172
173 for i := 0; i < val.Len(); i++ {
174 p, err := encodeValue(val.Index(i))
175 if err != nil {
176 return nil, err
177 }
178
179 b.Write(p)
180 }
181
182 b.WriteString("</data></array>")
183
184 return b.Bytes(), nil
185 }
186