-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #93 from sclasen/snapshots-streams
Snapshots streams
- Loading branch information
Showing
5 changed files
with
474 additions
and
152 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
package jsonutil | ||
|
||
// copied from github.com/aws/aws-sdk-go/internal/protocol/json/jsonutil/unmarshal.go | ||
// to give access to AWS-internal unmarshalling disallowed in go 1.5. | ||
// do not use for anything in the critical path. use AWS public packages where possible. | ||
|
||
import ( | ||
"encoding/base64" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"reflect" | ||
"strings" | ||
"time" | ||
) | ||
|
||
// UnmarshalJSON reads a stream and unmarshals the results in object v. | ||
func UnmarshalJSON(v interface{}, stream io.Reader) error { | ||
var out interface{} | ||
|
||
b, err := ioutil.ReadAll(stream) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if len(b) == 0 { | ||
return nil | ||
} | ||
|
||
if err := json.Unmarshal(b, &out); err != nil { | ||
return err | ||
} | ||
|
||
return unmarshalAny(reflect.ValueOf(v), out, "") | ||
} | ||
|
||
func unmarshalAny(value reflect.Value, data interface{}, tag reflect.StructTag) error { | ||
vtype := value.Type() | ||
if vtype.Kind() == reflect.Ptr { | ||
vtype = vtype.Elem() // check kind of actual element type | ||
} | ||
|
||
t := tag.Get("type") | ||
if t == "" { | ||
switch vtype.Kind() { | ||
case reflect.Struct: | ||
// also it can't be a time object | ||
if _, ok := value.Interface().(*time.Time); !ok { | ||
t = "structure" | ||
} | ||
case reflect.Slice: | ||
// also it can't be a byte slice | ||
if _, ok := value.Interface().([]byte); !ok { | ||
t = "list" | ||
} | ||
case reflect.Map: | ||
t = "map" | ||
} | ||
} | ||
|
||
switch t { | ||
case "structure": | ||
if field, ok := vtype.FieldByName("SDKShapeTraits"); ok { | ||
tag = field.Tag | ||
} | ||
return unmarshalStruct(value, data, tag) | ||
case "list": | ||
return unmarshalList(value, data, tag) | ||
case "map": | ||
return unmarshalMap(value, data, tag) | ||
default: | ||
return unmarshalScalar(value, data, tag) | ||
} | ||
} | ||
|
||
func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTag) error { | ||
if data == nil { | ||
return nil | ||
} | ||
mapData, ok := data.(map[string]interface{}) | ||
if !ok { | ||
return fmt.Errorf("JSON value is not a structure (%#v)", data) | ||
} | ||
|
||
t := value.Type() | ||
if value.Kind() == reflect.Ptr { | ||
if value.IsNil() { // create the structure if it's nil | ||
s := reflect.New(value.Type().Elem()) | ||
value.Set(s) | ||
value = s | ||
} | ||
|
||
value = value.Elem() | ||
t = t.Elem() | ||
} | ||
|
||
// unwrap any payloads | ||
if payload := tag.Get("payload"); payload != "" { | ||
field, _ := t.FieldByName(payload) | ||
return unmarshalAny(value.FieldByName(payload), data, field.Tag) | ||
} | ||
|
||
for i := 0; i < t.NumField(); i++ { | ||
field := t.Field(i) | ||
if c := field.Name[0:1]; strings.ToLower(c) == c { | ||
continue // ignore unexported fields | ||
} | ||
|
||
// figure out what this field is called | ||
name := field.Name | ||
if locName := field.Tag.Get("locationName"); locName != "" { | ||
name = locName | ||
} | ||
|
||
member := value.FieldByName(field.Name) | ||
err := unmarshalAny(member, mapData[name], field.Tag) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func unmarshalList(value reflect.Value, data interface{}, tag reflect.StructTag) error { | ||
if data == nil { | ||
return nil | ||
} | ||
listData, ok := data.([]interface{}) | ||
if !ok { | ||
return fmt.Errorf("JSON value is not a list (%#v)", data) | ||
} | ||
|
||
if value.IsNil() { | ||
l := len(listData) | ||
value.Set(reflect.MakeSlice(value.Type(), l, l)) | ||
} | ||
|
||
for i, c := range listData { | ||
err := unmarshalAny(value.Index(i), c, "") | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func unmarshalMap(value reflect.Value, data interface{}, tag reflect.StructTag) error { | ||
if data == nil { | ||
return nil | ||
} | ||
mapData, ok := data.(map[string]interface{}) | ||
if !ok { | ||
return fmt.Errorf("JSON value is not a map (%#v)", data) | ||
} | ||
|
||
if value.IsNil() { | ||
value.Set(reflect.MakeMap(value.Type())) | ||
} | ||
|
||
for k, v := range mapData { | ||
kvalue := reflect.ValueOf(k) | ||
vvalue := reflect.New(value.Type().Elem()).Elem() | ||
|
||
unmarshalAny(vvalue, v, "") | ||
value.SetMapIndex(kvalue, vvalue) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTag) error { | ||
errf := func() error { | ||
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type()) | ||
} | ||
|
||
switch d := data.(type) { | ||
case nil: | ||
return nil // nothing to do here | ||
case string: | ||
switch value.Interface().(type) { | ||
case *string: | ||
value.Set(reflect.ValueOf(&d)) | ||
case []byte: | ||
b, err := base64.StdEncoding.DecodeString(d) | ||
if err != nil { | ||
return err | ||
} | ||
value.Set(reflect.ValueOf(b)) | ||
default: | ||
return errf() | ||
} | ||
case float64: | ||
switch value.Interface().(type) { | ||
case *int64: | ||
di := int64(d) | ||
value.Set(reflect.ValueOf(&di)) | ||
case *float64: | ||
value.Set(reflect.ValueOf(&d)) | ||
case *time.Time: | ||
t := time.Unix(int64(d), 0).UTC() | ||
value.Set(reflect.ValueOf(&t)) | ||
default: | ||
return errf() | ||
} | ||
case bool: | ||
switch value.Interface().(type) { | ||
case *bool: | ||
value.Set(reflect.ValueOf(&d)) | ||
default: | ||
return errf() | ||
} | ||
default: | ||
return fmt.Errorf("unsupported JSON value (%v)", data) | ||
} | ||
return nil | ||
} |
Oops, something went wrong.