尝试获取以下代码以跳过JSON数据对象流中的解析错误 noise 。基本上我希望它跳过ERROR: ...
行并继续下一个可解析的记录。
json.Decoder
有一组有限的methods - 所以它不清楚如何向前移动解码器的索引(比如一次一个字节)以超越噪声
io.Reader
有方法可以跳过来说明行的结尾(或者至少尝试跳过一个字符) - 但这样的操作不会(理解)影响json.Decoder
&#39寻求国家。
有干净的方法吗?
https://play.golang.org/p/riIDh9g1Rx
package main
import (
"encoding/json"
"fmt"
"strings"
"time"
)
type event struct {
T time.Time
Desc string
}
var jsonStream = `
{"T":"2017-11-02T16:00:00-04:00","Desc":"window opened"}
{"T":"2017-11-02T16:30:00-04:00","Desc":"window closed"}
{"T":"2017-11-02T16:41:34-04:00","Desc":"front door opened"}
ERROR: retrieving event 1234
{"T":"2017-11-02T16:41:40-04:00","Desc":"front door closed"}
`
func main() {
jsonReader := strings.NewReader(jsonStream)
decodeStream := json.NewDecoder(jsonReader)
i := 0
for decodeStream.More() {
i++
var ev event
if err := decodeStream.Decode(&ev); err != nil {
fmt.Println("parse error: %s", err)
break
}
fmt.Printf("%3d: %+v\n", i, ev)
}
}
得到:
1: {T:2017-11-02 16:00:00 -0400 -0400 Desc:window opened}
2: {T:2017-11-02 16:30:00 -0400 -0400 Desc:window closed}
3: {T:2017-11-02 16:41:34 -0400 -0400 Desc:front door opened}
parse error: %s invalid character 'E' looking for beginning of value
想:
1: {T:2017-11-02 16:00:00 -0400 -0400 Desc:window opened}
2: {T:2017-11-02 16:30:00 -0400 -0400 Desc:window closed}
3: {T:2017-11-02 16:41:34 -0400 -0400 Desc:front door opened}
4: {T:2017-11-02 16:41:40 -0400 -0400 Desc:front door closed}
答案 0 :(得分:5)
我认为"正确"方法这样做,因为流本身是无效的JSON (即使没有错误,JSON文档必须有一个根条目,这是一系列无效的根对象),要预先解析为单独的,有效的JSON文档,并单独解组。使用例如逐行读取流bufio.Scanner
,丢弃非JSON行,Unmarshal
其他行正常。
请参阅此处的工作示例:https://play.golang.org/p/DZrAVmzwr-
答案 1 :(得分:1)
虽然不是很干净,但您可以使用JSON解码器的Buffered
方法来访问底层读取器,它仍应指向导致错误的字节,并将其包装在缓冲读取器中必要时。然后,您可以读取单个字节,直到遇到有效的JSON起始对象字节{
并且未读取该字节(在任何实现中至少1个字节可以未读取)以将字节推回到缓冲流上。
Playground link for code below
...
decodeLoop:
for decodeStream.More() {
i++
var ev event
if err := decodeStream.Decode(&ev); err != nil {
r := decodeStream.Buffered()
br, ok := r.(*bufio.Reader)
if !ok {
br = bufio.NewReader(r)
}
for {
b, err := br.ReadByte()
if err != nil {
// Whether EOF or not, there's nothing left to do except
// break the loop to trigger the "parse error" statement.
break
}
// A (potentially) valid JSON object was found;
// create a new decoder associated with the same decodeStream var
// using the new buffered reader and continue decoding.
if b == '{' {
br.UnreadByte()
decodeStream = json.NewDecoder(br)
continue decodeLoop
}
}
fmt.Println("parse error: %s", err)
break
}
...
然而,this is not bulletproof as-is。
恕我直言,处理此问题的正确方法要求您收到一个JSON对象的JSON数组,允许您通过提供event
方法来处理代表UnmarshalJSON
的每个JSON对象的手动标记化使用*event
方法接收器,但是如果你不能得到它,那么这并不重要,并且你需要修改提供的解决方案以使其在必要时工作,假设这是可能的。一种可能的补救方法是设置一个标志,并在检测到有效的JSON对象时取消设置:
objectDetected := false
i := 0
decodeLoop:
...
if b == '{' {
// If we already encountered an object and found ourselves here again,
// it's not really a valid JSON object.
if objectDetected {
break
}
objectDetected = true
br.UnreadByte()
...
fmt.Printf("%3d: %+v\n", i, ev)
objectDetected = false
} // decode loop end
}