1package xmlrpc 2 3import ( 4 "bytes" 5 "encoding/xml" 6 "fmt" 7 "reflect" 8 "strconv" 9 "time" 10) 11 12type encodeFunc func(reflect.Value) ([]byte, error) 13 14func marshal(v interface{}) ([]byte, error) { 15 if v == nil { 16 return []byte{}, nil 17 } 18 19 val := reflect.ValueOf(v) 20 return encodeValue(val) 21} 22 23func encodeValue(val reflect.Value) ([]byte, error) { 24 var b []byte 25 var err error 26 27 if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface { 28 if val.IsNil() { 29 return []byte("<value/>"), nil 30 } 31 32 val = val.Elem() 33 } 34 35 switch val.Kind() { 36 case reflect.Struct: 37 switch val.Interface().(type) { 38 case time.Time: 39 t := val.Interface().(time.Time) 40 b = []byte(fmt.Sprintf("<dateTime.iso8601>%s</dateTime.iso8601>", t.Format(iso8601))) 41 default: 42 b, err = encodeStruct(val) 43 } 44 case reflect.Map: 45 b, err = encodeMap(val) 46 case reflect.Slice: 47 b, err = encodeSlice(val) 48 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 49 b = []byte(fmt.Sprintf("<int>%s</int>", strconv.FormatInt(val.Int(), 10))) 50 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 51 b = []byte(fmt.Sprintf("<i4>%s</i4>", strconv.FormatUint(val.Uint(), 10))) 52 case reflect.Float32, reflect.Float64: 53 b = []byte(fmt.Sprintf("<double>%s</double>", 54 strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits()))) 55 case reflect.Bool: 56 if val.Bool() { 57 b = []byte("<boolean>1</boolean>") 58 } else { 59 b = []byte("<boolean>0</boolean>") 60 } 61 case reflect.String: 62 var buf bytes.Buffer 63 64 xml.Escape(&buf, []byte(val.String())) 65 66 if _, ok := val.Interface().(Base64); ok { 67 b = []byte(fmt.Sprintf("<base64>%s</base64>", buf.String())) 68 } else { 69 b = []byte(fmt.Sprintf("<string>%s</string>", buf.String())) 70 } 71 default: 72 return nil, fmt.Errorf("xmlrpc encode error: unsupported type") 73 } 74 75 if err != nil { 76 return nil, err 77 } 78 79 return []byte(fmt.Sprintf("<value>%s</value>", string(b))), nil 80} 81 82func encodeStruct(val reflect.Value) ([]byte, error) { 83 var b bytes.Buffer 84 85 b.WriteString("<struct>") 86 87 t := val.Type() 88 for i := 0; i < t.NumField(); i++ { 89 b.WriteString("<member>") 90 f := t.Field(i) 91 92 name := f.Tag.Get("xmlrpc") 93 if name == "" { 94 name = f.Name 95 } 96 b.WriteString(fmt.Sprintf("<name>%s</name>", name)) 97 98 p, err := encodeValue(val.FieldByName(f.Name)) 99 if err != nil { 100 return nil, err 101 } 102 b.Write(p) 103 104 b.WriteString("</member>") 105 } 106 107 b.WriteString("</struct>") 108 109 return b.Bytes(), nil 110} 111 112func encodeMap(val reflect.Value) ([]byte, error) { 113 var t = val.Type() 114 115 if t.Key().Kind() != reflect.String { 116 return nil, fmt.Errorf("xmlrpc encode error: only maps with string keys are supported") 117 } 118 119 var b bytes.Buffer 120 121 b.WriteString("<struct>") 122 123 keys := val.MapKeys() 124 125 for i := 0; i < val.Len(); i++ { 126 key := keys[i] 127 kval := val.MapIndex(key) 128 129 b.WriteString("<member>") 130 b.WriteString(fmt.Sprintf("<name>%s</name>", key.String())) 131 132 p, err := encodeValue(kval) 133 134 if err != nil { 135 return nil, err 136 } 137 138 b.Write(p) 139 b.WriteString("</member>") 140 } 141 142 b.WriteString("</struct>") 143 144 return b.Bytes(), nil 145} 146 147func encodeSlice(val reflect.Value) ([]byte, error) { 148 var b bytes.Buffer 149 150 b.WriteString("<array><data>") 151 152 for i := 0; i < val.Len(); i++ { 153 p, err := encodeValue(val.Index(i)) 154 if err != nil { 155 return nil, err 156 } 157 158 b.Write(p) 159 } 160 161 b.WriteString("</data></array>") 162 163 return b.Bytes(), nil 164} 165