Jelajahi Sumber

[Testing] More tests including common JSON

Further JSON tests to ensure Node unmarshals it correctly will be needed,
but, so far the JSON tests work in considerable detail.

Somethings to consider:
- Added a way to form a list of "where to go from here", for walking and
  automating walking JSON.
- Expand the concept of nested JSON with varying Array and Map likes.
david 1 tahun lalu
induk
melakukan
7d77d971b6
2 mengubah file dengan 159 tambahan dan 5 penghapusan
  1. 16 5
      node.go
  2. 143 0
      node_test.go

+ 16 - 5
node.go

@@ -242,9 +242,15 @@ func (n *Node) from(A any) {
 		for k, v := range m {
 			switch reflect.TypeOf(v).Kind() {
 			case reflect.Map, reflect.Array, reflect.Slice:
-				// New kid, with name, and recurse
-				kid := n.NewKidWithName(k)
-				kid.from(v)
+				if n.Depth() != 0 || len(m) > 1 {
+					// New kid, with name, and recurse
+					kid := n.NewKidWithName(k)
+					kid.from(v)
+				} else {
+					// This is within the "root" node
+					n.Name(k)
+					n.from(v)
+				}
 			default:
 				// New kid, with name, and data
 				n.NewKidWithNameAndData(k, v)
@@ -257,8 +263,13 @@ func (n *Node) from(A any) {
 		for idx, v := range a {
 			switch reflect.TypeOf(v).Kind() {
 			case reflect.Map, reflect.Array, reflect.Slice:
-				kid := n.NewKidWithName(fmt.Sprintf("%d", idx))
-				kid.from(v)
+				if n.Depth() != 0 || len(a) > 1 {
+					kid := n.NewKidWithName(fmt.Sprintf("%d", idx))
+					kid.from(v)
+				} else {
+					n.Name(fmt.Sprintf("%d", idx))
+					n.from(v)
+				}
 			default:
 				n.NewKidWithNameAndData(fmt.Sprintf("%d", idx), v)
 			}

+ 143 - 0
node_test.go

@@ -1,6 +1,7 @@
 package node_test
 
 import (
+	"encoding/json"
 	"testing"
 
 	"git.red-green.com/david/node"
@@ -134,3 +135,145 @@ func TestNodeTree1(t *testing.T) {
 		t.Logf("kid2-sub2 has invalid name, got '%s' from %#v", n.Kid(1).Kid(1).Name(), n.Kid(1).Kid(1))
 	}
 }
+
+func TestJSON2NodeA1(t *testing.T) {
+	pay := []byte(`
+	{
+		"test": {
+			"kid1": 1,
+			"kid2": 2.5,
+			"kid3": [
+				1,
+				2,
+				3
+			],
+			"kid4": [
+				{
+					"name": "Taco",
+					"price": 3.75
+				},
+				{
+					"name": "Soda",
+					"price": 1.99
+				}
+			]
+		}
+	}
+	`)
+	n := node.NewNode()
+	err := json.Unmarshal(pay, &n)
+	if err != nil {
+		t.Fail()
+		t.Logf("json.Unmarshal: %v", err)
+	}
+	if n.Name() != "test" {
+		if !t.Failed() {
+			t.Fail()
+		}
+		t.Logf("Root node name expected \"test\", but got \"%s\"", n.Name())
+	}
+	if n.Len() != 4 {
+		if !t.Failed() {
+			t.Fail()
+		}
+		t.Logf("Root node should have 4 children, has %d", n.Len())
+	}
+	if n.KidByName("kid1") == nil {
+		if !t.Failed() {
+			t.Fail()
+		}
+		t.Logf("Root node should have a child of \"kid1\", has %d:", n.Len())
+		for _, k := range n.Kids() {
+			t.Logf("\"%s\"", k.Name())
+		}
+	}
+	k1 := n.KidByName("kid1")
+	k3 := n.KidByName("kid3")
+	k4 := n.KidByName("kid4")
+	if k1.Data() != 1.0 { // JSON has Number, no Integer, thus Go marshals it into a Float
+		if !t.Failed() {
+			t.Fail()
+		}
+		t.Logf("Kid1 node should have data of 1, got %#v", k1.Data())
+	}
+	if k3.Len() != 3 {
+		if !t.Failed() {
+			t.Fail()
+		}
+		t.Logf("Kid3 node should have 3 children, has %d", k3.Len())
+	}
+	if k3.KidByName("2").Data() != 3.0 { // JSON has Number, no Integer, thus Go marshals it into a Float
+		if !t.Failed() {
+			t.Fail()
+		}
+		t.Logf("Kid3 sub-node \"2\" should have data of 3, got %#v from %#v", k3.KidByName("2").Data(), k3.KidByName("2"))
+	}
+	if k4.Len() != 2 {
+		if !t.Failed() {
+			t.Fail()
+		}
+		t.Logf("Kid4 node should have 2 children, has %d", k4.Len())
+	}
+	for idx, k := range k4.Kids() {
+		if k.Len() != 2 {
+			if !t.Failed() {
+				t.Fail()
+			}
+			t.Logf("Kid4 sub-node \"%s\" should have 2 children, has %d", k.Name(), k.Len())
+			continue
+		}
+		if k.KidByName("name") == nil {
+			if !t.Failed() {
+				t.Fail()
+			}
+			t.Logf("Kid4 sub-node \"%s\" should have a child of \"name\", has %d:", k.Name(), k.Len())
+			for _, k0 := range k.Kids() {
+				t.Logf("\"%s\"", k0.Name())
+			}
+			continue
+		}
+		if k.KidByName("price") == nil {
+			if !t.Failed() {
+				t.Fail()
+			}
+			t.Logf("Kid4 sub-node \"%s\" should have a child of \"price\", has %d:", k.Name(), k.Len())
+			for _, k0 := range k.Kids() {
+				t.Logf("\"%s\"", k0.Name())
+			}
+			continue
+		}
+		switch idx {
+		case 0: // name="Taco", price=3.75
+			if k.KidByName("name").Data().(string) != "Taco" {
+				if !t.Failed() {
+					t.Fail()
+				}
+				t.Logf("Kid4 sub-node \"%s\" failed name check \"Taco\", got \"%s\"", k.Name(), k.KidByName("name").Data().(string))
+			}
+			if k.KidByName("price").Data() != 3.75 {
+				if !t.Failed() {
+					t.Fail()
+				}
+				t.Logf("Kid4 sub-node \"%s\" failed price check 3.75, got %#v", k.Name(), k.KidByName("price").Data())
+			}
+		case 1: // name="Soda", price=1.99
+			if k.KidByName("name").Data().(string) != "Soda" {
+				if !t.Failed() {
+					t.Fail()
+				}
+				t.Logf("Kid4 sub-node \"%s\" failed name check \"Soda\", got \"%s\"", k.Name(), k.KidByName("name").Data().(string))
+			}
+			if k.KidByName("price").Data() != 1.99 {
+				if !t.Failed() {
+					t.Fail()
+				}
+				t.Logf("Kid4 sub-node \"%s\" failed price check 1.99, got %#v", k.Name(), k.KidByName("price").Data())
+			}
+		default:
+			if !t.Failed() {
+				t.Fail()
+			}
+			t.Logf("Unexpected sub-node for Kid4 \"%s\"", k.Name())
+		}
+	}
+}