Skip to content

Commit 87fecce

Browse files
author
James Haggerty
committed
Add line number to JSON parse error
1 parent e287c64 commit 87fecce

File tree

1 file changed

+61
-4
lines changed

1 file changed

+61
-4
lines changed

jp.go

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"encoding/json"
55
"fmt"
6+
"io"
67
"io/ioutil"
78
"os"
89

@@ -83,19 +84,27 @@ func runMain(c *cli.Context) int {
8384
return 0
8485
}
8586
var input interface{}
86-
var jsonParser *json.Decoder
87+
var inputStream io.Reader
8788
if c.String("filename") != "" {
8889
f, err := os.Open(c.String("filename"))
8990
if err != nil {
9091
return errMsg("Error opening input file: %s", err)
9192
}
92-
jsonParser = json.NewDecoder(f)
93+
inputStream = f
9394

9495
} else {
95-
jsonParser = json.NewDecoder(os.Stdin)
96+
inputStream = os.Stdin
9697
}
98+
newlineNumberReader := NewLineNumberReader(inputStream)
99+
jsonParser := json.NewDecoder(newlineNumberReader)
97100
if err := jsonParser.Decode(&input); err != nil {
98-
errMsg("Error parsing input json: %s\n", err)
101+
if syntaxError, ok := err.(*json.SyntaxError); ok {
102+
line, char := newlineNumberReader.ConvertOffset(syntaxError.Offset)
103+
errMsg("Error parsing input json: %s (line: %d, char: %d)\n",
104+
syntaxError, line, char)
105+
} else {
106+
errMsg("Error parsing input json: %s", err)
107+
}
99108
return 2
100109
}
101110
result, err := jmespath.Search(expression, input)
@@ -121,3 +130,51 @@ func runMain(c *cli.Context) int {
121130
os.Stdout.WriteString("\n")
122131
return 0
123132
}
133+
134+
type LineNumberReader struct {
135+
actualReader io.Reader
136+
newlinePositions []int64
137+
bytesRead int64
138+
}
139+
140+
func NewLineNumberReader(actualReader io.Reader) *LineNumberReader {
141+
return &LineNumberReader{
142+
actualReader: actualReader,
143+
}
144+
}
145+
146+
func (lnr *LineNumberReader) Read(p []byte) (n int, err error) {
147+
n, err = lnr.actualReader.Read(p)
148+
149+
if err != nil || n == 0 {
150+
return
151+
}
152+
153+
for i, v := range p {
154+
if i >= n {
155+
return
156+
}
157+
158+
if v == '\n' {
159+
lnr.newlinePositions = append(lnr.newlinePositions, lnr.bytesRead+int64(i)+1)
160+
}
161+
}
162+
163+
lnr.bytesRead = lnr.bytesRead + int64(n)
164+
return
165+
}
166+
167+
func (lnr *LineNumberReader) ConvertOffset(offset int64) (linePos int, charPos int64) {
168+
lastNewlinePos := int64(0)
169+
lastLineNumber := 0
170+
for lineNumber, newlinePos := range lnr.newlinePositions {
171+
if newlinePos >= offset {
172+
break
173+
}
174+
175+
lastNewlinePos = newlinePos
176+
lastLineNumber = lineNumber + 1
177+
}
178+
179+
return lastLineNumber + 1, offset - lastNewlinePos
180+
}

0 commit comments

Comments
 (0)