tidwall / gjson

Get JSON values quickly - JSON parser for Go
MIT License
13.95k stars 841 forks source link

1.14.2: Breaking change: gjson.GetManyBytes does not respect custom modifiers #290

Closed janisz closed 1 year ago

janisz commented 1 year ago

Consider following tests. It's passing on 1.14.1 but fails on 1.14.2 with

# 1.14.2
Expected :{"_":[["-"]]}
Actual   :{"_":[[]]}
package gjson

import (
    "testing"

    "github.com/stretchr/testify/assert"
    "github.com/tidwall/gjson"
)

func TestGetManyBytes(t *testing.T) {
    gjson.AddModifier("boolReplace", BoolReplaceModifier())
    rawJson := `{"x":[{"y":[{"z":false}]}]}`
    jsonPathExpr := `{x.#.y.#.z.@boolReplace:{"true":"X","false":"-"}}`
    results := gjson.GetManyBytes([]byte(rawJson), jsonPathExpr)
    assert.Len(t, results, 1)
    actua := results[0].Raw
    expected := `{"_":[["-"]]}`
    assert.Equal(t, expected, actua)
}

// BoolReplaceModifier provides the @boolReplace modifier for gjson which replaces "true" and "false" with the given
// strings, the configuration happens via a json object.
// The JSON object has the following structure:
// {"true": "string-you-want-to-replace-with", "false": "string-you-want-to-replace-with"}
func BoolReplaceModifier() CustomModifier {
    return func(j, arg string) string {
        opts := defaultReplaceBoolOptions()
        if arg != "" {
            gjson.Parse(arg).ForEach(func(key, value gjson.Result) bool {
                switch key.Bool() {
                case true:
                    opts.TrueReplace = value.String()
                case false:
                    opts.FalseReplace = value.String()
                }
                return true
            })
        }

        res := gjson.Parse(j)
        if res.Type == gjson.True {
            j = strings.ReplaceAll(j, "true", opts.TrueReplace)
        } else if res.Type == gjson.False {
            j = strings.ReplaceAll(j, "false", opts.FalseReplace)
        }
        bytes, _ := json.Marshal(j)
        return string(bytes)
    }
}

Refs: https://github.com/stackrox/stackrox/pull/2654 Introduced in: https://github.com/tidwall/gjson/commit/475b4036c39569593ccabc9770b1188ffd2193e0

janisz commented 1 year ago

Here is breaking test for built in modifier

func TestGetManyBytes(t *testing.T) {
    rawJson := `{"x":[{"y":[{"z":{"b":1, "c": 2, "a": 3}}]}]}`
    jsonPathExpr := `x.#.y.#.z.@pretty:{"sortKeys":true}`
    results := gjson.GetManyBytes([]byte(rawJson), jsonPathExpr)
    assert.Len(t, results, 1)
    actua := results[0].Raw
    expected := `[[{
  "a": 3,
  "b": 1,
  "c": 2
}
]]`
    assert.Equal(t, expected, actua)
}