Repository: TomWright/dasel
Pull request #366 (2023-10-18 16:44:59)

Fix an parsing issue in the CSV formatter
Fixed https://github.com/TomWright/dasel/issues/364


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fdb1318..939b975 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Fixed
 
 - Resolved an issue with YAML parser that was causing strings to be read as booleans.
+- Fix a parsing issue with CSV types that forced you to expand and merge in order for it selects to work [Issue 364](https://github.com/TomWright/dasel/issues/364). 
 
 ## [v2.3.6] - 2023-08-30
 
diff --git a/storage/csv.go b/storage/csv.go
index a2f888e..cee683d 100644
--- a/storage/csv.go
+++ b/storage/csv.go
@@ -33,7 +33,7 @@ func (p *CSVParser) FromBytes(byteData []byte) (dasel.Value, error) {
 	}
 
 	reader := csv.NewReader(bytes.NewBuffer(byteData))
-	res := make([]map[string]interface{}, 0)
+	res := make([]*dencoding.Map, 0)
 	records, err := reader.ReadAll()
 	if err != nil {
 		return dasel.Value{}, fmt.Errorf("could not read csv file: %w", err)
@@ -47,13 +47,13 @@ func (p *CSVParser) FromBytes(byteData []byte) (dasel.Value, error) {
 			headers = row
 			continue
 		}
-		rowRes := make(map[string]interface{})
+		rowRes := dencoding.NewMap()
 		allEmpty := true
 		for index, val := range row {
 			if val != "" {
 				allEmpty = false
 			}
-			rowRes[headers[index]] = val
+			rowRes.Set(headers[index], val)
 		}
 		if !allEmpty {
 			res = append(res, rowRes)
@@ -124,6 +124,30 @@ func interfaceToCSVDocument(val interface{}) (*CSVDocument, error) {
 			Headers: headers,
 		}, nil
 
+	case []*dencoding.Map:
+		mapVals := make([]map[string]interface{}, 0)
+		headers := make([]string, 0)
+		for _, val := range v {
+			mapVals = append(mapVals, val.UnorderedData())
+			for _, objectKey := range val.Keys() {
+				found := false
+				for _, existingHeader := range headers {
+					if existingHeader == objectKey {
+						found = true
+						break
+					}
+				}
+				if !found {
+					headers = append(headers, objectKey)
+				}
+			}
+		}
+		sort.Strings(headers)
+		return &CSVDocument{
+			Value:   mapVals,
+			Headers: headers,
+		}, nil
+
 	default:
 		return nil, fmt.Errorf("CSVParser.toBytes cannot handle type %T", val)
 	}


Test patch:
diff --git a/internal/command/select_test.go b/internal/command/select_test.go
index 8dbbd3e..6ff855d 100644
--- a/internal/command/select_test.go
+++ b/internal/command/select_test.go
@@ -228,4 +228,28 @@ octal: 8`)),
 		nil,
 	))
 
+	t.Run("Issue364 - CSV root element part 1", runTest(
+		[]string{"-r", "csv", "-w", "csv", "all().merge()"},
+		[]byte(`A,B,C
+a,b,c
+d,e,f`),
+		newline([]byte(`A,B,C
+a,b,c
+d,e,f`)),
+		nil,
+		nil,
+	))
+
+	t.Run("Issue364 - CSV root element part 2", runTest(
+		[]string{"-r", "csv", "-w", "csv"},
+		[]byte(`A,B,C
+a,b,c
+d,e,f`),
+		newline([]byte(`A,B,C
+a,b,c
+d,e,f`)),
+		nil,
+		nil,
+	))
+
 }
diff --git a/storage/csv_test.go b/storage/csv_test.go
index dc4131d..a8eed7f 100644
--- a/storage/csv_test.go
+++ b/storage/csv_test.go
@@ -2,6 +2,7 @@ package storage_test
 
 import (
 	"github.com/tomwright/dasel/v2"
+	"github.com/tomwright/dasel/v2/dencoding"
 	"github.com/tomwright/dasel/v2/storage"
 	"reflect"
 	"testing"
@@ -11,15 +12,13 @@ var csvBytes = []byte(`id,name
 1,Tom
 2,Jim
 `)
-var csvMap = []map[string]interface{}{
-	{
-		"id":   "1",
-		"name": "Tom",
-	},
-	{
-		"id":   "2",
-		"name": "Jim",
-	},
+var csvMap = []*dencoding.Map{
+	dencoding.NewMap().
+		Set("id", "1").
+		Set("name", "Tom"),
+	dencoding.NewMap().
+		Set("id", "2").
+		Set("name", "Jim"),
 }
 
 func TestCSVParser_FromBytes(t *testing.T) {