From 5f9bf3866a2ec927cc3821ef136e91ce9c7fdfcc Mon Sep 17 00:00:00 2001 From: James Haggerty Date: Wed, 9 Aug 2017 10:42:12 +0000 Subject: [PATCH] Allow processing of a stream of separate JSON elements See #11 --- jp.go | 56 +++++++++++++++++++++++++----------------- test/cases/search.bats | 11 +++++++++ 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/jp.go b/jp.go index 9af9124..7c93a02 100644 --- a/jp.go +++ b/jp.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "fmt" + "io" "io/ioutil" "os" @@ -33,6 +34,10 @@ func main() { Usage: "If the final result is a string, it will be printed without quotes.", EnvVar: "JP_UNQUOTED", }, + cli.BoolFlag{ + Name: "stream, s", + Usage: "Parse JSON elements until the input stream is exhausted (rather than just the first).", + }, cli.BoolFlag{ Name: "ast", Usage: "Only print the AST of the parsed expression. Do not rely on this output, only useful for debugging purposes.", @@ -82,7 +87,6 @@ func runMain(c *cli.Context) int { fmt.Printf("%s\n", parsed) return 0 } - var input interface{} var jsonParser *json.Decoder if c.String("filename") != "" { f, err := os.Open(c.String("filename")) @@ -94,30 +98,38 @@ func runMain(c *cli.Context) int { } else { jsonParser = json.NewDecoder(os.Stdin) } - if err := jsonParser.Decode(&input); err != nil { - errMsg("Error parsing input json: %s\n", err) - return 2 - } - result, err := jmespath.Search(expression, input) - if err != nil { - if syntaxError, ok := err.(jmespath.SyntaxError); ok { - return errMsg("%s\n%s\n", - syntaxError, - syntaxError.HighlightLocation()) + for { + var input interface{} + if err := jsonParser.Decode(&input); err == io.EOF { + break + } else if err != nil { + errMsg("Error parsing input json: %s\n", err) + return 2 } - return errMsg("Error evaluating JMESPath expression: %s", err) - } - converted, isString := result.(string) - if c.Bool("unquoted") && isString { - os.Stdout.WriteString(converted) - } else { - toJSON, err := json.MarshalIndent(result, "", " ") + result, err := jmespath.Search(expression, input) if err != nil { - errMsg("Error marshalling result to JSON: %s\n", err) - return 3 + if syntaxError, ok := err.(jmespath.SyntaxError); ok { + return errMsg("%s\n%s\n", + syntaxError, + syntaxError.HighlightLocation()) + } + return errMsg("Error evaluating JMESPath expression: %s", err) + } + converted, isString := result.(string) + if c.Bool("unquoted") && isString { + os.Stdout.WriteString(converted) + } else { + toJSON, err := json.MarshalIndent(result, "", " ") + if err != nil { + errMsg("Error marshalling result to JSON: %s\n", err) + return 3 + } + os.Stdout.Write(toJSON) + } + os.Stdout.WriteString("\n") + if !c.Bool("stream") { + break } - os.Stdout.Write(toJSON) } - os.Stdout.WriteString("\n") return 0 } diff --git a/test/cases/search.bats b/test/cases/search.bats index 5285185..db9ad65 100644 --- a/test/cases/search.bats +++ b/test/cases/search.bats @@ -14,6 +14,17 @@ [ "$output" == "\"bar\"" ] } +@test "Ignores subsequent data" { + output=$(echo '{"foo": "bar"}blah' | ./jp foo) + [ "$output" == "\"bar\"" ] +} + +@test "Processes subsequent data in stream mode" { + output=$(echo '{"foo": "bar"}{"foo": "x"}' | ./jp -s foo) + echo "$output" + [ "$output" == $'\"bar\"\n\"x\"' ] +} + @test "Can search subexpr expression" { output=$(echo '{"foo": {"bar": "baz"}}' | ./jp foo.bar) [ "$output" == "\"baz\"" ]