github.com/smugmug/godynamo@v0.0.0-20151122084750-7913028f6623/types/attributevalue/attributevalue.go (about)

     1  // Support for AttributeValue type. See
     2  // http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_AttributeValue.html
     3  package attributevalue
     4  
     5  import (
     6  	"encoding/base64"
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"github.com/smugmug/godynamo/types/cast"
    11  	"strconv"
    12  )
    13  
    14  // SetList represents the SS,BS and NS types which are ostensibly sets but encoded as
    15  // json lists. Duplicates are allowed but removed when marshaling or unmarshaling.
    16  type SetList []string
    17  
    18  // MarshalJSON will remove duplicates
    19  func (s SetList) MarshalJSON() ([]byte, error) {
    20  	m := make(map[string]bool)
    21  	for _, v := range s {
    22  		m[v] = true
    23  	}
    24  	t := make([]string, len(m))
    25  	i := 0
    26  	for k := range m {
    27  		t[i] = k
    28  		i++
    29  	}
    30  	return json.Marshal(t)
    31  }
    32  
    33  // UnmarshalJSON will remove duplicates
    34  func (s *SetList) UnmarshalJSON(data []byte) error {
    35  	if s == nil {
    36  		return errors.New("AttributeValue.UnmarshalJSON: pointer receiver for unmarshal nil")
    37  	}
    38  	t := make([]string, 0)
    39  	um_err := json.Unmarshal(data, &t)
    40  	if um_err != nil {
    41  		return um_err
    42  	}
    43  	m := make(map[string]bool)
    44  	for _, v := range t {
    45  		m[v] = true
    46  	}
    47  	for k := range m {
    48  		*s = append(*s, k)
    49  	}
    50  	return nil
    51  }
    52  
    53  type AttributeValue struct {
    54  	N string `json:",omitempty"`
    55  	S string `json:",omitempty"`
    56  	B string `json:",omitempty"`
    57  
    58  	// These are pointers so we can have a vacuous type (nil), otherwise, we don't know
    59  	// if false was a set value or the default. To set these
    60  	BOOL *bool `json:",omitempty"`
    61  	NULL *bool `json:",omitempty"`
    62  
    63  	L []*AttributeValue          `json:",omitempty"`
    64  	M map[string]*AttributeValue `json:",omitempty"`
    65  
    66  	SS SetList `json:",omitempty"`
    67  	NS SetList `json:",omitempty"`
    68  	BS SetList `json:",omitempty"`
    69  }
    70  
    71  // Empty determines if an AttributeValue is vacuous. Explicitly do not bother
    72  // testing the boolean fields.
    73  func (a AttributeValue) Empty() bool {
    74  	return a.N == "" &&
    75  		a.S == "" &&
    76  		a.B == "" &&
    77  		len(a.M) == 0 &&
    78  		len(a.L) == 0 &&
    79  		len(a.SS) == 0 &&
    80  		len(a.NS) == 0 &&
    81  		len(a.BS) == 0 &&
    82  		a.BOOL == nil &&
    83  		a.NULL == nil
    84  }
    85  
    86  type attributevalue AttributeValue
    87  
    88  // MarshalJSON will emit null if the AttributeValue is Empty
    89  func (a AttributeValue) MarshalJSON() ([]byte, error) {
    90  	if a.Empty() {
    91  		return json.Marshal(nil)
    92  	} else {
    93  		return json.Marshal(attributevalue(a))
    94  	}
    95  }
    96  
    97  // Valid determines if more than one field has been set (in which case it is invalid).
    98  func (a *AttributeValue) Valid() bool {
    99  	if a == nil {
   100  		return false
   101  	}
   102  	c := 0
   103  	if a.S != "" {
   104  		c++
   105  		if c > 1 {
   106  			return false
   107  		}
   108  	}
   109  	if a.N != "" {
   110  		c++
   111  		if c > 1 {
   112  			return false
   113  		}
   114  	}
   115  	if a.B != "" {
   116  		c++
   117  		if c > 1 {
   118  			return false
   119  		}
   120  	}
   121  	if len(a.M) != 0 {
   122  		c++
   123  		if c > 1 {
   124  			return false
   125  		}
   126  	}
   127  	if len(a.L) != 0 {
   128  		c++
   129  		if c > 1 {
   130  			return false
   131  		}
   132  	}
   133  	if len(a.SS) != 0 {
   134  		c++
   135  		if c > 1 {
   136  			return false
   137  		}
   138  	}
   139  	if len(a.NS) != 0 {
   140  		c++
   141  		if c > 1 {
   142  			return false
   143  		}
   144  	}
   145  	if len(a.BS) != 0 {
   146  		c++
   147  		if c > 1 {
   148  			return false
   149  		}
   150  	}
   151  	if a.BOOL != nil {
   152  		c++
   153  		if c > 1 {
   154  			return false
   155  		}
   156  	}
   157  	if a.NULL != nil {
   158  		c++
   159  		if c > 1 {
   160  			return false
   161  		}
   162  	}
   163  	return c == 1
   164  }
   165  
   166  func NewAttributeValue() *AttributeValue {
   167  	a := new(AttributeValue)
   168  	a.L = make([]*AttributeValue, 0)
   169  	a.M = make(map[string]*AttributeValue)
   170  	a.SS = make([]string, 0)
   171  	a.NS = make([]string, 0)
   172  	a.BS = make([]string, 0)
   173  
   174  	// BOOL and NULL let to nil to represent vacuous state
   175  
   176  	return a
   177  }
   178  
   179  // Copy makes a copy of the this AttributeValue into ac.
   180  func (a *AttributeValue) Copy(ac *AttributeValue) error {
   181  	if a == nil {
   182  		return errors.New("AttributeValue.Copy: pointer receiver is nil")
   183  	}
   184  	if ac == nil {
   185  		return errors.New("AttributeValue.Copy: copy target attributevalue instance is nil")
   186  	}
   187  	ac.S = a.S
   188  	ac.N = a.N
   189  	ac.B = a.B
   190  
   191  	if a.BOOL == nil {
   192  		ac.BOOL = nil
   193  	} else {
   194  		ac.BOOL = new(bool)
   195  		*ac.BOOL = *a.BOOL
   196  	}
   197  
   198  	if a.NULL == nil {
   199  		ac.NULL = nil
   200  	} else {
   201  		ac.NULL = new(bool)
   202  		*ac.NULL = *a.NULL
   203  	}
   204  
   205  	l_ss := len(a.SS)
   206  	if l_ss != 0 {
   207  		ac.SS = make([]string, l_ss)
   208  		copy(ac.SS, a.SS)
   209  	}
   210  
   211  	l_ns := len(a.NS)
   212  	if l_ns != 0 {
   213  		ac.NS = make([]string, l_ns)
   214  		copy(ac.NS, a.NS)
   215  	}
   216  
   217  	l_bs := len(a.BS)
   218  	if l_bs != 0 {
   219  		ac.BS = make([]string, l_bs)
   220  		copy(ac.BS, a.BS)
   221  	}
   222  
   223  	// L is a recursive type, so the copy must be recursive
   224  	l_L := len(a.L)
   225  	if l_L != 0 {
   226  		ac.L = make([]*AttributeValue, l_L)
   227  		for i := range a.L {
   228  			ac.L[i] = NewAttributeValue()
   229  			L_i_cp_err := a.L[i].Copy(ac.L[i])
   230  			if L_i_cp_err != nil {
   231  				return L_i_cp_err
   232  			}
   233  		}
   234  	}
   235  
   236  	// M is a recursive type, so the copy must be recursive
   237  	l_M := len(a.M)
   238  	if l_M != 0 {
   239  		ac.M = make(map[string]*AttributeValue, l_M)
   240  		for k := range a.M {
   241  			ac.M[k] = NewAttributeValue()
   242  			M_k_cp_err := a.M[k].Copy(ac.M[k])
   243  			if M_k_cp_err != nil {
   244  				return M_k_cp_err
   245  			}
   246  		}
   247  	}
   248  	return nil
   249  }
   250  
   251  // InsertS sets the S field to string k
   252  func (a *AttributeValue) InsertS(k string) error {
   253  	if a == nil {
   254  		return errors.New("AttributeValue.InsertS: pointer receiver is nil")
   255  	}
   256  	a.S = k
   257  	return nil
   258  }
   259  
   260  // InsertN sets the N field to number string k
   261  func (a *AttributeValue) InsertN(k string) error {
   262  	if a == nil {
   263  		return errors.New("AttributeValue.InsertN: pointer receiver is nil")
   264  	}
   265  	fs, ferr := cast.AWSParseFloat(k)
   266  	if ferr != nil {
   267  		return ferr
   268  	}
   269  	a.N = fs
   270  	return nil
   271  }
   272  
   273  // InsertN_float64 works like InsertN but takes a float64
   274  func (a *AttributeValue) InsertN_float64(f float64) error {
   275  	if a == nil {
   276  		return errors.New("AttributeValue.InsertN_float64: pointer receiver is nil")
   277  	}
   278  	a.N = strconv.FormatFloat(f, 'f', -1, 64)
   279  	return nil
   280  }
   281  
   282  // InsertB sets the B field to string k, which it is assumed the caller has
   283  // already encoded.
   284  func (a *AttributeValue) InsertB(k string) error {
   285  	if a == nil {
   286  		return errors.New("AttributeValue.InsertB: pointer receiver is nil")
   287  	}
   288  	berr := cast.AWSParseBinary(k)
   289  	if berr != nil {
   290  		return berr
   291  	}
   292  	a.B = k
   293  	return nil
   294  }
   295  
   296  // InsertB_unencoded adds a new plain string to the B field.
   297  // The argument is assumed to be plaintext and will be base64 encoded.
   298  func (a *AttributeValue) InsertB_unencoded(k string) error {
   299  	if a == nil {
   300  		return errors.New("AttributeValue.Insert_unencoded: pointer receiver is nil")
   301  	}
   302  	a.B = base64.StdEncoding.EncodeToString([]byte(k))
   303  	return nil
   304  }
   305  
   306  // InsertSS adds a new string to the ss (JSON: SS) set.
   307  // SS is *generated* from an internal representation (UM_ss)
   308  // as it transforms a map into a list (a "set")
   309  func (a *AttributeValue) InsertSS(k string) error {
   310  	if a == nil {
   311  		return errors.New("AttributeValue.InsertSS: pointer receiver is nil")
   312  	}
   313  	for _, v := range a.SS {
   314  		if v == k {
   315  			return nil
   316  		}
   317  	}
   318  	a.SS = append(a.SS, k)
   319  	return nil
   320  }
   321  
   322  // InsertNS adds a new number string to the ns (JSON: NS) set.
   323  // String is parsed to make sure it is a represents a valid float.
   324  // NS is *generated* from an internal representation (UM_ns)
   325  // as it transforms a map into a list (a "set")
   326  func (a *AttributeValue) InsertNS(k string) error {
   327  	if a == nil {
   328  		return errors.New("AttributeValue.InsertNS: pointer receiver is nil")
   329  	}
   330  	fs, ferr := cast.AWSParseFloat(k)
   331  	if ferr != nil {
   332  		return ferr
   333  	}
   334  	for _, v := range a.NS {
   335  		if v == fs {
   336  			return nil
   337  		}
   338  	}
   339  	a.NS = append(a.NS, fs)
   340  	return nil
   341  }
   342  
   343  // InsertNS_float64 works like InsertNS but takes a float64
   344  func (a *AttributeValue) InsertNS_float64(f float64) error {
   345  	if a == nil {
   346  		return errors.New("AttributeValue.InsertNS_float64: pointer receiver is nil")
   347  	}
   348  	k := strconv.FormatFloat(f, 'f', -1, 64)
   349  	for _, v := range a.NS {
   350  		if v == k {
   351  			return nil
   352  		}
   353  	}
   354  	a.NS = append(a.NS, k)
   355  	return nil
   356  }
   357  
   358  // InsertBS adds a new base64 string to the bs (JSON: BS) set.
   359  // String is parsed to make sure it is a represents a valid base64 blob.
   360  // BS is *generated* from an internal representation (UM_bs)
   361  // as it transforms a map into a list (a "set").
   362  // The argument is assumed to be already encoded by the caller.
   363  func (a *AttributeValue) InsertBS(k string) error {
   364  	if a == nil {
   365  		return errors.New("AttributeValue.InsertBS: pointer receiver is nil")
   366  	}
   367  	berr := cast.AWSParseBinary(k)
   368  	if berr != nil {
   369  		return berr
   370  	}
   371  	for _, v := range a.BS {
   372  		if v == k {
   373  			return nil
   374  		}
   375  	}
   376  	a.BS = append(a.BS, k)
   377  	return nil
   378  }
   379  
   380  // InsertBS_unencoded adds a new plain string to the bs (JSON: BS) set.
   381  // BS is *generated* from an internal representation (UM_bs)
   382  // as it transforms a map into a list (a "set").
   383  // The argument is assumed to be plaintext and will be base64 encoded.
   384  func (a *AttributeValue) InsertBS_unencoded(k string) error {
   385  	if a == nil {
   386  		return errors.New("AttributeValue.InsertBS_unencoded: pointer receiver is nil")
   387  	}
   388  	b64_k := base64.StdEncoding.EncodeToString([]byte(k))
   389  	for _, v := range a.BS {
   390  		if v == b64_k {
   391  			return nil
   392  		}
   393  	}
   394  	a.BS = append(a.BS, b64_k)
   395  	return nil
   396  }
   397  
   398  // InsertL will append a pointer to a new AttributeValue v to the L list.
   399  func (a *AttributeValue) InsertL(v *AttributeValue) error {
   400  	if a == nil {
   401  		return errors.New("AttributeValue.InsertL: pointer receiver is nil")
   402  	}
   403  	v_cp := NewAttributeValue()
   404  	cp_err := v.Copy(v_cp)
   405  	if cp_err != nil {
   406  		return cp_err
   407  	}
   408  	a.L = append(a.L, v_cp)
   409  	return nil
   410  }
   411  
   412  // InsertM will insert a pointer to a new AttributeValue v to the M map for key k.
   413  // If k was previously set in the M map, the value will be overwritten.
   414  func (a *AttributeValue) InsertM(k string, v *AttributeValue) error {
   415  	if a == nil {
   416  		return errors.New("AttributeValue.InsertM: pointer receiver is nil")
   417  	}
   418  	v_cp := NewAttributeValue()
   419  	cp_err := v.Copy(v_cp)
   420  	if cp_err != nil {
   421  		return cp_err
   422  	}
   423  	a.M[k] = v_cp
   424  	return nil
   425  }
   426  
   427  // InsertBOOL will set the BOOL field.
   428  func (a *AttributeValue) InsertBOOL(b bool) error {
   429  	if a == nil {
   430  		return errors.New("AttributeValue.InsertBOOL: pointer receiver is nil")
   431  	}
   432  	if a.BOOL == nil {
   433  		a.BOOL = new(bool)
   434  	}
   435  	*a.BOOL = b
   436  	return nil
   437  }
   438  
   439  // InsertNULL will set the NULL field.
   440  func (a *AttributeValue) InsertNULL(b bool) error {
   441  	if a == nil {
   442  		return errors.New("AttributeValue.InsertNULL: pointer receiver is nil")
   443  	}
   444  	if a.NULL == nil {
   445  		a.NULL = new(bool)
   446  	}
   447  	*a.NULL = b
   448  	return nil
   449  }
   450  
   451  // AttributeValueMap is used throughout GoDynamo
   452  type AttributeValueMap map[string]*AttributeValue
   453  
   454  func NewAttributeValueMap() AttributeValueMap {
   455  	m := make(map[string]*AttributeValue)
   456  	return m
   457  }
   458  
   459  // Copy makes a copy of the this AttributeValueMap into ac.
   460  func (a AttributeValueMap) Copy(ac AttributeValueMap) error {
   461  	if a == nil {
   462  		return errors.New("AttributeValueMap.Copy: pointer receiver is nil")
   463  	}
   464  	if ac == nil {
   465  		return errors.New("AttributeValueMap.Copy: copy target attributeValueMap instance is nil")
   466  	}
   467  	for key, attributeValue := range a {
   468  		av_cp := NewAttributeValue()
   469  		if av_cp == nil {
   470  			return errors.New("AttributeValueMap.Copy: copy attributeValue is nil")
   471  		}
   472  		cp_err := attributeValue.Copy(av_cp)
   473  		if cp_err != nil {
   474  			e := fmt.Sprintf("AttributeValueMap.Copy: copy attributeValue err:%s", cp_err.Error())
   475  			return errors.New(e)
   476  		}
   477  		ac[key] = av_cp
   478  	}
   479  	return nil
   480  }
   481  
   482  // AttributeValueUpdate is used in UpdateItem
   483  type AttributeValueUpdate struct {
   484  	Action string          `json:",omitempty"`
   485  	Value  *AttributeValue `json:",omitempty"`
   486  }
   487  
   488  func NewAttributeValueUpdate() *AttributeValueUpdate {
   489  	a := new(AttributeValueUpdate)
   490  	a.Value = NewAttributeValue()
   491  	return a
   492  }
   493  
   494  type AttributeValueUpdateMap map[string]*AttributeValueUpdate
   495  
   496  func NewAttributeValueUpdateMap() AttributeValueUpdateMap {
   497  	m := make(map[string]*AttributeValueUpdate)
   498  	return m
   499  }
   500  
   501  // BasicJSONToAttributeValueMap provides a lossy mapping from "basic" json to an AttributeValueMap.
   502  // This allows for the type of "JSON Document" functionality employed in the
   503  // current AWS SDK and outlined in the docs
   504  // (see http://aws.amazon.com/blogs/aws/dynamodb-update-json-and-more/)
   505  func BasicJSONToAttributeValueMap(b []byte) (AttributeValueMap, error) {
   506  	if b == nil {
   507  		return nil, errors.New("AttributeValue.Insert.BasicJSONToAttributeValueMap: arg is nil")
   508  	}
   509  	// unmarshal the arbitrary json
   510  	var i interface{}
   511  	um_err := json.Unmarshal(b, &i)
   512  	if um_err != nil {
   513  		return nil, um_err
   514  	}
   515  	return InterfaceToAttributeValueMap(i)
   516  }
   517  
   518  // InterfaceToAttributeValueMap attempts to coerce an appropriate interface {} to
   519  // an AttributeValueMap
   520  func InterfaceToAttributeValueMap(i interface{}) (AttributeValueMap, error) {
   521  	m, m_ok := i.(map[string]interface{})
   522  	if !m_ok {
   523  		return nil, errors.New("AttributeValue.InterfaceToAttributeValueMap: top level unmarshal not (map[string] interface{})")
   524  	}
   525  	avm := NewAttributeValueMap()
   526  	for k, v := range m {
   527  		c, cerr := CoerceToAttributeValue(v)
   528  		if cerr != nil {
   529  			return nil, cerr
   530  		}
   531  		avm[k] = c
   532  	}
   533  	return avm, nil
   534  }
   535  
   536  // BasicJSONToAttributeValue provides a lossy mapping from "basic" json to an AttributeValue.
   537  // This allows for the type of "JSON Document" functionality employed in the
   538  // current AWS SDK and outlined in the docs
   539  // (see http://aws.amazon.com/blogs/aws/dynamodb-update-json-and-more/)
   540  func BasicJSONToAttributeValue(b []byte) (*AttributeValue, error) {
   541  	if b == nil {
   542  		return nil, errors.New("AttributeValue.Insert.BasicJSONToAttributeValue: arg is nil")
   543  	}
   544  	// unmarshal the arbitrary json
   545  	var i interface{}
   546  	um_err := json.Unmarshal(b, &i)
   547  	if um_err != nil {
   548  		return nil, um_err
   549  	}
   550  	return InterfaceToAttributeValue(i)
   551  }
   552  
   553  // InterfaceToAttributeValue attempts to coerce an appropriate interface {} to
   554  // an *AttributeValue
   555  func InterfaceToAttributeValue(i interface{}) (*AttributeValue, error) {
   556  	return CoerceToAttributeValue(i)
   557  }
   558  
   559  // CoerceToAttributeValue is a lossy translation for basic json to the AWS serialization format
   560  // for AttributeValue. There are types that will be dropped as they are indistinguishable
   561  // without their type designations:
   562  // 1. binary will be dropped as the values will always be coerced to string.
   563  // 2. null (as a type, not a value) will always be coerced to bool.
   564  func CoerceToAttributeValue(i interface{}) (*AttributeValue, error) {
   565  	a := NewAttributeValue()
   566  
   567  	// bool (null also coerced to bool)
   568  	b, b_ok := i.(bool)
   569  	if b_ok {
   570  		a.BOOL = new(bool)
   571  		*a.BOOL = b
   572  		return a, nil
   573  	}
   574  
   575  	// number - float (the default unmarshal will always use this type)
   576  	n, n_ok := i.(float64)
   577  	if n_ok {
   578  		a.N = strconv.FormatFloat(n, 'f', -1, 64)
   579  		return a, nil
   580  	}
   581  
   582  	// string (binary also coerced to string)
   583  	s, s_ok := i.(string)
   584  	if s_ok {
   585  		a.S = s
   586  		return a, nil
   587  	}
   588  
   589  	// map of string -> *AttributeValue
   590  	m, m_ok := i.(map[string]interface{})
   591  	if m_ok {
   592  		for k, v := range m {
   593  			a_child, a_child_err := CoerceToAttributeValue(v)
   594  			if a_child_err != nil {
   595  				return nil, a_child_err
   596  			}
   597  			a.M[k] = a_child
   598  		}
   599  		return a, nil
   600  	}
   601  
   602  	// the only type of list that is inferred by the generic unmarshal is []interface{}.
   603  	// we need to use further type inference to determine if the list can be made into
   604  	// an NS or SS...or is heterogenous and should be turned into an L
   605  	l, l_ok := i.([]interface{})
   606  	if l_ok {
   607  		l_len := len(l)
   608  
   609  		// check first if the list is composed strictly of floats or strings. If so,
   610  		// then we can make a NS or SS list
   611  		float_vals := make([]float64, 0)
   612  		string_vals := make([]string, 0)
   613  		for _, u := range l {
   614  			f, f_ok := u.(float64)
   615  			if f_ok {
   616  				float_vals = append(float_vals, f)
   617  			} else {
   618  				s, s_ok := u.(string)
   619  				if s_ok {
   620  					string_vals = append(string_vals, s)
   621  				}
   622  			}
   623  		}
   624  		floats_len := len(float_vals)
   625  		strings_len := len(string_vals)
   626  
   627  		// the list is all floats, turn it into an NS
   628  		if (floats_len == l_len) && (strings_len == 0) {
   629  			for _, f := range float_vals {
   630  				ferr := a.InsertNS_float64(f)
   631  				if ferr != nil {
   632  					return nil, ferr
   633  				}
   634  			}
   635  			return a, nil
   636  		}
   637  		// the list is all strings, turn it into an SS
   638  		if (strings_len == l_len) && (floats_len == 0) {
   639  			for _, v := range string_vals {
   640  				_ = a.InsertSS(v)
   641  			}
   642  			return a, nil
   643  		}
   644  
   645  		// the list was not just strictly strings or floats
   646  		for _, v := range l {
   647  			a_child, a_child_err := CoerceToAttributeValue(v)
   648  			if a_child_err != nil {
   649  				return nil, a_child_err
   650  			}
   651  			a.L = append(a.L, a_child)
   652  		}
   653  		return a, nil
   654  	}
   655  
   656  	e := fmt.Sprintf("no coercion for %v", i)
   657  	return nil, errors.New(e)
   658  }
   659  
   660  // ToBasicJSON provides a mapping from an AttributeValueMap to basic json
   661  // This allows for items from dynamo to be printed in a flat fashion if desired.
   662  func (a AttributeValueMap) ToBasicJSON() ([]byte, error) {
   663  	c, cerr := a.ToInterface()
   664  	if cerr != nil {
   665  		return nil, cerr
   666  	}
   667  	b, merr := json.Marshal(c)
   668  	if merr != nil {
   669  		return nil, merr
   670  	} else {
   671  		return b, nil
   672  	}
   673  }
   674  
   675  // AttributeValueMapToInterface converts the map into a map of the key names to interface types
   676  // that do not have type designations, and be marshaled into basic json
   677  func (a AttributeValueMap) ToInterface() (interface{}, error) {
   678  	m := make(map[string]interface{})
   679  	for k, v := range a {
   680  		c, cerr := v.ToInterface()
   681  		if cerr != nil {
   682  			return nil, cerr
   683  		} else {
   684  			m[k] = c
   685  		}
   686  	}
   687  	return m, nil
   688  }
   689  
   690  // ToBasicJSON provides a mapping from an AttributeValue to basic json
   691  // This allows for items from dynamo to be printed in a flat fashion if desired.
   692  func (a *AttributeValue) ToBasicJSON() ([]byte, error) {
   693  	if a == nil {
   694  		return nil, errors.New("AttributeValue.ToBasicJSON: pointer receiver is nil")
   695  	}
   696  	c, cerr := a.ToInterface()
   697  	if cerr != nil {
   698  		return nil, cerr
   699  	}
   700  	b, merr := json.Marshal(c)
   701  	if merr != nil {
   702  		return nil, merr
   703  	} else {
   704  		return b, nil
   705  	}
   706  }
   707  
   708  // AttributeValueToInterface strips the AttributeValue type designations and returns a structure
   709  // that can be marshaled into basic json.
   710  func (a *AttributeValue) ToInterface() (interface{}, error) {
   711  	if a == nil {
   712  		return "", errors.New("AttributeValue.ToInterface: pointer receiver is nil")
   713  	}
   714  	if a.BOOL != nil {
   715  		return *a.BOOL, nil
   716  	}
   717  	if a.NULL != nil {
   718  		return *a.NULL, nil
   719  	}
   720  	if a.S != "" {
   721  		return a.S, nil
   722  	}
   723  	if a.B != "" {
   724  		return a.B, nil
   725  	}
   726  	if a.N != "" {
   727  		f, ferr := strconv.ParseFloat(a.N, 64)
   728  		if ferr != nil {
   729  			return nil, ferr
   730  		} else {
   731  			return f, nil
   732  		}
   733  	}
   734  	if len(a.SS) != 0 {
   735  		return a.SS, nil
   736  	}
   737  	if len(a.BS) != 0 {
   738  		return a.BS, nil
   739  	}
   740  	ns_len := len(a.NS)
   741  	if ns_len != 0 {
   742  		ns := make([]float64, ns_len)
   743  		for i, n := range a.NS {
   744  			f, ferr := strconv.ParseFloat(n, 64)
   745  			if ferr != nil {
   746  				return nil, ferr
   747  			} else {
   748  				ns[i] = f
   749  			}
   750  		}
   751  		return ns, nil
   752  	}
   753  	l_len := len(a.L)
   754  	if l_len != 0 {
   755  		ls := make([]interface{}, l_len)
   756  		for i, v := range a.L {
   757  			c, cerr := v.ToInterface()
   758  			if cerr != nil {
   759  				return nil, cerr
   760  			} else {
   761  				ls[i] = c
   762  			}
   763  		}
   764  		return ls, nil
   765  	}
   766  	m_len := len(a.M)
   767  	if m_len != 0 {
   768  		m := make(map[string]interface{})
   769  		for k, v := range a.M {
   770  			c, cerr := v.ToInterface()
   771  			if cerr != nil {
   772  				return nil, cerr
   773  			} else {
   774  				m[k] = c
   775  			}
   776  		}
   777  		return m, nil
   778  	}
   779  	e := fmt.Sprintf("no coercion for %v", a)
   780  	return nil, errors.New(e)
   781  }