Tao Wen il y a 9 ans
Parent
commit
b32182e935
3 fichiers modifiés avec 249 ajouts et 11 suppressions
  1. 71 11
      jsoniter.go
  2. 84 0
      jsoniter_nested_test.go
  3. 94 0
      jsoniter_object_test.go

+ 71 - 11
jsoniter.go

@@ -304,18 +304,18 @@ func appendRune(p []byte, r rune) []byte {
 	}
 }
 
-func (iter *Iterator) ReadArray() bool {
+func (iter *Iterator) ReadArray() (ret bool) {
 	iter.skipWhitespaces()
 	c := iter.readByte()
 	if iter.Error != nil {
-		return false
+		return
 	}
-	if c == '[' {
+	switch c {
+	case '[': {
 		iter.skipWhitespaces()
 		c = iter.readByte()
 		if iter.Error != nil {
-			iter.ReportError("ReadArray", "eof after [")
-			return false
+			return
 		}
 		if c == ']' {
 			return false
@@ -324,11 +324,71 @@ func (iter *Iterator) ReadArray() bool {
 			return true
 		}
 	}
-	if c == ']' {
-		return false
-	} else if c == ',' {
-		return true
+	case ']': return false
+	case ',': return true
+	default:
+		iter.ReportError("ReadArray", "expect [ or , or ]")
+		return
+	}
+}
+
+func (iter *Iterator) ReadObject() (ret string) {
+	iter.skipWhitespaces()
+	c := iter.readByte()
+	if iter.Error != nil {
+		return
 	}
-	iter.ReportError("ReadArray", "expect [ or ,")
-	return false
+	switch c {
+	case '{': {
+		iter.skipWhitespaces()
+		c = iter.readByte()
+		if iter.Error != nil {
+			return
+		}
+		switch c {
+		case '}':
+			return "" // end of object
+		case '"':
+			iter.unreadByte()
+			field := iter.readObjectField()
+			if iter.Error != nil {
+				return
+			}
+			return field
+		default:
+			iter.ReportError("ReadObject", `expect " after {`)
+			return
+		}
+	}
+	case ',':
+		iter.skipWhitespaces()
+		field := iter.readObjectField()
+		if iter.Error != nil {
+			return
+		}
+		return field
+	case '}':
+		return "" // end of object
+	default:
+		iter.ReportError("ReadObject", `expect { or , or }`)
+		return
+	}
+}
+
+func (iter *Iterator) readObjectField() (ret string) {
+	field := iter.ReadString()
+	if iter.Error != nil {
+		return
+	}
+	iter.skipWhitespaces()
+	c := iter.readByte()
+	if iter.Error != nil {
+		return
+	}
+	if c != ':' {
+		iter.ReportError("ReadObject", "expect : after object field")
+		return
+	}
+	iter.skipWhitespaces()
+	return field
 }

+ 84 - 0
jsoniter_nested_test.go

@@ -0,0 +1,84 @@
+package jsoniter
+
+import (
+	"testing"
+	"reflect"
+	"encoding/json"
+)
+
+type Level1 struct {
+	Hello []Level2
+}
+
+type Level2 struct {
+	World string
+}
+
+func Test_nested(t *testing.T) {
+	iter := ParseString(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
+	l1 := Level1{}
+	for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
+		switch l1Field {
+		case "hello":
+			l2Array := []Level2{}
+			for iter.ReadArray() {
+				l2 := Level2{}
+				for l2Field := iter.ReadObject(); l2Field != ""; l2Field = iter.ReadObject() {
+					switch l2Field {
+					case "world":
+						l2.World = iter.ReadString()
+					default:
+						iter.ReportError("bind l2", "unexpected field: " + l2Field)
+					}
+				}
+				l2Array = append(l2Array, l2)
+			}
+			l1.Hello = l2Array
+		default:
+			iter.ReportError("bind l1", "unexpected field: " + l1Field)
+		}
+	}
+	if !reflect.DeepEqual(l1, Level1{
+		Hello: []Level2{
+			{World: "value1"},
+			{World: "value2"},
+		},
+	}) {
+		t.Fatal(l1)
+	}
+}
+
+func Benchmark_jsoniter_nested(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		iter := ParseString(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
+		l1 := Level1{}
+		for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
+			switch l1Field {
+			case "hello":
+				l2Array := make([]Level2, 0, 2)
+				for iter.ReadArray() {
+					l2 := Level2{}
+					for l2Field := iter.ReadObject(); l2Field != ""; l2Field = iter.ReadObject() {
+						switch l2Field {
+						case "world":
+							l2.World = iter.ReadString()
+						default:
+							iter.ReportError("bind l2", "unexpected field: " + l2Field)
+						}
+					}
+					l2Array = append(l2Array, l2)
+				}
+				l1.Hello = l2Array
+			default:
+				iter.ReportError("bind l1", "unexpected field: " + l1Field)
+			}
+		}
+	}
+}
+
+func Benchmark_json_nested(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		l1 := Level1{}
+		json.Unmarshal([]byte(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`), &l1)
+	}
+}

+ 94 - 0
jsoniter_object_test.go

@@ -0,0 +1,94 @@
+package jsoniter
+
+import (
+	"testing"
+	"encoding/json"
+)
+
+func Test_empty_object(t *testing.T) {
+	iter := ParseString(`{}`)
+	field := iter.ReadObject()
+	if field != "" {
+		t.Fatal(field)
+	}
+}
+
+func Test_one_field(t *testing.T) {
+	iter := ParseString(`{"a": "b"}`)
+	field := iter.ReadObject()
+	if field != "a" {
+		t.Fatal(field)
+	}
+	value := iter.ReadString()
+	if value != "b" {
+		t.Fatal(field)
+	}
+	field = iter.ReadObject()
+	if field != "" {
+		t.Fatal(field)
+	}
+}
+
+func Test_two_field(t *testing.T) {
+	iter := ParseString(`{ "a": "b" , "c": "d" }`)
+	field := iter.ReadObject()
+	if field != "a" {
+		t.Fatal(field)
+	}
+	value := iter.ReadString()
+	if value != "b" {
+		t.Fatal(field)
+	}
+	field = iter.ReadObject()
+	if field != "c" {
+		t.Fatal(field)
+	}
+	value = iter.ReadString()
+	if value != "d" {
+		t.Fatal(field)
+	}
+	field = iter.ReadObject()
+	if field != "" {
+		t.Fatal(field)
+	}
+	iter = ParseString(`{"field1": "1", "field2": 2}`)
+	for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
+		switch field {
+		case "field1":
+			iter.ReadString()
+		case "field2":
+			iter.ReadInt64()
+		default:
+			iter.ReportError("bind object", "unexpected field")
+		}
+	}
+}
+
+type TestObj struct {
+	Field1 string
+	Field2 uint64
+}
+
+func Benchmark_jsoniter_object(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		iter := ParseString(`{"field1": "1", "field2": 2}`)
+		obj := TestObj{}
+		for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
+			switch field {
+			case "field1":
+				obj.Field1 = iter.ReadString()
+			case "field2":
+				obj.Field2 = iter.ReadUint64()
+			default:
+				iter.ReportError("bind object", "unexpected field")
+			}
+		}
+	}
+}
+
+func Benchmark_json_object(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		result := TestObj{}
+		json.Unmarshal([]byte(`{"field1": "1", "field2": 2}`), &result)
+	}
+}