Tao Wen 8 éve
szülő
commit
95823d0bf1
4 módosított fájl, 454 hozzáadás és 7 törlés
  1. 2 0
      feature_any.go
  2. 4 5
      feature_any_array.go
  3. 425 2
      feature_any_object.go
  4. 23 0
      jsoniter_object_test.go

+ 2 - 0
feature_any.go

@@ -84,6 +84,8 @@ func Wrap(val interface{}) Any {
 	switch type_.Kind() {
 	case reflect.Slice:
 		return wrapArray(val)
+	case reflect.Struct:
+		return wrapStruct(val)
 	case reflect.String:
 		return WrapString(val.(string))
 	case reflect.Int:

+ 4 - 5
feature_any_array.go

@@ -296,11 +296,10 @@ type arrayAny struct {
 	err      error
 	cache    []Any
 	val      reflect.Value
-	parsedTo int
 }
 
 func wrapArray(val interface{}) *arrayAny {
-	return &arrayAny{baseAny{}, nil, nil, reflect.ValueOf(val), 0}
+	return &arrayAny{baseAny{}, nil, nil, reflect.ValueOf(val)}
 }
 
 func (any *arrayAny) ValueType() ValueType {
@@ -373,7 +372,7 @@ func (any *arrayAny) ToFloat64() float64 {
 }
 
 func (any *arrayAny) ToString() string {
-	if any.parsedTo == 0 {
+	if len(any.cache) == 0 {
 		// nothing has been parsed yet
 		str, err := MarshalToString(any.val.Interface())
 		any.err = err
@@ -390,7 +389,7 @@ func (any *arrayAny) fillCacheUntil(idx int) Any {
 	if idx < len(any.cache) {
 		return any.cache[idx]
 	} else {
-		for i := any.parsedTo; i < any.val.Len(); i++ {
+		for i := len(any.cache); i < any.val.Len(); i++ {
 			element := Wrap(any.val.Index(i).Interface())
 			any.cache = append(any.cache, element)
 			if idx == i {
@@ -464,7 +463,7 @@ func (any *arrayAny) SetArray(newList []Any) bool {
 }
 
 func (any *arrayAny) WriteTo(stream *Stream) {
-	if any.parsedTo == 0 {
+	if len(any.cache) == 0 {
 		// nothing has been parsed yet
 		stream.WriteVal(any.val)
 	} else {

+ 425 - 2
feature_any_object.go

@@ -3,6 +3,7 @@ package jsoniter
 import (
 	"unsafe"
 	"fmt"
+	"reflect"
 )
 
 type objectLazyAny struct {
@@ -107,7 +108,7 @@ func (any *objectLazyAny) fillCache() {
 }
 
 func (any *objectLazyAny) LastError() error {
-	return nil
+	return any.err
 }
 
 func (any *objectLazyAny) ToBool() bool {
@@ -309,4 +310,426 @@ func (any *objectLazyAny) WriteTo(stream *Stream) {
 func (any *objectLazyAny) GetInterface() interface{} {
 	any.fillCache()
 	return any.cache
-}
+}
+
+type objectAny struct {
+	baseAny
+	err   error
+	cache map[string]Any
+	val   reflect.Value
+}
+
+func wrapStruct(val interface{}) *objectAny {
+	return &objectAny{baseAny{}, nil, nil, reflect.ValueOf(val)}
+}
+
+func (any *objectAny) ValueType() ValueType {
+	return Object
+}
+
+func (any *objectAny) Parse() *Iterator {
+	return nil
+}
+
+func (any *objectAny) fillCacheUntil(target string) Any {
+	if any.cache == nil {
+		any.cache = map[string]Any{}
+	}
+	element, found := any.cache[target]
+	if found {
+		return element
+	}
+	for i := len(any.cache); i < any.val.NumField(); i++ {
+		field := any.val.Field(i)
+		fieldName := any.val.Type().Field(i).Name
+		var element Any
+		if field.CanInterface() {
+			element = Wrap(field.Interface())
+		} else {
+			element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", fieldName, any.cache)}
+		}
+		any.cache[fieldName] = element
+		if fieldName == target {
+			return element
+		}
+	}
+	return nil
+}
+
+func (any *objectAny) fillCache() {
+	if any.cache == nil {
+		any.cache = map[string]Any{}
+	}
+	for i := 0; i < any.val.NumField(); i++ {
+		field := any.val.Field(i)
+		fieldName := any.val.Type().Field(i).Name
+		var element Any
+		if field.CanInterface() {
+			element = Wrap(field.Interface())
+		} else {
+			element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", fieldName, any.cache)}
+		}
+		any.cache[fieldName] = element
+	}
+}
+
+func (any *objectAny) LastError() error {
+	return any.err
+}
+
+func (any *objectAny) ToBool() bool {
+	if any.cache == nil {
+		any.IterateObject() // trigger first value read
+	}
+	return len(any.cache) != 0
+}
+
+func (any *objectAny) ToInt() int {
+	if any.cache == nil {
+		any.IterateObject() // trigger first value read
+	}
+	if len(any.cache) == 0 {
+		return 0
+	}
+	return 1
+}
+
+func (any *objectAny) ToInt32() int32 {
+	if any.cache == nil {
+		any.IterateObject() // trigger first value read
+	}
+	if len(any.cache) == 0 {
+		return 0
+	}
+	return 1
+}
+
+func (any *objectAny) ToInt64() int64 {
+	if any.cache == nil {
+		any.IterateObject() // trigger first value read
+	}
+	if len(any.cache) == 0 {
+		return 0
+	}
+	return 1
+}
+
+func (any *objectAny) ToFloat32() float32 {
+	if any.cache == nil {
+		any.IterateObject() // trigger first value read
+	}
+	if len(any.cache) == 0 {
+		return 0
+	}
+	return 1
+}
+
+func (any *objectAny) ToFloat64() float64 {
+	if any.cache == nil {
+		any.IterateObject() // trigger first value read
+	}
+	if len(any.cache) == 0 {
+		return 0
+	}
+	return 1
+}
+
+func (any *objectAny) ToString() string {
+	if len(any.cache) == 0 {
+		str, err := MarshalToString(any.val)
+		any.err = err
+		return str
+	} else {
+		any.fillCache()
+		str, err := MarshalToString(any.cache)
+		any.err = err
+		return str
+	}
+}
+
+func (any *objectAny) Get(path ...interface{}) Any {
+	if len(path) == 0 {
+		return any
+	}
+	var element Any
+	key, ok := path[0].(string)
+	if ok {
+		element = any.fillCacheUntil(key)
+		if element == nil {
+			element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", key, any.cache)}
+		}
+	} else {
+		element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", key, any.cache)}
+	}
+	if len(path) == 1 {
+		return element
+	} else {
+		return element.Get(path[1:]...)
+	}
+}
+
+func (any *objectAny) Keys() []string {
+	any.fillCache()
+	keys := make([]string, 0, len(any.cache))
+	for key := range any.cache {
+		keys = append(keys, key)
+	}
+	return keys
+}
+
+func (any *objectAny) Size() int {
+	any.fillCache()
+	return len(any.cache)
+}
+
+func (any *objectAny) IterateObject() (func() (string, Any, bool), bool) {
+	if any.cache == nil {
+		any.cache = map[string]Any{}
+	}
+	if any.val.NumField() == 0 {
+		return nil, false
+	}
+	cacheKeys := make([]string, len(any.cache))
+	i := 0
+	for key := range any.cache {
+		cacheKeys[i] = key
+		i++
+	}
+	i = 0
+	return func() (string, Any, bool) {
+		if i == any.val.NumField() {
+			return "", nil, false
+		}
+		var fieldName string
+		var fieldValueAsAny Any
+		if i == len(cacheKeys) {
+			fieldName = any.val.Type().Field(i).Name
+			fmt.Println(fieldName)
+			cacheKeys = append(cacheKeys, fieldName)
+			fieldValue := any.val.Field(i)
+			if fieldValue.CanInterface() {
+				fieldValueAsAny = Wrap(fieldValue.Interface())
+				any.cache[fieldName] = fieldValueAsAny
+			} else {
+				fieldValueAsAny = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", fieldName, any.cache)}
+				any.cache[fieldName] = fieldValueAsAny
+			}
+		} else {
+			fieldName = cacheKeys[i]
+			fieldValueAsAny = any.cache[fieldName]
+		}
+		i++
+		return fieldName, fieldValueAsAny, i != any.val.NumField()
+	}, true
+}
+
+func (any *objectAny) GetObject() map[string]Any {
+	any.fillCache()
+	return any.cache
+}
+
+func (any *objectAny) SetObject(val map[string]Any) bool {
+	any.fillCache()
+	any.cache = val
+	return true
+}
+
+func (any *objectAny) WriteTo(stream *Stream) {
+	if len(any.cache) == 0 {
+		// nothing has been parsed yet
+		stream.WriteVal(any.val)
+	} else {
+		any.fillCache()
+		stream.WriteVal(any.cache)
+	}
+}
+
+func (any *objectAny) GetInterface() interface{} {
+	any.fillCache()
+	return any.cache
+}
+
+
+type mapAny struct {
+	baseAny
+	err   error
+	cache map[string]Any
+	val   reflect.Value
+}
+
+func wrapMap(val interface{}) *mapAny {
+	return &mapAny{baseAny{}, nil, nil, reflect.ValueOf(val)}
+}
+
+func (any *mapAny) ValueType() ValueType {
+	return Object
+}
+
+func (any *mapAny) Parse() *Iterator {
+	return nil
+}
+
+func (any *mapAny) fillCacheUntil(target string) Any {
+	if any.cache == nil {
+		any.cache = map[string]Any{}
+	}
+	element, found := any.cache[target]
+	if found {
+		return element
+	}
+	for _, key := range any.val.MapKeys() {
+		keyAsStr := key.String()
+		_, found := any.cache[keyAsStr]
+		if found {
+			continue
+		}
+		element := Wrap(any.val.MapIndex(key).Interface())
+		any.cache[keyAsStr] = element
+		if keyAsStr == target {
+			return element
+		}
+	}
+	return nil
+}
+
+func (any *mapAny) fillCache() {
+}
+
+func (any *mapAny) LastError() error {
+	return any.err
+}
+
+func (any *mapAny) ToBool() bool {
+	if any.cache == nil {
+		any.IterateObject() // trigger first value read
+	}
+	return len(any.cache) != 0
+}
+
+func (any *mapAny) ToInt() int {
+	if any.cache == nil {
+		any.IterateObject() // trigger first value read
+	}
+	if len(any.cache) == 0 {
+		return 0
+	}
+	return 1
+}
+
+func (any *mapAny) ToInt32() int32 {
+	if any.cache == nil {
+		any.IterateObject() // trigger first value read
+	}
+	if len(any.cache) == 0 {
+		return 0
+	}
+	return 1
+}
+
+func (any *mapAny) ToInt64() int64 {
+	if any.cache == nil {
+		any.IterateObject() // trigger first value read
+	}
+	if len(any.cache) == 0 {
+		return 0
+	}
+	return 1
+}
+
+func (any *mapAny) ToFloat32() float32 {
+	if any.cache == nil {
+		any.IterateObject() // trigger first value read
+	}
+	if len(any.cache) == 0 {
+		return 0
+	}
+	return 1
+}
+
+func (any *mapAny) ToFloat64() float64 {
+	if any.cache == nil {
+		any.IterateObject() // trigger first value read
+	}
+	if len(any.cache) == 0 {
+		return 0
+	}
+	return 1
+}
+
+func (any *mapAny) ToString() string {
+	if len(any.cache) == 0 {
+		str, err := MarshalToString(any.val)
+		any.err = err
+		return str
+	} else {
+		any.fillCache()
+		str, err := MarshalToString(any.cache)
+		any.err = err
+		return str
+	}
+}
+
+func (any *mapAny) Get(path ...interface{}) Any {
+	if len(path) == 0 {
+		return any
+	}
+	var element Any
+	key, ok := path[0].(string)
+	if ok {
+		element = any.fillCacheUntil(key)
+		if element == nil {
+			element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", key, any.cache)}
+		}
+	} else {
+		element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", key, any.cache)}
+	}
+	if len(path) == 1 {
+		return element
+	} else {
+		return element.Get(path[1:]...)
+	}
+}
+
+func (any *mapAny) Keys() []string {
+	any.fillCache()
+	keys := make([]string, 0, len(any.cache))
+	for key := range any.cache {
+		keys = append(keys, key)
+	}
+	return keys
+}
+
+func (any *mapAny) Size() int {
+	any.fillCache()
+	return len(any.cache)
+}
+
+func (any *mapAny) IterateObject() (func() (string, Any, bool), bool) {
+	return nil, false
+}
+
+func (any *mapAny) GetObject() map[string]Any {
+	any.fillCache()
+	return any.cache
+}
+
+func (any *mapAny) SetObject(val map[string]Any) bool {
+	any.fillCache()
+	any.cache = val
+	return true
+}
+
+func (any *mapAny) WriteTo(stream *Stream) {
+	if len(any.cache) == 0 {
+		// nothing has been parsed yet
+		stream.WriteVal(any.val)
+	} else {
+		any.fillCache()
+		stream.WriteVal(any.cache)
+	}
+}
+
+func (any *mapAny) GetInterface() interface{} {
+	any.fillCache()
+	return any.cache
+}

+ 23 - 0
jsoniter_object_test.go

@@ -165,6 +165,29 @@ func Test_object_lazy_any_set(t *testing.T) {
 	should.Equal(`{"a":1}`, str)
 }
 
+func Test_wrap_object(t *testing.T) {
+	should := require.New(t)
+	type TestObject struct {
+		Field1 string
+		field2 string
+	}
+	any := Wrap(TestObject{"hello", "world"})
+	should.Equal("hello", any.Get("Field1").ToString())
+	any = Wrap(TestObject{"hello", "world"})
+	should.Equal(2, any.Size())
+	any = Wrap(TestObject{"hello", "world"})
+	vals := map[string]string{}
+	var k string
+	var v Any
+	for next, hasNext := any.IterateObject(); hasNext; {
+		k, v, hasNext = next()
+		if v.ValueType() == String {
+			vals[k] = v.ToString()
+		}
+	}
+	should.Equal(map[string]string{"Field1":"hello"}, vals)
+}
+
 func Test_write_object(t *testing.T) {
 	should := require.New(t)
 	buf := &bytes.Buffer{}