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