# Processing CSV

version 6.0.0-alpha-1

Groovy has an optional `groovy-csv` module which provides support for reading and writing CSV (RFC 4180) data. The classes are found in the `groovy.csv` package.

## 1. CsvSlurper

`CsvSlurper` parses CSV text into a list of maps, where each row becomes a map keyed by the column headers from the first row. Values are returned as strings.

    def csv = new CsvSlurper().parseText('name,age\nAlice,30\nBob,25')
    assert csv.size() == 2
    assert csv[0].name == 'Alice'
    assert csv[0].age == '30'
    assert csv[1].name == 'Bob'

Rows support dynamic property access using the header names:

    def csv = new CsvSlurper().parseText('''\
        name,city,country
        Alice,London,UK
        Bob,Paris,France'''.stripIndent())
    assert csv[0].city == 'London'
    assert csv[1].country == 'France'

### 1.1. Configuration

The separator character and quote character can be customised:

    def csv = new CsvSlurper().setSeparator((char) '\t').parseText('name\tage\nAlice\t30')
    assert csv[0].name == 'Alice'
    assert csv[0].age == '30'

Quoted fields follow RFC 4180 — fields containing the separator, newlines, or the quote character are enclosed in quotes, with embedded quotes doubled:

    def csv = new CsvSlurper().parseText('name,note\nAlice,"hello, world"\nBob,"say ""hi"""')
    assert csv[0].note == 'hello, world'
    assert csv[1].note == 'say "hi"'

### 1.2. Typed parsing

`CsvSlurper` can parse CSV directly into typed objects using Jackson databinding. Standard Jackson annotations such as `@JsonProperty` and `@JsonFormat` are supported for column name mapping and type conversion. This is particularly useful for CSV since all values are strings — Jackson handles the conversion to numeric, date, and other types automatically:

    static class Sale {
        String customer
        BigDecimal amount
    }

    def sales = new CsvSlurper().parseAs(Sale, 'customer,amount\nAcme,1500.00\nGlobex,250.50')
    assert sales.size() == 2
    assert sales[0].customer == 'Acme'
    assert sales[0].amount == 1500.00
    assert sales[1].customer == 'Globex'

## 2. CsvBuilder

`CsvBuilder` converts collections of maps or typed objects to CSV. The keys of the first map are used as column headers.

    def data = [
        [name: 'Alice', age: 30],
        [name: 'Bob', age: 25]
    ]
    def csv = CsvBuilder.toCsv(data)
    assert csv.contains('name,age')
    assert csv.contains('Alice,30')
    assert csv.contains('Bob,25')

### 2.1. Typed writing

`CsvBuilder` can also write typed objects. Jackson annotations are supported for column naming and formatting:

    static class Product {
        String name
        BigDecimal price
    }

    def products = [new Product(name: 'Widget', price: 9.99),
                    new Product(name: 'Gadget', price: 24.50)]
    def csv = CsvBuilder.toCsv(products, Product)
    assert csv.contains('name,price')
    assert csv.contains('Widget,9.99')
    assert csv.contains('Gadget,24.5')

### 2.2. Round-trip

CSV written by `CsvBuilder` can be read back with `CsvSlurper`:

    def original = [[name: 'Alice', age: '30'], [name: 'Bob', age: '25']]
    def csv = CsvBuilder.toCsv(original)
    def parsed = new CsvSlurper().parseText(csv)
    assert parsed[0].name == 'Alice'
    assert parsed[1].age == '25'