Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ast.Node should support Set variant that sets at any depth, creating objects and arrays as needed #730

Open
scr-oath opened this issue Jan 12, 2025 · 2 comments

Comments

@scr-oath
Copy link

scr-oath commented Jan 12, 2025

Some use-cases use a combination of gjson and sjson with paths deeper than just a key to alter incoming json data to be fuzzy with a schema - making it "fit" even if clients are at various levels of "compliance" vs correctness, in the spirit of "be generous in what you receive and strict with what you output".

https://github.com/cloudwego/gjson can be useful for the gjson side, but there is no corresponding sjson side. In addition, I'm curious if the ast framework could be good at holding the entire tree and making updates or additions at any level with a more convenient variant of Set, which could create objects and arrays as needed to make a place for the value to sit.

The current state of the module would seemingly require a client to, for every non-leaf key/value do a get and, if !Exists, set to the appropriate type and call either Set or Add as needed for Object v. Array. - while doable, it seems like something that could be made MUCH more convenient and a "pit of success" for clients to use properly/efficiently.

The ultimate hope would be to be able to combinations of get and set on the root structure, without having to re-parse (even lazily) the entire content over and over again, like is needed for gjson/sjson - if updates are made with sjson and return a new []byte then gjson would have to re-read that on subsequent gets to decide further updates/corrections/etc

@scr-oath
Copy link
Author

Here's a rough draft of something that might work externally, but I'm sure it could be written nicer if internal:

package deepset

import (
	"github.com/bytedance/sonic/ast"
	"slices"
)

// GetByPathDeep load given path on demands,
// which only ensure nodes before this path got parsed.
//
// Note, the api expects the json is well-formed at least,
// otherwise it may return unexpected result.
func GetByPathDeep(self *ast.Node, path ...interface{}) *ast.Node {
	var s = self
	for _, p := range path {
		switch p := p.(type) {
		case int:
			if !s.Exists() {
				*s = ast.NewArray(slices.Repeat([]ast.Node{ast.NewNull()}, p+1))
			} else {
				if ln, err := s.Len(); err == nil && ln <= p {
					for range p + 1 - ln {
						if err = s.Add(ast.NewNull()); err != nil {
							break
						}
					}
				}
			}
			s = s.Index(p)
		case string:
			if !s.Exists() {
				*s = ast.NewObject([]ast.Pair{ast.NewPair(p, ast.Node{})})
			} else {
				if v := s.Get(p); !v.Exists() {
					_, _ = s.Set(p, ast.Node{})
				}
			}
			s = s.Get(p)
		default:
			panic("path must be either int or string")
		}
	}
	return s
}

func SetByPath(self *ast.Node, v ast.Node, path ...any) {
	*GetByPathDeep(self, path...) = v
}

@AsterDY
Copy link
Collaborator

AsterDY commented Jan 13, 2025

ok,we will look forward it. But GetByPath shouldn't change json data since its syntax is just reading. Maybe we can add SetByPath method

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants