wip: unmarshal basic functionality works in debug
This commit is contained in:
parent
5b9b896f34
commit
8a84f5164d
@ -1,8 +1,10 @@
|
|||||||
package gocsvparser
|
package gocsvparser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,7 +28,12 @@ func newDefaultHandler() *defaultRecordHandler {
|
|||||||
|
|
||||||
func (d *defaultRecordHandler) HandleRecord(v any, record []string) error {
|
func (d *defaultRecordHandler) HandleRecord(v any, record []string) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
|
strField string
|
||||||
|
index int
|
||||||
|
ok bool
|
||||||
|
binding columnFieldBinding
|
||||||
|
val reflect.Value
|
||||||
)
|
)
|
||||||
|
|
||||||
if d.outType == nil {
|
if d.outType == nil {
|
||||||
@ -34,20 +41,116 @@ func (d *defaultRecordHandler) HandleRecord(v any, record []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error HandleRecord: detType: %+v", err)
|
return fmt.Errorf("error HandleRecord: detType: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = d.mapStructTag()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error HandleRecord: mapStructTag: %+v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !d.columnNameMapped {
|
if !d.columnNameMapped {
|
||||||
|
for index, strField = range record {
|
||||||
|
if binding, ok = d.fieldByTag[strField]; ok {
|
||||||
|
binding.index = index
|
||||||
|
d.fieldByTag[strField] = binding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.columnNameMapped = true
|
||||||
|
return HeaderRead
|
||||||
|
}
|
||||||
|
|
||||||
|
val = reflect.ValueOf(v)
|
||||||
|
if val.Kind() != reflect.Pointer {
|
||||||
|
return errors.New("error HandleRecord: v Kind() is not Pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
val = val.Elem()
|
||||||
|
switch val.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
for _, binding = range d.fieldByTag {
|
||||||
|
if binding.index < len(record) {
|
||||||
|
val, err = d.setValue(val, record[binding.index], binding.field)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error HandleRecord: StructField SetValue: %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
return errors.New("error HandleRecord: Map is not supported yet") //TODO: fix me
|
||||||
|
case reflect.Slice:
|
||||||
|
return errors.New("error HandleRecord: Slice is not supported yet") //TODO: fix me
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *defaultRecordHandler) setValue(val reflect.Value, strValue string, structFiled reflect.StructField) (reflect.Value, error) {
|
||||||
|
var (
|
||||||
|
fieldVal reflect.Value
|
||||||
|
f64 float64
|
||||||
|
i64 int64
|
||||||
|
i int
|
||||||
|
err error
|
||||||
|
b bool
|
||||||
|
)
|
||||||
|
|
||||||
|
fieldVal = val.FieldByIndex(structFiled.Index)
|
||||||
|
switch fieldVal.Type().Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
fieldVal.SetString(strValue)
|
||||||
|
case reflect.Bool:
|
||||||
|
b, err = strconv.ParseBool(strValue)
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, fmt.Errorf("ParseBool: %+v", err)
|
||||||
|
}
|
||||||
|
fieldVal.SetBool(b)
|
||||||
|
case reflect.Int64:
|
||||||
|
i64, err = strconv.ParseInt(strValue, 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, fmt.Errorf("ParseInt 64: %+v", err)
|
||||||
|
}
|
||||||
|
fieldVal.SetInt(i64)
|
||||||
|
case reflect.Int32:
|
||||||
|
i64, err = strconv.ParseInt(strValue, 0, 32)
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, fmt.Errorf("ParseInt 32: %+v", err)
|
||||||
|
}
|
||||||
|
fieldVal.SetInt(i64)
|
||||||
|
case reflect.Int16:
|
||||||
|
i64, err = strconv.ParseInt(strValue, 0, 16)
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, fmt.Errorf("ParseInt 16: %+v", err)
|
||||||
|
}
|
||||||
|
fieldVal.SetInt(i64)
|
||||||
|
case reflect.Int8:
|
||||||
|
i64, err = strconv.ParseInt(strValue, 0, 8)
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, fmt.Errorf("ParseInt 8: %+v", err)
|
||||||
|
}
|
||||||
|
fieldVal.SetInt(i64)
|
||||||
|
case reflect.Int:
|
||||||
|
i, err = strconv.Atoi(strValue)
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, fmt.Errorf("strconv.Atoi: %+v", err)
|
||||||
|
}
|
||||||
|
fieldVal.SetInt(int64(i))
|
||||||
|
case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint:
|
||||||
|
return reflect.Value{}, errors.New("unimplemented") //TODO: fix me
|
||||||
|
case reflect.Float32:
|
||||||
|
f64, err = strconv.ParseFloat(strValue, 32)
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, fmt.Errorf("ParseFloat 32: %+v", err)
|
||||||
|
}
|
||||||
|
fieldVal.SetFloat(f64)
|
||||||
|
case reflect.Float64:
|
||||||
|
f64, err = strconv.ParseFloat(strValue, 64)
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, fmt.Errorf("ParseFloat 64: %+v", err)
|
||||||
|
}
|
||||||
|
fieldVal.SetFloat(f64)
|
||||||
|
default:
|
||||||
|
return reflect.Value{}, errors.New("missing implementation") //TODO: fix me
|
||||||
|
}
|
||||||
|
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *defaultRecordHandler) SetFieldConfigs(configs []FieldsConfig) {
|
func (d *defaultRecordHandler) SetFieldConfigs(configs []FieldsConfig) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1,38 @@
|
|||||||
package example
|
package example
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/budiuno/gocsvparser"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mtcarsFlat struct {
|
||||||
|
Model string `csv:"model"`
|
||||||
|
MilesPerGalon float64 `csv:"mpg"`
|
||||||
|
Cylinder int `csv:"cyl"`
|
||||||
|
Displacement float64 `csv:"disp"`
|
||||||
|
Horsepower int `csv:"hp"`
|
||||||
|
DriveShaftRatio float64 `csv:"drat"`
|
||||||
|
Weight float64 `csv:"wt"`
|
||||||
|
QuerterMileTime float64 `csv:"qsec"`
|
||||||
|
VEngine bool `csv:"vs"`
|
||||||
|
AutoTransmission bool `csv:"am"`
|
||||||
|
Gear int `csv:"gear"`
|
||||||
|
Carburetors int `csv:"carb"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMtcars(t *testing.T) {
|
||||||
|
var (
|
||||||
|
cars []mtcarsFlat
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
err = gocsvparser.Unmarshal(MtcarsCsv, &cars)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cars {
|
||||||
|
t.Logf("%+v", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1 +1,37 @@
|
|||||||
package example
|
package example
|
||||||
|
|
||||||
|
var (
|
||||||
|
MtcarsCsv = []byte(`model,mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb
|
||||||
|
Mazda RX4,21,6,160,110,3.9,2.62,16.46,0,1,4,4
|
||||||
|
Mazda RX4 Wag,21,6,160,110,3.9,2.875,17.02,0,1,4,4
|
||||||
|
Datsun 710,22.8,4,108,93,3.85,2.32,18.61,1,1,4,1
|
||||||
|
Hornet 4 Drive,21.4,6,258,110,3.08,3.215,19.44,1,0,3,1
|
||||||
|
Hornet Sportabout,18.7,8,360,175,3.15,3.44,17.02,0,0,3,2
|
||||||
|
Valiant,18.1,6,225,105,2.76,3.46,20.22,1,0,3,1
|
||||||
|
Duster 360,14.3,8,360,245,3.21,3.57,15.84,0,0,3,4
|
||||||
|
Merc 240D,24.4,4,146.7,62,3.69,3.19,20,1,0,4,2
|
||||||
|
Merc 230,22.8,4,140.8,95,3.92,3.15,22.9,1,0,4,2
|
||||||
|
Merc 280,19.2,6,167.6,123,3.92,3.44,18.3,1,0,4,4
|
||||||
|
Merc 280C,17.8,6,167.6,123,3.92,3.44,18.9,1,0,4,4
|
||||||
|
Merc 450SE,16.4,8,275.8,180,3.07,4.07,17.4,0,0,3,3
|
||||||
|
Merc 450SL,17.3,8,275.8,180,3.07,3.73,17.6,0,0,3,3
|
||||||
|
Merc 450SLC,15.2,8,275.8,180,3.07,3.78,18,0,0,3,3
|
||||||
|
Cadillac Fleetwood,10.4,8,472,205,2.93,5.25,17.98,0,0,3,4
|
||||||
|
Lincoln Continental,10.4,8,460,215,3,5.424,17.82,0,0,3,4
|
||||||
|
Chrysler Imperial,14.7,8,440,230,3.23,5.345,17.42,0,0,3,4
|
||||||
|
Fiat 128,32.4,4,78.7,66,4.08,2.2,19.47,1,1,4,1
|
||||||
|
Honda Civic,30.4,4,75.7,52,4.93,1.615,18.52,1,1,4,2
|
||||||
|
Toyota Corolla,33.9,4,71.1,65,4.22,1.835,19.9,1,1,4,1
|
||||||
|
Toyota Corona,21.5,4,120.1,97,3.7,2.465,20.01,1,0,3,1
|
||||||
|
Dodge Challenger,15.5,8,318,150,2.76,3.52,16.87,0,0,3,2
|
||||||
|
AMC Javelin,15.2,8,304,150,3.15,3.435,17.3,0,0,3,2
|
||||||
|
Camaro Z28,13.3,8,350,245,3.73,3.84,15.41,0,0,3,4
|
||||||
|
Pontiac Firebird,19.2,8,400,175,3.08,3.845,17.05,0,0,3,2
|
||||||
|
Fiat X1-9,27.3,4,79,66,4.08,1.935,18.9,1,1,4,1
|
||||||
|
Porsche 914-2,26,4,120.3,91,4.43,2.14,16.7,0,1,5,2
|
||||||
|
Lotus Europa,30.4,4,95.1,113,3.77,1.513,16.9,1,1,5,2
|
||||||
|
Ford Pantera L,15.8,8,351,264,4.22,3.17,14.5,0,1,5,4
|
||||||
|
Ferrari Dino,19.7,6,145,175,3.62,2.77,15.5,0,1,5,6
|
||||||
|
Maserati Bora,15,8,301,335,3.54,3.57,14.6,0,1,5,8
|
||||||
|
Volvo 142E,21.4,4,121,109,4.11,2.78,18.6,1,1,4,2`)
|
||||||
|
)
|
||||||
|
|||||||
94
unmarshal.go
94
unmarshal.go
@ -3,6 +3,14 @@ package gocsvparser
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
HeaderRead = errors.New("column headers successfully read")
|
||||||
)
|
)
|
||||||
|
|
||||||
type FieldsConfig struct {
|
type FieldsConfig struct {
|
||||||
@ -115,6 +123,13 @@ func (u *Unmarshaler) processOptions(options ...CsvOption) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *Unmarshaler) Unmarshal(data []byte, v any, options ...CsvOption) error {
|
func (u *Unmarshaler) Unmarshal(data []byte, v any, options ...CsvOption) error {
|
||||||
|
var (
|
||||||
|
typ reflect.Type
|
||||||
|
val, slice reflect.Value
|
||||||
|
record []string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
u.options = append(u.options, options...)
|
u.options = append(u.options, options...)
|
||||||
}
|
}
|
||||||
@ -124,9 +139,38 @@ func (u *Unmarshaler) Unmarshal(data []byte, v any, options ...CsvOption) error
|
|||||||
|
|
||||||
if u.handlerType == direct {
|
if u.handlerType == direct {
|
||||||
if u.recordHandler == nil {
|
if u.recordHandler == nil {
|
||||||
//TODO: build default generator
|
u.recordHandler = newDefaultHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slice, typ, err = u.detOutputType(v)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error Unmarshal: detOutputType: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
record, err = u.csvReader.Read()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return fmt.Errorf("error Unmarshal: csv.Reader.Read: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err = u.newElem(typ)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error Unmarshal: newElem: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = u.recordHandler.HandleRecord(val.Interface(), record)
|
||||||
|
if err != nil {
|
||||||
|
if err == HeaderRead {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return fmt.Errorf("error Unmarshal: RecordHandler.HandleRecord: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
slice.Set(reflect.Append(slice, val.Elem()))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -135,23 +179,41 @@ func (u *Unmarshaler) Unmarshal(data []byte, v any, options ...CsvOption) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *Unmarshaler) detOutputType(v any) (reflect.Value, reflect.Type, error) {
|
||||||
|
var typ reflect.Type
|
||||||
|
|
||||||
|
if v == nil {
|
||||||
|
return reflect.Value{}, nil, errors.New("the output parameter is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
if val.Kind() != reflect.Pointer {
|
||||||
|
return reflect.Value{}, nil, errors.New("invalid output parameter type")
|
||||||
|
}
|
||||||
|
|
||||||
|
val = val.Elem()
|
||||||
|
if val.Kind() != reflect.Slice {
|
||||||
|
return reflect.Value{}, nil, errors.New("invalid output parameter type")
|
||||||
|
}
|
||||||
|
|
||||||
|
typ = val.Type().Elem()
|
||||||
|
|
||||||
|
return val, typ, nil
|
||||||
|
}
|
||||||
|
|
||||||
func Unmarshal(data []byte, v any, options ...CsvOption) error {
|
func Unmarshal(data []byte, v any, options ...CsvOption) error {
|
||||||
return NewUnmarshaler().Unmarshal(data, v, options...)
|
return NewUnmarshaler().Unmarshal(data, v, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func Read(i interface{}) {
|
func (u *Unmarshaler) newElem(typ reflect.Type) (reflect.Value, error) {
|
||||||
// var val reflect.Value
|
switch typ.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
return reflect.New(typ), nil
|
||||||
|
case reflect.Map:
|
||||||
|
//TODO: implementation
|
||||||
|
case reflect.Slice:
|
||||||
|
//TODO: implementation
|
||||||
|
}
|
||||||
|
|
||||||
// val = reflect.ValueOf(i)
|
return reflect.Zero(typ), errors.New("invalid impelementation") //TODO: placeholder; update me
|
||||||
// if val.Kind() == reflect.Pointer {
|
}
|
||||||
// val = val.Elem()
|
|
||||||
|
|
||||||
// if val.Kind() == reflect.Slice {
|
|
||||||
// val = val.Elem()
|
|
||||||
|
|
||||||
// if val.Kind() == reflect.Struct {
|
|
||||||
// fields := reflect.VisibleFields(val.Type())
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user