github.com/aavshr/aws-sdk-go@v1.41.3/private/protocol/json/jsonutil/build.go (about)

     1  // Package jsonutil provides JSON serialization of AWS requests and responses.
     2  package jsonutil
     3  
     4  import (
     5  	"bytes"
     6  	"encoding/base64"
     7  	"encoding/json"
     8  	"fmt"
     9  	"math"
    10  	"reflect"
    11  	"sort"
    12  	"strconv"
    13  	"time"
    14  
    15  	"github.com/aavshr/aws-sdk-go/aws"
    16  	"github.com/aavshr/aws-sdk-go/private/protocol"
    17  )
    18  
    19  var timeType = reflect.ValueOf(time.Time{}).Type()
    20  var byteSliceType = reflect.ValueOf([]byte{}).Type()
    21  
    22  // BuildJSON builds a JSON string for a given object v.
    23  func BuildJSON(v interface{}) ([]byte, error) {
    24  	var buf bytes.Buffer
    25  
    26  	err := buildAny(reflect.ValueOf(v), &buf, "")
    27  	return buf.Bytes(), err
    28  }
    29  
    30  func buildAny(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
    31  	origVal := value
    32  	value = reflect.Indirect(value)
    33  	if !value.IsValid() {
    34  		return nil
    35  	}
    36  
    37  	vtype := value.Type()
    38  
    39  	t := tag.Get("type")
    40  	if t == "" {
    41  		switch vtype.Kind() {
    42  		case reflect.Struct:
    43  			// also it can't be a time object
    44  			if value.Type() != timeType {
    45  				t = "structure"
    46  			}
    47  		case reflect.Slice:
    48  			// also it can't be a byte slice
    49  			if _, ok := value.Interface().([]byte); !ok {
    50  				t = "list"
    51  			}
    52  		case reflect.Map:
    53  			// cannot be a JSONValue map
    54  			if _, ok := value.Interface().(aws.JSONValue); !ok {
    55  				t = "map"
    56  			}
    57  		}
    58  	}
    59  
    60  	switch t {
    61  	case "structure":
    62  		if field, ok := vtype.FieldByName("_"); ok {
    63  			tag = field.Tag
    64  		}
    65  		return buildStruct(value, buf, tag)
    66  	case "list":
    67  		return buildList(value, buf, tag)
    68  	case "map":
    69  		return buildMap(value, buf, tag)
    70  	default:
    71  		return buildScalar(origVal, buf, tag)
    72  	}
    73  }
    74  
    75  func buildStruct(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
    76  	if !value.IsValid() {
    77  		return nil
    78  	}
    79  
    80  	// unwrap payloads
    81  	if payload := tag.Get("payload"); payload != "" {
    82  		field, _ := value.Type().FieldByName(payload)
    83  		tag = field.Tag
    84  		value = elemOf(value.FieldByName(payload))
    85  		if !value.IsValid() && tag.Get("type") != "structure" {
    86  			return nil
    87  		}
    88  	}
    89  
    90  	buf.WriteByte('{')
    91  	defer buf.WriteString("}")
    92  
    93  	if !value.IsValid() {
    94  		return nil
    95  	}
    96  
    97  	t := value.Type()
    98  	first := true
    99  	for i := 0; i < t.NumField(); i++ {
   100  		member := value.Field(i)
   101  
   102  		// This allocates the most memory.
   103  		// Additionally, we cannot skip nil fields due to
   104  		// idempotency auto filling.
   105  		field := t.Field(i)
   106  
   107  		if field.PkgPath != "" {
   108  			continue // ignore unexported fields
   109  		}
   110  		if field.Tag.Get("json") == "-" {
   111  			continue
   112  		}
   113  		if field.Tag.Get("location") != "" {
   114  			continue // ignore non-body elements
   115  		}
   116  		if field.Tag.Get("ignore") != "" {
   117  			continue
   118  		}
   119  
   120  		if protocol.CanSetIdempotencyToken(member, field) {
   121  			token := protocol.GetIdempotencyToken()
   122  			member = reflect.ValueOf(&token)
   123  		}
   124  
   125  		if (member.Kind() == reflect.Ptr || member.Kind() == reflect.Slice || member.Kind() == reflect.Map) && member.IsNil() {
   126  			continue // ignore unset fields
   127  		}
   128  
   129  		if first {
   130  			first = false
   131  		} else {
   132  			buf.WriteByte(',')
   133  		}
   134  
   135  		// figure out what this field is called
   136  		name := field.Name
   137  		if locName := field.Tag.Get("locationName"); locName != "" {
   138  			name = locName
   139  		}
   140  
   141  		writeString(name, buf)
   142  		buf.WriteString(`:`)
   143  
   144  		err := buildAny(member, buf, field.Tag)
   145  		if err != nil {
   146  			return err
   147  		}
   148  
   149  	}
   150  
   151  	return nil
   152  }
   153  
   154  func buildList(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
   155  	buf.WriteString("[")
   156  
   157  	for i := 0; i < value.Len(); i++ {
   158  		buildAny(value.Index(i), buf, "")
   159  
   160  		if i < value.Len()-1 {
   161  			buf.WriteString(",")
   162  		}
   163  	}
   164  
   165  	buf.WriteString("]")
   166  
   167  	return nil
   168  }
   169  
   170  type sortedValues []reflect.Value
   171  
   172  func (sv sortedValues) Len() int           { return len(sv) }
   173  func (sv sortedValues) Swap(i, j int)      { sv[i], sv[j] = sv[j], sv[i] }
   174  func (sv sortedValues) Less(i, j int) bool { return sv[i].String() < sv[j].String() }
   175  
   176  func buildMap(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
   177  	buf.WriteString("{")
   178  
   179  	sv := sortedValues(value.MapKeys())
   180  	sort.Sort(sv)
   181  
   182  	for i, k := range sv {
   183  		if i > 0 {
   184  			buf.WriteByte(',')
   185  		}
   186  
   187  		writeString(k.String(), buf)
   188  		buf.WriteString(`:`)
   189  
   190  		buildAny(value.MapIndex(k), buf, "")
   191  	}
   192  
   193  	buf.WriteString("}")
   194  
   195  	return nil
   196  }
   197  
   198  func buildScalar(v reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
   199  	// prevents allocation on the heap.
   200  	scratch := [64]byte{}
   201  	switch value := reflect.Indirect(v); value.Kind() {
   202  	case reflect.String:
   203  		writeString(value.String(), buf)
   204  	case reflect.Bool:
   205  		if value.Bool() {
   206  			buf.WriteString("true")
   207  		} else {
   208  			buf.WriteString("false")
   209  		}
   210  	case reflect.Int64:
   211  		buf.Write(strconv.AppendInt(scratch[:0], value.Int(), 10))
   212  	case reflect.Float64:
   213  		f := value.Float()
   214  		if math.IsInf(f, 0) || math.IsNaN(f) {
   215  			return &json.UnsupportedValueError{Value: v, Str: strconv.FormatFloat(f, 'f', -1, 64)}
   216  		}
   217  		buf.Write(strconv.AppendFloat(scratch[:0], f, 'f', -1, 64))
   218  	default:
   219  		switch converted := value.Interface().(type) {
   220  		case time.Time:
   221  			format := tag.Get("timestampFormat")
   222  			if len(format) == 0 {
   223  				format = protocol.UnixTimeFormatName
   224  			}
   225  
   226  			ts := protocol.FormatTime(format, converted)
   227  			if format != protocol.UnixTimeFormatName {
   228  				ts = `"` + ts + `"`
   229  			}
   230  
   231  			buf.WriteString(ts)
   232  		case []byte:
   233  			if !value.IsNil() {
   234  				buf.WriteByte('"')
   235  				if len(converted) < 1024 {
   236  					// for small buffers, using Encode directly is much faster.
   237  					dst := make([]byte, base64.StdEncoding.EncodedLen(len(converted)))
   238  					base64.StdEncoding.Encode(dst, converted)
   239  					buf.Write(dst)
   240  				} else {
   241  					// for large buffers, avoid unnecessary extra temporary
   242  					// buffer space.
   243  					enc := base64.NewEncoder(base64.StdEncoding, buf)
   244  					enc.Write(converted)
   245  					enc.Close()
   246  				}
   247  				buf.WriteByte('"')
   248  			}
   249  		case aws.JSONValue:
   250  			str, err := protocol.EncodeJSONValue(converted, protocol.QuotedEscape)
   251  			if err != nil {
   252  				return fmt.Errorf("unable to encode JSONValue, %v", err)
   253  			}
   254  			buf.WriteString(str)
   255  		default:
   256  			return fmt.Errorf("unsupported JSON value %v (%s)", value.Interface(), value.Type())
   257  		}
   258  	}
   259  	return nil
   260  }
   261  
   262  var hex = "0123456789abcdef"
   263  
   264  func writeString(s string, buf *bytes.Buffer) {
   265  	buf.WriteByte('"')
   266  	for i := 0; i < len(s); i++ {
   267  		if s[i] == '"' {
   268  			buf.WriteString(`\"`)
   269  		} else if s[i] == '\\' {
   270  			buf.WriteString(`\\`)
   271  		} else if s[i] == '\b' {
   272  			buf.WriteString(`\b`)
   273  		} else if s[i] == '\f' {
   274  			buf.WriteString(`\f`)
   275  		} else if s[i] == '\r' {
   276  			buf.WriteString(`\r`)
   277  		} else if s[i] == '\t' {
   278  			buf.WriteString(`\t`)
   279  		} else if s[i] == '\n' {
   280  			buf.WriteString(`\n`)
   281  		} else if s[i] < 32 {
   282  			buf.WriteString("\\u00")
   283  			buf.WriteByte(hex[s[i]>>4])
   284  			buf.WriteByte(hex[s[i]&0xF])
   285  		} else {
   286  			buf.WriteByte(s[i])
   287  		}
   288  	}
   289  	buf.WriteByte('"')
   290  }
   291  
   292  // Returns the reflection element of a value, if it is a pointer.
   293  func elemOf(value reflect.Value) reflect.Value {
   294  	for value.Kind() == reflect.Ptr {
   295  		value = value.Elem()
   296  	}
   297  	return value
   298  }