ohler55/ojg

Does JsonPath supports escaping?

paulo-raca opened this issue ยท 13 comments

I Know that JsonPath syntax is not very strictly defined ๐Ÿ˜“, but is it possible to select weird field names (containing ' and \)?

My preliminary tests suggest it doesn't work. Am I doing something wrong?

func TestEscaping(t *testing.T) {
	data := map[string]any{
		`\`: "ok",
	}
	expr, err := jp.ParseString(`$['\\']`)
	assert.NoError(t, err)
	assert.Equal(t, []any{"ok"}, expr.Get(data))
}

Thanks!

Sorry for the slow reply. The block syntax is what you want to use as you figured out. I expect that to work so let me look into it and get it fixed.

https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base is the most comprehensive spec so far for JSONPath.

I can confirm it is a bug. I'll try to get it fixed this weekend.

Sorry for the slow reply

Not slow at all. Thank you very much! ๐Ÿ™

Please try the jsonpath-escape branch. I have to add more tests to get the coverage back up but I believe the issue is fixed.

Tests added and ready to merge.

Awesome, thank you! ๐Ÿ™

I looked briefly at your branch and it looks great

The only thing I'm not sure about is if you would need to check out-of-bound when handling escaped chars in readEscStr(). E.g., \, \x, \x1, \u123

Awesome, thank you! ๐Ÿ™

I looked briefly at your branch and it looks great

The only thing I'm not sure about is if you would need to check out-of-bound when handling escaped chars in readEscStr(). E.g., \, \x, \x1, \u123

I added a few tests to make sure that was covered. It should be in the release I make tonight.

Released v1.18.2

Thanks you!

Hey, @ohler55 :

I have a complimentary question, is there a function to build JsonPaths?

In particular the escaping bit is never trivial :)

What I have right now is this:

// Valid identifiers in `.fieldName` syntax are not clearly defined anywhere,
// so we use a generic/conservative pattern and fallback to ['fieldName'] otherwise
var validNameRegex = regexp.MustCompile("^[a-zA-Z_][a-zA-Z_0-9]*$")

func NewJsonPathString(path ...any) string {
	ret := "$"
	for _, v := range path {
		switch vv := (v).(type) {
		case int, int8, int16, int32, int64:
			// Array index, use `[index]` syntax
			ret = fmt.Sprintf("%s[%d]", ret, vv)
		case string:
			if validNameRegex.Match([]byte(vv)) {
				// Simple field name, `.fieldName` syntax
				ret = fmt.Sprintf("%s.%s", ret, v)
			} else {
				// Complex field name, use `['fieldName']` syntax
				// FIXME, this is terrible
				vv = strings.ReplaceAll(vv, `\`, `\\`)
				vv = strings.ReplaceAll(vv, `'`, `\'`)
				ret = fmt.Sprintf("%s['%s']", ret, vv)
			}
		default:
			panic(fmt.Errorf("Invalid JSON Path token: %v", v))
		}
	}
	return ret
}

Example:

NewJsonPathString("foo_bar56", 19, "๐Ÿ˜€", 1, "5")-> "$.foo_bar56[19]['๐Ÿ˜€'][1]['5']"

This is a completely different issue. Well not really and issue more like a question. Yes there are functions to build JSONPaths. Looks at the Expr functions here: https://pkg.go.dev/github.com/ohler55/ojg/jp

I'll close this issue later today. If you still have questions please start a discussion instead.

Thanks for the pointers!

And sorry for reusing it this issue, I'll open a new one to continue