Skip to content

Commit

Permalink
chore: handle newline delimited JSON array
Browse files Browse the repository at this point in the history
  • Loading branch information
tuan78 committed Aug 29, 2024
1 parent d62db57 commit 84e99a0
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 6 deletions.
41 changes: 36 additions & 5 deletions json_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ package jsonconv
import (
"encoding/json"
"io"
"reflect"
"strings"
)

// A JsonReader reads and decodes JSON values from an input stream.
type JsonReader struct {
reader io.Reader
reader io.ReadSeeker
}

// NewJsonReader returns a new JsonReader that reads from r.
func NewJsonReader(r io.Reader) *JsonReader {
func NewJsonReader(r io.ReadSeeker) *JsonReader {
return &JsonReader{
reader: r,
}
Expand All @@ -21,11 +23,40 @@ func NewJsonReader(r io.Reader) *JsonReader {
// input and stores it in the value pointed to by v.
func (r *JsonReader) Read(v interface{}) error {
decoder := json.NewDecoder(r.reader)
for decoder.More() {
err := decoder.Decode(v)
if err != nil {
err := decoder.Decode(v)
if err != nil {
if !strings.Contains(err.Error(), "cannot unmarshal object into Go value of type") {
return err
}

// The JSON data is a valid JSON array. However, we will try to decode it line by line.
// Reset reader and decoder.
r.reader.Seek(0, io.SeekStart)
decoder = json.NewDecoder(r.reader)

// Check if v is a pointer to a slice or an array. If not, return an error.
refval := reflect.ValueOf(v)
if refval.Kind() == reflect.Pointer {
refval = refval.Elem()
}
if refval.Kind() != reflect.Slice && refval.Kind() != reflect.Array {
return err
}

// Decode JSON array into v.
for {
obj := reflect.New(refval.Type().Elem())
objInterface := obj.Interface()
err := decoder.Decode(objInterface)
if err != nil {
if err == io.EOF {
reflect.ValueOf(v).Elem().Set(refval)
return nil
}
return err
}
refval.Set(reflect.Append(refval, reflect.ValueOf(objInterface).Elem()))
}
}
return nil
}
41 changes: 40 additions & 1 deletion json_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,46 @@ func TestJsonReader_JsonArray(t *testing.T) {
}

// Check
if len(arr) == 0 {
if len(arr) != 3 {
t.Fatalf("failed to read json array")
}
if arr[0]["id"] != "ce06f5b1-5721-42c0-91e1-9f72a09c250a" ||
arr[0]["user"] != "Tuấn" ||
arr[0]["score"] != "1.5" ||
arr[0]["is active"] != "true" {
t.Fatalf("failed to read json array")
}
if arr[1]["id"] != "b042ab5c-ca73-4460-b739-96410ea9d3a6" ||
arr[1]["user"] != "Jon Doe" ||
arr[1]["score"] != "-100" ||
arr[1]["is active"] != "false" {
t.Fatalf("failed to read json array")
}
if arr[2]["id"] != "4e01b638-44e5-4079-8043-baabbff21cc8" ||
arr[2]["user"] != "高橋" ||
arr[2]["score"] != "100000000000000000000000" ||
arr[2]["is active"] != "true" {
t.Fatalf("failed to read json array")
}
}

func TestJsonReader_JsonArray_NewlineDelimited(t *testing.T) {
// Prepare
raw := `
{"id": "ce06f5b1-5721-42c0-91e1-9f72a09c250a","user": "Tuấn","score": "1.5","is active": "true"}
{"id": "b042ab5c-ca73-4460-b739-96410ea9d3a6","user": "Jon Doe","score": "-100","is active": "false"}
{"id": "4e01b638-44e5-4079-8043-baabbff21cc8","user": "高橋","score": "100000000000000000000000","is active": "true"}`
arr := make(JsonArray, 0)
re := NewJsonReader(strings.NewReader(raw))

// Process
err := re.Read(&arr)
if err != nil {
t.Fatalf("failed to read file %v", err)
}

// Check
if len(arr) != 3 {
t.Fatalf("failed to read json array")
}
if arr[0]["id"] != "ce06f5b1-5721-42c0-91e1-9f72a09c250a" ||
Expand Down

0 comments on commit 84e99a0

Please sign in to comment.