Added @keys and @values modifiers

The "@keys" and "@values" modifiers converts an object into an
array of its keys or values respectively.

Take this json for example:

{"first":"Tom","last":"Smith"}

@keys   -> ["first","last"]
@values -> ["Tom","Smith"]

This feature was requested in #161.
This commit is contained in:
tidwall 2021-10-20 16:31:29 -07:00
parent 4fe1916c56
commit 35fa0d71c8
4 changed files with 86 additions and 0 deletions

View File

@ -200,6 +200,8 @@ There are currently the following built-in modifiers:
- `@valid`: Ensure the json document is valid. - `@valid`: Ensure the json document is valid.
- `@flatten`: Flattens an array. - `@flatten`: Flattens an array.
- `@join`: Joins multiple objects into a single object. - `@join`: Joins multiple objects into a single object.
- `@keys`: Returns an array of keys for an object.
- `@values`: Returns an array of values for an object.
### Modifier arguments ### Modifier arguments

View File

@ -236,6 +236,8 @@ There are currently the following built-in modifiers:
- `@valid`: Ensure the json document is valid. - `@valid`: Ensure the json document is valid.
- `@flatten`: Flattens an array. - `@flatten`: Flattens an array.
- `@join`: Joins multiple objects into a single object. - `@join`: Joins multiple objects into a single object.
- `@keys`: Returns an array of keys for an object.
- `@values`: Returns an array of values for an object.
#### Modifier arguments #### Modifier arguments

View File

@ -2565,6 +2565,8 @@ var modifiers = map[string]func(json, arg string) string{
"flatten": modFlatten, "flatten": modFlatten,
"join": modJoin, "join": modJoin,
"valid": modValid, "valid": modValid,
"keys": modKeys,
"values": modValues,
} }
// AddModifier binds a custom modifier command to the GJSON syntax. // AddModifier binds a custom modifier command to the GJSON syntax.
@ -2721,6 +2723,58 @@ func modFlatten(json, arg string) string {
return bytesString(out) return bytesString(out)
} }
// @keys extracts the keys from an object.
// {"first":"Tom","last":"Smith"} -> ["first","last"]
func modKeys(json, arg string) string {
v := Parse(json)
if !v.Exists() {
return "[]"
}
obj := v.IsObject()
var out strings.Builder
out.WriteByte('[')
var i int
v.ForEach(func(key, _ Result) bool {
if i > 0 {
out.WriteByte(',')
}
if obj {
out.WriteString(key.Raw)
} else {
out.WriteString("null")
}
i++
return true
})
out.WriteByte(']')
return out.String()
}
// @values extracts the values from an object.
// {"first":"Tom","last":"Smith"} -> ["Tom","Smith"]
func modValues(json, arg string) string {
v := Parse(json)
if !v.Exists() {
return "[]"
}
if v.IsArray() {
return json
}
var out strings.Builder
out.WriteByte('[')
var i int
v.ForEach(func(_, value Result) bool {
if i > 0 {
out.WriteByte(',')
}
out.WriteString(value.Raw)
i++
return true
})
out.WriteByte(']')
return out.String()
}
// @join multiple objects into a single object. // @join multiple objects into a single object.
// [{"first":"Tom"},{"last":"Smith"}] -> {"first","Tom","last":"Smith"} // [{"first":"Tom"},{"last":"Smith"}] -> {"first","Tom","last":"Smith"}
// The arg can be "true" to specify that duplicate keys should be preserved. // The arg can be "true" to specify that duplicate keys should be preserved.

View File

@ -2207,3 +2207,31 @@ func TestIssue240(t *testing.T) {
parsed = Parse(arrayData) parsed = Parse(arrayData)
assert(t, len(parsed.Get("params.data").Array()) == 1) assert(t, len(parsed.Get("params.data").Array()) == 1)
} }
func TestKeysValuesModifier(t *testing.T) {
var json = `{
"1300014": {
"code": "1300014",
"price": 59.18,
"symbol": "300014",
"update": "2020/04/15 15:59:54",
},
"1300015": {
"code": "1300015",
"price": 43.31,
"symbol": "300015",
"update": "2020/04/15 15:59:54",
}
}`
assert(t, Get(json, `@keys`).String() == `["1300014","1300015"]`)
assert(t, Get(``, `@keys`).String() == `[]`)
assert(t, Get(`"hello"`, `@keys`).String() == `[null]`)
assert(t, Get(`[]`, `@keys`).String() == `[]`)
assert(t, Get(`[1,2,3]`, `@keys`).String() == `[null,null,null]`)
assert(t, Get(json, `@values.#.code`).String() == `["1300014","1300015"]`)
assert(t, Get(``, `@values`).String() == `[]`)
assert(t, Get(`"hello"`, `@values`).String() == `["hello"]`)
assert(t, Get(`[]`, `@values`).String() == `[]`)
assert(t, Get(`[1,2,3]`, `@values`).String() == `[1,2,3]`)
}