diff --git a/jp.go b/jp.go index 80a9f07..5025781 100644 --- a/jp.go +++ b/jp.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "fmt" + "io" "io/ioutil" "os" @@ -43,6 +44,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 JSON).", + }, cli.BoolFlag{ Name: "ast", Usage: "Only print the AST of the parsed expression. Do not rely on this output, only useful for debugging purposes.", @@ -106,7 +111,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")) @@ -118,36 +122,44 @@ 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 { - var toJSON []byte - if c.Bool("compact") { - toJSON, err = jsoncolor.Marshal(result) + 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()) + } + return errMsg("Error evaluating JMESPath expression: %s", err) + } + converted, isString := result.(string) + if c.Bool("unquoted") && isString { + os.Stdout.WriteString(converted) } else { - toJSON, err = jsoncolor.MarshalIndent(result, "", " ") + var toJSON []byte + if c.Bool("compact") { + toJSON, err = json.Marshal(result) + } else { + toJSON, err = json.MarshalIndent(result, "", " ") + } + if err != nil { + errMsg("Error marshalling result to JSON: %s\n", err) + return 3 + } + os.Stdout.Write(toJSON) } - if err != nil { - errMsg("Error marshalling result to JSON: %s\n", err) - return 3 + 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..8065096 100644 --- a/test/cases/search.bats +++ b/test/cases/search.bats @@ -69,3 +69,14 @@ ASTField { [ "$status" -eq 0 ] [ "$output" == "12345" ] } + +@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\"' ] +} \ No newline at end of file