Skip to content

JSON errors: line numbers #24

@joonas-fi

Description

@joonas-fi

Playground code:

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"strings"
)

// (ab)using the fact that the JSON decoder has accumulated over all it's read plus it's working on the new buffer
// it's about to receive now.
type lineCounter struct {
	committedLines int
	committedBytes int64

	uncommitted      []byte
	uncommittedLines int
}

func (l *lineCounter) LineNumberFromOffset(offset int64) int {
	if offset < l.committedBytes {
		panic("offset < l.committedBytes")
	}

	intoUncommitted := offset - l.committedBytes

	linesIntoUncommitted := countNewlines(l.uncommitted[:intoUncommitted])

	return l.committedLines + linesIntoUncommitted
}

var _ io.Writer = (*lineCounter)(nil)

func (l *lineCounter) Write(buf []byte) (int, error) {
	// the fact that `Write()` was called means that the previous uncommitted buffer was consumed fully. "commit" now.
	l.committedBytes += int64(len(l.uncommitted))
	l.committedLines += l.uncommittedLines

	l.uncommittedLines = countNewlines(buf)
	l.uncommitted = buf

	return len(buf), nil
}

func main() {
	lc := &lineCounter{}

	jd := json.NewDecoder(io.TeeReader(strings.NewReader(`{


	"Foo": "bar"}`), lc))
	err := jd.Decode(&struct{ Foo struct{} }{})
	if err != nil {
		fmt.Printf("err=%v pos=%d\n", err, lc.LineNumberFromOffset(jd.InputOffset()))
	}

	fmt.Println("Hello, 世界")
}

func countNewlines(buf []byte) int {
	newlines := 0

	for _, ch := range buf {
		if ch == '\n' {
			newlines++
		}
	}

	return newlines
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions