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

Support Keyed and Indexed interfaces in jp.Expr.Modify #156

Closed
twelvelabs opened this issue Jan 14, 2024 · 3 comments
Closed

Support Keyed and Indexed interfaces in jp.Expr.Modify #156

twelvelabs opened this issue Jan 14, 2024 · 3 comments

Comments

@twelvelabs
Copy link
Contributor

Hello!

First off, many thanks for a fantastic library! It's made my life a lot easier and I really appreciate it.

I'm currently using ojg/jp in a CLI tool that (among other things) allows users to modify JSON and YAML files via JSON path expressions. In both cases I'm unmarshalling the files to map[string]any and passing them into jp.Expr.Modify().

One thing I'd like to add is the ability to maintain comments, whitespace, and key ordering when updating YAML files. The YAML lib I'm using supports this when unmarshalling into a yaml.Node, so I've been looking into using that instead of an untyped map.

I noticed the recent addition of the jp.Keyed and jp.Indexed interfaces, and I was thinking about creating a wrapper around yaml.Node that implemented those interfaces. This seems like it would do what I want.

Unfortunately it doesn't look like the jp.Expr.Modify() method supports those new interfaces. That or perhaps I'm invoking it incorrectly 🤷. Here's what I've tried:

package main

import (
	"fmt"
	"os"
	"sort"

	"github.com/k0kubun/pp/v3"
	"github.com/ohler55/ojg/jp"
	"github.com/ohler55/ojg/oj"
)

var _ jp.Keyed = &Node{}

func NewNode(m map[string]any) *Node {
	return &Node{
		data: m,
	}
}

type Node struct {
	data map[string]any
}

func (n *Node) ValueForKey(key string) (any, bool) {
	if value, has := n.data[key]; has {
		return value, true
	}
	return nil, false
}
func (n *Node) SetValueForKey(key string, value any) {
	n.data[key] = value
}
func (n *Node) RemoveValueForKey(key string) {
	delete(n.data, key)
}
func (n *Node) Keys() []string {
	keys := []string{}
	for k := range n.data {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	return keys
}

func main() {
	data := NewNode(map[string]any{
		"a": NewNode(map[string]any{"x": 1, "y": 2, "z": 3}),
		"b": NewNode(map[string]any{"x": 4, "y": 5, "z": 6}),
	})

	fmt.Println("")
	exp := jp.MustParseString("$.*.z")
	fmt.Println("Expression: " + exp.String())

	found := exp.Get(data)
	fmt.Println("Get: " + oj.JSON(found))
	fmt.Println("")

	// fmt.Println("Set:")
	// _ = exp.Set(data, 999)
	// pp.Println(data)
	// fmt.Println("")

	fmt.Println("Modified:")
	modified := exp.MustModify(data, func(element any) (any, bool) {
		return 999, true
	})
	pp.Println(modified)
	fmt.Println("")

	os.Exit(0)
}

Which returns:


Expression: $.*.z
Get: [3,6]

Modified:
&main.Node{
  data: map[string]interface {}{
    "a": &main.Node{
      data: map[string]interface {}{
        "x": 1,
        "y": 2,
        "z": 3,
      },
    },
    "b": &main.Node{
      data: map[string]interface {}{
        "x": 4,
        "y": 5,
        "z": 6,
      },
    },
  },
}

I expected the z keys in the data maps to be set to 999. Works as expected when removing the Node wrappers. Also works when using jp.Expr.Set().

I didn't see any references to Indexed or Keyed in https://github.com/ohler55/ojg/blob/develop/jp/modify.go, so I'm thinking that Modify just does't currently support them. I would love to help out and add that myself, but honestly that code is a bit beyond me 😞. Is there any chance you'd be interested in doing so?

@ohler55
Copy link
Owner

ohler55 commented Jan 15, 2024

I'll have to look at that carefully. It might require another interface to support modifying a keyed or indexed.

@ohler55
Copy link
Owner

ohler55 commented Jun 22, 2024

My apologies that it has taken this long for me to get to this. I've started a branch called modify-keyed-indexed that has the start of the updates. All have been implemented but minimally tested if you want to try it out as I add the tests.

@ohler55
Copy link
Owner

ohler55 commented Jun 23, 2024

Added in release v1.22.1

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