github.com/mithrandie/csvq@v1.18.1/lib/json/conversion.go (about) 1 package json 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "time" 8 9 "github.com/mithrandie/go-text/json" 10 11 "github.com/mithrandie/csvq/lib/value" 12 "github.com/mithrandie/ternary" 13 ) 14 15 func ConvertToValue(structure json.Structure) value.Primary { 16 var p value.Primary 17 18 switch structure.(type) { 19 case json.Number: 20 p = value.NewFloat(structure.(json.Number).Raw()) 21 case json.Integer: 22 p = value.NewInteger(structure.(json.Integer).Raw()) 23 case json.String: 24 p = value.NewString(structure.(json.String).Raw()) 25 case json.Boolean: 26 p = value.NewBoolean(structure.(json.Boolean).Raw()) 27 case json.Null: 28 p = value.NewNull() 29 default: 30 p = value.NewString(structure.Encode()) 31 } 32 33 return p 34 } 35 36 func ConvertToArray(array json.Array) []value.Primary { 37 row := make([]value.Primary, 0, len(array)) 38 for _, v := range array { 39 row = append(row, ConvertToValue(v)) 40 } 41 42 return row 43 } 44 45 func ConvertToTableValue(array json.Array) ([]string, [][]value.Primary, error) { 46 exists := func(s string, list []string) bool { 47 for _, v := range list { 48 if s == v { 49 return true 50 } 51 } 52 return false 53 } 54 55 var header []string 56 for _, elem := range array { 57 obj, ok := elem.(json.Object) 58 if !ok { 59 return nil, nil, errors.New("rows loaded from json must be objects") 60 } 61 if header == nil { 62 header = make([]string, 0, obj.Len()) 63 } 64 65 for _, m := range obj.Members { 66 if !exists(m.Key, header) { 67 header = append(header, m.Key) 68 } 69 } 70 } 71 72 rows := make([][]value.Primary, 0, len(array)) 73 for _, elem := range array { 74 row := make([]value.Primary, 0, len(header)) 75 76 obj, _ := elem.(json.Object) 77 for _, column := range header { 78 if obj.Exists(column) { 79 row = append(row, ConvertToValue(obj.Value(column))) 80 } else { 81 row = append(row, ConvertToValue(json.Null{})) 82 } 83 } 84 85 rows = append(rows, row) 86 } 87 88 return header, rows, nil 89 } 90 91 func ConvertTableValueToJsonStructure(ctx context.Context, fields []string, rows [][]value.Primary) (json.Structure, error) { 92 pathes, err := ParsePathes(fields) 93 if err != nil { 94 return nil, err 95 } 96 97 structure := make(json.Array, len(rows)) 98 for i := range rows { 99 if i&15 == 0 && ctx.Err() != nil { 100 return nil, ctx.Err() 101 } 102 103 rowStructure, err := ConvertRecordValueToJsonStructure(pathes, rows[i]) 104 if err != nil { 105 return nil, err 106 } 107 structure[i] = rowStructure 108 } 109 110 return structure, nil 111 } 112 113 func ParsePathes(fields []string) ([]PathExpression, error) { 114 var err error 115 pathes := make([]PathExpression, len(fields)) 116 for i, field := range fields { 117 pathes[i], err = Path.Parse(field) 118 if err != nil { 119 if perr, ok := err.(*PathSyntaxError); ok { 120 err = errors.New(fmt.Sprintf("%s at column %d in %q", perr.Error(), perr.Column, field)) 121 } 122 return nil, err 123 } 124 } 125 return pathes, nil 126 } 127 128 func ConvertRecordValueToJsonStructure(pathes []PathExpression, row []value.Primary) (json.Structure, error) { 129 var structure json.Structure 130 131 fieldLen := len(pathes) 132 133 if len(row) != fieldLen { 134 return nil, errors.New("field length does not match") 135 } 136 137 for i, path := range pathes { 138 structure = addPathValueToRowStructure(structure, path.(ObjectPath), row[i], fieldLen) 139 } 140 141 return structure, nil 142 } 143 144 func addPathValueToRowStructure(parent json.Structure, path ObjectPath, val value.Primary, fieldLen int) json.Structure { 145 var obj json.Object 146 if parent == nil { 147 obj = json.NewObject(fieldLen) 148 } else { 149 obj = parent.(json.Object) 150 } 151 152 if path.Child == nil { 153 obj.Add(path.Name, ParseValueToStructure(val)) 154 } else { 155 valueStructure := addPathValueToRowStructure(obj.Value(path.Name), path.Child.(ObjectPath), val, fieldLen) 156 if obj.Exists(path.Name) { 157 obj.Update(path.Name, valueStructure) 158 } else { 159 obj.Add(path.Name, valueStructure) 160 } 161 } 162 163 return obj 164 } 165 166 func ParseValueToStructure(val value.Primary) json.Structure { 167 var s json.Structure 168 169 switch val.(type) { 170 case *value.String: 171 s = json.String(val.(*value.String).Raw()) 172 case *value.Integer: 173 s = json.Integer(val.(*value.Integer).Raw()) 174 case *value.Float: 175 s = json.Float(val.(*value.Float).Raw()) 176 case *value.Boolean: 177 s = json.Boolean(val.(*value.Boolean).Raw()) 178 case *value.Ternary: 179 t := val.(*value.Ternary) 180 if t.Ternary() == ternary.UNKNOWN { 181 s = json.Null{} 182 } else { 183 s = json.Boolean(t.Ternary().ParseBool()) 184 } 185 case *value.Datetime: 186 s = json.String(val.(*value.Datetime).Format(time.RFC3339Nano)) 187 case *value.Null: 188 s = json.Null{} 189 } 190 191 return s 192 }