├── README.md ├── jsonlines.go └── jsonlines_test.go /README.md: -------------------------------------------------------------------------------- 1 | Simple JSON Lines encoding/decoding package for Go. 2 | 3 | Using: 4 | ```go 5 | package main 6 | 7 | import ( 8 | "jsonlines" 9 | "strings" 10 | "fmt" 11 | ) 12 | 13 | func main() { 14 | data := `{"Name": "Bob", "Age": 20, "Cars": ["Ford", "Dodge"]} 15 | {"Name": "John", "Age": 30, "Cars": ["BMW", "Toyota"]}` 16 | type Person struct { 17 | Name string 18 | Age int64 19 | Cars []string 20 | } 21 | people := []Person{} 22 | err := jsonlines.Decode(strings.NewReader(data), &people) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | fmt.Println(people) 27 | } 28 | ``` 29 | Output: 30 | ``` 31 | [{Bob 20 [Ford Dodge]} {John 30 [BMW Toyota]}] 32 | ``` 33 | -------------------------------------------------------------------------------- /jsonlines.go: -------------------------------------------------------------------------------- 1 | package jsonlines 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "io" 7 | "bufio" 8 | "encoding/json" 9 | ) 10 | 11 | func getOriginalSlice(ptrToSlice interface{}) (slice reflect.Value, err error) { 12 | ptr2sl := reflect.TypeOf(ptrToSlice) 13 | if ptr2sl.Kind() != reflect.Ptr { 14 | return reflect.ValueOf(nil), fmt.Errorf("expected pointer to slice, got %s", ptr2sl.Kind()) 15 | } 16 | 17 | originalSlice := reflect.Indirect(reflect.ValueOf(ptrToSlice)) 18 | sliceType := originalSlice.Type() 19 | if sliceType.Kind() != reflect.Slice { 20 | return reflect.ValueOf(nil), fmt.Errorf("expected pointer to slice, got pointer to %s", sliceType.Kind()) 21 | } 22 | return originalSlice, nil 23 | } 24 | 25 | // Decode reads the next JSON Lines-encoded value that reads 26 | // from r and stores it in the slice pointed to by ptrToSlice. 27 | func Decode(r io.Reader, ptrToSlice interface{}) error { 28 | originalSlice, err := getOriginalSlice(ptrToSlice) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | slElem := originalSlice.Type().Elem() 34 | //originalSlice := reflect.Indirect(reflect.ValueOf(ptrToSlice)) 35 | scanner := bufio.NewScanner(r) 36 | for scanner.Scan() { 37 | //create new object 38 | newObj := reflect.New(slElem).Interface() 39 | item := scanner.Bytes() 40 | err := json.Unmarshal(item, newObj) 41 | if err != nil { 42 | return err 43 | } 44 | ptrToNewObj := reflect.Indirect(reflect.ValueOf(newObj)) 45 | originalSlice.Set(reflect.Append(originalSlice, ptrToNewObj)) 46 | } 47 | if err := scanner.Err(); err != nil { 48 | return err 49 | } 50 | return nil 51 | } 52 | 53 | 54 | // Encode writes the JSON Lines encoding of ptrToSlice to the w stream 55 | func Encode(w io.Writer, ptrToSlice interface{}) error { 56 | originalSlice, err := getOriginalSlice(ptrToSlice) 57 | if err != nil { 58 | return err 59 | } 60 | 61 | enc := json.NewEncoder(w) 62 | for i := 0; i < originalSlice.Len(); i++ { 63 | elem := originalSlice.Index(i).Interface() 64 | err = enc.Encode(elem) 65 | if err != nil { 66 | return err 67 | } 68 | } 69 | return nil 70 | } -------------------------------------------------------------------------------- /jsonlines_test.go: -------------------------------------------------------------------------------- 1 | package jsonlines 2 | 3 | import ( 4 | "testing" 5 | "strings" 6 | "bytes" 7 | ) 8 | 9 | const data = `{"Name":"Paul","Age":20} 10 | {"Name":"John","Age":30}` 11 | 12 | type Person struct { 13 | Name string 14 | Age int64 15 | } 16 | 17 | func TestDecode(t *testing.T) { 18 | people := []Person{} 19 | err := Decode(strings.NewReader(data), &people) 20 | if err != nil { 21 | t.Fatalf("Decode returns error: ", err) 22 | } 23 | if len(people) != 2 { 24 | t.Fatalf("Expected 2 objects in slice, got %s", len(people)) 25 | } 26 | if people[0].Name != "Paul" { 27 | t.Fatalf("Unexpected value in first object Name field: ", people[0].Name) 28 | } 29 | } 30 | 31 | func TestDecodeWrongTypes(t *testing.T) { 32 | people := map[Person]int{} 33 | err := Decode(strings.NewReader(data), &people) 34 | if err == nil { 35 | t.Fatal("Decode doesnt returns error") 36 | } 37 | if err.Error() != "expected pointer to slice, got pointer to map" { 38 | t.Fatalf("Decode return wrong error: ", err) 39 | } 40 | } 41 | 42 | func TestEncode(t *testing.T) { 43 | people := []*Person{ 44 | &Person{Name: "Paul", Age: 20}, 45 | &Person{Name: "John", Age: 30}, 46 | } 47 | 48 | var buf bytes.Buffer 49 | err := Encode(&buf, &people) 50 | if err != nil { 51 | t.Fatal("Encode returns error: ", err) 52 | } 53 | if !strings.Contains(buf.String(), data) { 54 | t.Fatalf("Encode return wrong data, expected: %s, got: %s", 55 | data, buf.String()) 56 | } 57 | } --------------------------------------------------------------------------------