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  }