package xmlrpc import ( "bytes" "encoding/xml" "fmt" "reflect" "strconv" "time" ) type encodeFunc func(reflect.Value) ([]byte, error) func marshal(v interface{}) ([]byte, error) { if v == nil { return []byte{}, nil } val := reflect.ValueOf(v) return encodeValue(val) } func encodeValue(val reflect.Value) ([]byte, error) { var b []byte var err error if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface { if val.IsNil() { return []byte(""), nil } val = val.Elem() } switch val.Kind() { case reflect.Struct: switch val.Interface().(type) { case time.Time: t := val.Interface().(time.Time) b = []byte(fmt.Sprintf("%s", t.Format(iso8601))) default: b, err = encodeStruct(val) } case reflect.Map: b, err = encodeMap(val) case reflect.Slice: b, err = encodeSlice(val) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: b = []byte(fmt.Sprintf("%s", strconv.FormatInt(val.Int(), 10))) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: b = []byte(fmt.Sprintf("%s", strconv.FormatUint(val.Uint(), 10))) case reflect.Float32, reflect.Float64: b = []byte(fmt.Sprintf("%s", strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits()))) case reflect.Bool: if val.Bool() { b = []byte("1") } else { b = []byte("0") } case reflect.String: var buf bytes.Buffer xml.Escape(&buf, []byte(val.String())) if _, ok := val.Interface().(Base64); ok { b = []byte(fmt.Sprintf("%s", buf.String())) } else { b = []byte(fmt.Sprintf("%s", buf.String())) } default: return nil, fmt.Errorf("xmlrpc encode error: unsupported type") } if err != nil { return nil, err } return []byte(fmt.Sprintf("%s", string(b))), nil } func encodeStruct(val reflect.Value) ([]byte, error) { var b bytes.Buffer b.WriteString("") t := val.Type() for i := 0; i < t.NumField(); i++ { b.WriteString("") f := t.Field(i) name := f.Tag.Get("xmlrpc") if name == "" { name = f.Name } b.WriteString(fmt.Sprintf("%s", name)) p, err := encodeValue(val.FieldByName(f.Name)) if err != nil { return nil, err } b.Write(p) b.WriteString("") } b.WriteString("") return b.Bytes(), nil } func encodeMap(val reflect.Value) ([]byte, error) { var t = val.Type() if t.Key().Kind() != reflect.String { return nil, fmt.Errorf("xmlrpc encode error: only maps with string keys are supported") } var b bytes.Buffer b.WriteString("") keys := val.MapKeys() for i := 0; i < val.Len(); i++ { key := keys[i] kval := val.MapIndex(key) b.WriteString("") b.WriteString(fmt.Sprintf("%s", key.String())) p, err := encodeValue(kval) if err != nil { return nil, err } b.Write(p) b.WriteString("") } b.WriteString("") return b.Bytes(), nil } func encodeSlice(val reflect.Value) ([]byte, error) { var b bytes.Buffer b.WriteString("") for i := 0; i < val.Len(); i++ { p, err := encodeValue(val.Index(i)) if err != nil { return nil, err } b.Write(p) } b.WriteString("") return b.Bytes(), nil }