gabs icon indicating copy to clipboard operation
gabs copied to clipboard

add new json item not sync in memory

Open perrynzhou opened this issue 2 years ago • 2 comments

  • plateform
mac os
  • code
func main() {
	b,_ := ioutil.ReadFile("./test_cluster.conf")
	jsoner,_ := gabs.ParseJSON(b)
	key :="cluster/test/demo"
	tmpMap := map[string]string{"aaa":"bbb"}
	keys_args  := strings.Split(key,"/")
	jsoner.Set(tmpMap,keys_args...)
	// if add that: jsoner,_ = gabs.ParseJSON(jsoner.Bytes()),i can search data
	ioutil.WriteFile("./temp.json",jsoner.BytesIndent(" "," "),os.ModePerm)
	c :=jsoner.Search(keys_args...)
	if c== nil {
		log.Println("c is nil")
	}else {
                //c.ChildrenMap is empty
		log.Println("c is not nil:",c.ChildrenMap())
		for k,v :=range c.ChildrenMap() {
			log.Printf("%v=%v",k,v)
		}
	}
}

perrynzhou avatar Oct 22 '21 11:10 perrynzhou

@perrynzhou Thank you for opening this issue and sorry for the late reply! Unfortunately, there isn't enough information in your report to investigate the problem. Would you mind providing the contents of test_cluster.conf and the expected outcome? I'm guessing you're running into something similar to #108, but not sure.

mihaitodor avatar Nov 24 '21 01:11 mihaitodor

I believe what he's reporting here is something I ran into during testing attempting to set a map[string]string value:

Code

package main

import (
	"fmt"

	"github.com/Jeffail/gabs/v2"
)

func main() {
	c1, _ := gabs.ParseJSON([]byte(`{"a":"a"}`))
	c2, _ := gabs.ParseJSON([]byte(`{"a":"a"}`))
	c1.Set("test", "b", "c")                     // can retrieve fine
	c2.Set(map[string]string{"c": "test2"}, "b") // won't be able to retrieve

	fmt.Println("c1->b.c", c1.S("b", "c"))
	fmt.Println("c2->b.c", c2.S("b", "c"))

	c1j, _ := c1.MarshalJSON()
	fmt.Printf("c1 .MarshalJSON():\n%s\n", string(c1j))

	c2j, _ := c2.MarshalJSON()
	fmt.Printf("c2 .MarshalJSON():\n%s\n", string(c2j))
}

Output

c1->b.c null
c2->b.c null
c1->d.e "test2"
c2->d.e null
c1 .MarshalJSON():
{"a":"a","d":{"e":"test2"}}
c2 .MarshalJSON():
{"a":"a","d":{"e":"test2"}}

The reason for this is that on gabs.go:322 the hierarchy will bottom out and whatever value you are adding will be put into the map[string]...interface{} hierarchy as-is. So, in this case, it inserts the map just fine, but, when S("b","c") is called, the hierarchy loop will reach b, and, instead of the expected map[string]interface{} or []interface{}, which it knows how to traverse, it will find something else and return immediately.

This is confusing, since the result will still marshal correctly, since json.Marshal will transparently navigate the nested hierarchy, marshalling what it finds.

Workaround

The quick fix is just to use map[string]any/ map[string]interface{} which will work fine, so long as any deeper hierarchy is also map[string]any:

c2, _ = gabs.ParseJSON([]byte(`{"a":"a"}`))
c2.Set(map[string]any{"c": "test2"}, "b") // works again!
fmt.Println("c2->b.c", c2.S("b", "c"))

// ouput: c2->b.c "test2"

Fix

The long-term "fix" is either:

  • Document that adding anything with deeper nesting is unpredictable and may cause problems on retrieval, or
  • Reflect on the incoming value on set and, if the value is not a scalar, make a container at that location by either json.Marshal ->gabs.ParseJson -> Data() or ripping a page from the mapstructure book, and just crawling the value. You could also add a new method that explicitly does this.

This means that you'll lose any ability you had before to call S/Search/Path and get back something you can type assert and call methods on, but since this is a package for "dealing with dynamic or unknown JSON structures in Go", and there isn't any documentation that I can find that explicitly mentions that, you could write that off as an "unsupported use-case" anyway. 🤷

flowchartsman avatar Mar 23 '23 16:03 flowchartsman