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  }