github.com/tobgu/qframe@v0.4.0/internal/io/json.go (about) 1 package io 2 3 import ( 4 "encoding/json" 5 "github.com/tobgu/qframe/qerrors" 6 "io" 7 ) 8 9 type JSONRecords []map[string]interface{} 10 11 type JSONColumns map[string]json.RawMessage 12 13 func fillInts(col []int, records JSONRecords, colName string) error { 14 for i := range col { 15 record := records[i] 16 value, ok := record[colName] 17 if !ok { 18 return qerrors.New("fillInts", "missing value for column %s, row %d", colName, i) 19 } 20 21 intValue, ok := value.(int) 22 if !ok { 23 return qerrors.New("fillInts", "wrong type for column %s, row %d, expected int", colName, i) 24 } 25 col[i] = intValue 26 } 27 28 return nil 29 } 30 31 func fillFloats(col []float64, records JSONRecords, colName string) error { 32 for i := range col { 33 record := records[i] 34 value, ok := record[colName] 35 if !ok { 36 return qerrors.New("fillFloats", "missing value for column %s, row %d", colName, i) 37 } 38 39 floatValue, ok := value.(float64) 40 if !ok { 41 return qerrors.New("fillFloats", "wrong type for column %s, row %d, expected float", colName, i) 42 } 43 col[i] = floatValue 44 } 45 46 return nil 47 } 48 49 func fillBools(col []bool, records JSONRecords, colName string) error { 50 for i := range col { 51 record := records[i] 52 value, ok := record[colName] 53 if !ok { 54 return qerrors.New("fillBools", "wrong type for column %s, row %d", colName, i) 55 } 56 57 boolValue, ok := value.(bool) 58 if !ok { 59 return qerrors.New("fillBools", "wrong type for column %s, row %d, expected bool", colName, i) 60 } 61 col[i] = boolValue 62 } 63 64 return nil 65 } 66 67 func fillStrings(col []*string, records JSONRecords, colName string) error { 68 for i := range col { 69 record := records[i] 70 value, ok := record[colName] 71 if !ok { 72 return qerrors.New("fillStrings", "wrong type for column %s, row %d", colName, i) 73 } 74 75 switch t := value.(type) { 76 case string: 77 col[i] = &t 78 case nil: 79 col[i] = nil 80 default: 81 return qerrors.New("fillStrings", "wrong type for column %s, row %d, expected int", colName, i) 82 } 83 } 84 85 return nil 86 } 87 88 func jsonRecordsToData(records JSONRecords) (map[string]interface{}, error) { 89 result := map[string]interface{}{} 90 if len(records) == 0 { 91 return result, nil 92 } 93 94 r0 := records[0] 95 for colName, value := range r0 { 96 switch t := value.(type) { 97 case int: 98 col := make([]int, len(records)) 99 if err := fillInts(col, records, colName); err != nil { 100 return nil, err 101 } 102 result[colName] = col 103 case float64: 104 col := make([]float64, len(records)) 105 if err := fillFloats(col, records, colName); err != nil { 106 return nil, err 107 } 108 result[colName] = col 109 case bool: 110 col := make([]bool, len(records)) 111 if err := fillBools(col, records, colName); err != nil { 112 return nil, err 113 } 114 result[colName] = col 115 case nil, string: 116 col := make([]*string, len(records)) 117 if err := fillStrings(col, records, colName); err != nil { 118 return nil, err 119 } 120 result[colName] = col 121 default: 122 return nil, qerrors.New("jsonRecordsToData", "unknown type of %s", t) 123 } 124 } 125 return result, nil 126 } 127 128 // UnmarshalJSON transforms JSON containing data records or columns into a map of columns 129 // that can be used to create a QFrame. 130 func UnmarshalJSON(r io.Reader) (map[string]interface{}, error) { 131 var records JSONRecords 132 decoder := json.NewDecoder(r) 133 err := decoder.Decode(&records) 134 if err != nil { 135 return nil, qerrors.Propagate("UnmarshalJSON", err) 136 } 137 138 return jsonRecordsToData(records) 139 }