ohler55 / ojg

Optimized JSON for Go
MIT License
839 stars 50 forks source link

Does JsonPath supports escaping? #123

Closed paulo-raca closed 1 year ago

paulo-raca commented 1 year ago

I Know that JsonPath syntax is not very strictly defined :sweat:, 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!

ohler55 commented 1 year ago

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.

ohler55 commented 1 year ago

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

paulo-raca commented 1 year ago

Sorry for the slow reply

Not slow at all. Thank you very much! :pray:

ohler55 commented 1 year ago

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.

ohler55 commented 1 year ago

Tests added and ready to merge.

paulo-raca commented 1 year ago

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

paulo-raca commented 1 year ago

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

ohler55 commented 1 year ago

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

ohler55 commented 1 year ago

Released v1.18.2

paulo-raca commented 1 year ago

Thanks you!

paulo-raca commented 1 year ago

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']"

ohler55 commented 1 year ago

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.

paulo-raca commented 1 year ago

Thanks for the pointers!

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