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