You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by gi...@apache.org on 2026/08/25 05:39:10 UTC
(groovy-dev-site) branch asf-site updated: 2026/08/25 05:39:05: Generated dev website from groovy-website@f0e6490
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/groovy-dev-site.git
The following commit(s) were added to refs/heads/asf-site by this push:
new aae6fa65 2026/08/25 05:39:05: Generated dev website from groovy-website@f0e6490
aae6fa65 is described below
commit aae6fa656d20cfb526cbe63b54f8b2d24f7d00a2
Author: jenkins <bu...@apache.org>
AuthorDate: Tue Aug 25 05:39:05 2026 +0000
2026/08/25 05:39:05: Generated dev website from groovy-website@f0e6490
---
blog/feed.atom | 6 +-
blog/index.html | 4 +-
blog/reading-and-writing-csv-files.html | 197 +++++++++++++++++++++++++++++++-
search/search-index.json | 4 +-
4 files changed, 198 insertions(+), 13 deletions(-)
diff --git a/blog/feed.atom b/blog/feed.atom
index 5bef4c9e..b24a660a 100644
--- a/blog/feed.atom
+++ b/blog/feed.atom
@@ -4,7 +4,7 @@
<link href="http://groovy.apache.org/blog"/>
<link href="http://groovy.apache.org/blog/feed.atom" rel="self"/>
<id>http://groovy.apache.org/blog</id>
- <updated>2026-08-24T20:00:00Z</updated>
+ <updated>2026-08-25T10:00:00Z</updated>
<entry>
<id>http://groovy.apache.org/blog/groovy-geaflow</id>
<author>
@@ -782,9 +782,9 @@
</author>
<title type="html">Reading and Writing CSV files with Groovy&trade;</title>
<link href="http://groovy.apache.org/blog/reading-and-writing-csv-files"/>
- <updated>2022-07-25T14:26:20Z</updated>
+ <updated>2026-08-25T10:00:00Z</updated>
<published>2022-07-25T14:26:20Z</published>
- <summary type="html">This post looks at processing CSV files using OpenCSV, Apache Commons CSV, and Jackson Databind libraries.</summary>
+ <summary type="html">This post looks at processing CSV files using Groovy's groovy-csv module, along with the OpenCSV, Apache Commons CSV, and Jackson Databind libraries.</summary>
</entry>
<entry>
<id>http://groovy.apache.org/blog/groovy-release-train-4-0</id>
diff --git a/blog/index.html b/blog/index.html
index 846d21e3..dbdf3dc2 100644
--- a/blog/index.html
+++ b/blog/index.html
@@ -63,7 +63,7 @@
</ul>
</div>
</div>
- </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3' id='blog-index'><ul class='nav-sidebar list'><li class='active'><a href='/blog/'>Blogs</a></li><li><a href='groovy6-formal-methods'>Groovy 6 features for formal method advocates</a></li><li><a href='groovy-null-checker'>Compile-time null safety for Groovy™</a></li><li><a href='groovy-pekko-gpars'>Using Apache Pekko actors and Groovy™ 6 actors</a></li><l [...]
+ </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3' id='blog-index'><ul class='nav-sidebar list'><li class='active'><a href='/blog/'>Blogs</a></li><li><a href='reading-and-writing-csv-files'>Reading and Writing CSV files with Groovy™</a></li><li><a href='groovy6-formal-methods'>Groovy 6 features for formal method advocates</a></li><li><a href='groovy-null-checker'>Compile-time null safety for Groovy™</a> [...]
<div class='row'>
<div class='colset-3-footer'>
<div class='col-1'>
@@ -107,7 +107,7 @@
colors: am5.ColorSet.new(root, {})
}));
wc.data.setAll([
- { category: "groovy", value: 94 }, { category: "asf", value: 2 }, { category: "apachecon", value: 3 }, { category: "communityovercode", value: 2 }, { category: "natural language processing", value: 2 }, { category: "nlp", value: 1 }, { category: "nlpcraft", value: 1 }, { category: "combinations", value: 1 }, { category: "permutations", value: 1 }, { category: "testing", value: 2 }, { category: "junit", value: 2 }, { category: "spock", value: 2 }, { category: "jqwik", valu [...]
+ { category: "groovy", value: 94 }, { category: "asf", value: 2 }, { category: "apachecon", value: 3 }, { category: "communityovercode", value: 2 }, { category: "natural language processing", value: 2 }, { category: "nlp", value: 1 }, { category: "nlpcraft", value: 1 }, { category: "combinations", value: 1 }, { category: "permutations", value: 1 }, { category: "testing", value: 2 }, { category: "junit", value: 2 }, { category: "spock", value: 2 }, { category: "jqwik", valu [...]
]);
wc.labels.template.setAll({
paddingTop: 5,
diff --git a/blog/reading-and-writing-csv-files.html b/blog/reading-and-writing-csv-files.html
index 8240273c..890ef419 100644
--- a/blog/reading-and-writing-csv-files.html
+++ b/blog/reading-and-writing-csv-files.html
@@ -3,7 +3,7 @@
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]--><head>
- <meta charset='utf-8'/><meta http-equiv='X-UA-Compatible' content='IE=edge'/><meta name='viewport' content='width=device-width, initial-scale=1'/><meta name='keywords' content='csv, data, deserialization, files, groovy, reading, records, data science, serialization, writing, opencsv, commons csv, jackson databind, cycling'/><meta name='description' content='This post looks at processing CSV files using OpenCSV, Apache Commons CSV, and Jackson Databind libraries.'/><title>The Apache G [...]
+ <meta charset='utf-8'/><meta http-equiv='X-UA-Compatible' content='IE=edge'/><meta name='viewport' content='width=device-width, initial-scale=1'/><meta name='keywords' content='csv, data, deserialization, files, groovy, reading, records, data science, serialization, writing, groovy-csv, csvslurper, csvbuilder, opencsv, commons csv, jackson databind, cycling'/><meta name='description' content='This post looks at processing CSV files using Groovy's groovy-csv module, along with th [...]
</head><body>
<div id='fork-me'>
<a href='https://github.com/apache/groovy'>
@@ -63,7 +63,7 @@
</ul>
</div>
</div>
- </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Reading and Writing CSV files with Groovy™</a></li><li><a href='#_introduction' class='anchor-link'>Introduction</a></li><li><a href='#_commons_csv' class='anchor-link'>Commons CSV</a></li><li><a href='#_opencsv' class='anchor-link'>OpenCSV</a></li><li><a href='#_jac [...]
+ </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Reading and Writing CSV files with Groovy™</a></li><li><a href='#_introduction' class='anchor-link'>Introduction</a></li><li><a href='#_groovys_csv_module' class='anchor-link'>Groovy’s CSV module</a></li><li><a href='#_commons_csv' class='anchor-link'>Commons C [...]
<h2 id="_introduction">Introduction</h2>
<div class="sectionbody">
<div class="paragraph">
@@ -164,7 +164,7 @@ double quotes but then what if the value contains a double quote?
And so forth. For this reason, CSV libraries are recommended.</p>
</div>
<div class="paragraph">
-<p>We’ll look at three shortly, but first let’s summarise some of
+<p>We’ll look at four shortly, but first let’s summarise some of
the highlights of the tour by looking at multiple winners.
Here is some code which summarises our CSV data:</p>
</div>
@@ -195,7 +195,189 @@ Here’s the output:</p>
<p><span class="image"><img src="./img/HommesMultipleWins.png" alt="MultipleWins"></span></p>
</div>
<div class="paragraph">
-<p>Okay, now let’s look at our three CSV libraries.</p>
+<p>Okay, now let’s look at our four CSV options, starting with the
+one which comes with Groovy itself.</p>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_groovys_csv_module">Groovy’s CSV module</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>Groovy 6 added an optional <code>groovy-csv</code> module
+(<a href="https://issues.apache.org/jira/browse/GROOVY-11923">GROOVY-11923</a>)
+containing <code>CsvSlurper</code> and <code>CsvBuilder</code> classes in the <code>groovy.csv</code>
+package. If you have used <code>JsonSlurper</code>/<code>JsonBuilder</code> or
+<code>XmlSlurper</code>/<code>XmlBuilder</code>, the naming and the style will look familiar.
+Being an optional module, we need to add it to our build, or for a
+script:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">@Grab('org.apache.groovy:groovy-csv:6.0.0-beta-3')</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This module is row-oriented rather than line-oriented: <code>CsvBuilder</code>
+writes a collection of maps, and <code>CsvSlurper</code> reads back a list of maps
+keyed by the header names. So, we’ll express our podium data like this:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">def data = [
+ [place: 1, firstname: 'Lorena', lastname: 'Wiebes', team: 'Team DSM'],
+ [place: 2, firstname: 'Marianne', lastname: 'Vos', team: 'Team Jumbo Visma'],
+ [place: 3, firstname: 'Lotte', lastname: 'Kopecky', team: 'Team SD Worx']
+]</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Writing it is a one-liner, with the keys of the first map becoming the
+header row:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">file.text = CsvBuilder.toCsv(data)</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>That gives us:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code>place,firstname,lastname,team
+1,Lorena,Wiebes,"Team DSM"
+2,Marianne,Vos,"Team Jumbo Visma"
+3,Lotte,Kopecky,"Team SD Worx"</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Reading it back needs no configuration, since treating the first row as
+a header is the default:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">def podium = new CsvSlurper().parse(file)
+assert podium[0].firstname == 'Lorena'
+assert podium*.team.toSet().size() == 3</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>As well as <code>parseText</code> for a String, there are <code>parse</code> overloads taking
+a <code>Reader</code>, <code>InputStream</code>, <code>File</code> or <code>Path</code>, each with an optional
+<code>Charset</code>.</p>
+</div>
+<div class="paragraph">
+<p>Our more elaborate example needs no extra work to skip or interpret the
+header row, and like the other libraries, we can process using column
+names:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">def rows = new CsvSlurper().parse(new File('HommesStageWinners.csv'))
+assert rows.size() == 21
+assert rows.collect { it.firstname + ' ' + it.lastname }.toSet().size() == 15
+assert rows*.team.toSet().size() == 10
+assert rows*.country.toSet().size() == 9</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The messy details we worried about earlier are handled for us. Fields
+containing the separator, newlines or quotes are quoted on the way out
+and unquoted on the way in, with embedded quotes doubled, as per
+RFC 4180:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">def csv = '''\
+name,quote
+"Vos, Marianne","She said ""go"""
+'''
+def row = new CsvSlurper().parseText(csv)[0]
+assert row.name == 'Vos, Marianne'
+assert row.quote == 'She said "go"'</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>If your data doesn’t use commas and double quotes, fluent setters
+change that:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">assert new CsvSlurper().setSeparator('\t' as char)
+ .parseText('a\tb\n1\t2') == [[a: '1', b: '2']]</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p><code>CsvBuilder</code> has matching <code>setSeparator</code> and <code>setQuoteChar</code> setters and
+implements <code>Writable</code>, so it can be handed straight to a writer:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">file.withWriter { w -> new CsvBuilder()(data).writeTo(w) }</code></pre>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_typed_parsing">Typed parsing</h3>
+<div class="paragraph">
+<p>CSV has no types of its own, every cell is just text, so the <code>parse</code>
+methods hand back <code>String</code> values. That is often what we want, but when
+it isn’t, the <code>parseAs</code> method converts rows into typed objects. We
+describe the shape we want with a record:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">record Cyclist(int stage,
+ @JsonProperty('firstname') String first,
+ @JsonProperty('lastname') String last,
+ String team, String country) {
+ String full() { "$first $last" }
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>And then read our file directly into records:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">List<Cyclist> cyclists = new CsvSlurper().parseAs(Cyclist, new File('HommesStageWinners.csv'))
+assert cyclists.size() == 21
+assert cyclists*.full().toSet().size() == 15
+assert cyclists*.team.toSet().size() == 10
+assert cyclists*.country.toSet().size() == 9
+assert cyclists[0].stage == 1 // an int, not a String</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Jackson Databind performs the databinding here, which is why that is a
+Jackson annotation. We only need it for the two components whose names
+don’t match their column, and <code>stage</code> arrives as an <code>int</code> without any
+help. We’ll meet the same annotations again in the Jackson Databind
+section later, but here we don’t assemble a <code>CsvMapper</code> and a
+<code>CsvSchema</code> ourselves. Conversion isn’t limited to numbers, <code>java.time</code>
+types work too, with <code>@JsonFormat</code> where the pattern isn’t the default:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">record Sale(String customer, BigDecimal amount,
+ @JsonFormat(pattern = 'yyyy-MM-dd') LocalDate date) { }
+
+def sales = new CsvSlurper().parseAs(Sale,
+ 'customer,amount,date\nAcme,1500.00,2026-03-01\nGlobex,250.50,2026-03-02')
+assert sales*.amount.sum() == 1750.50
+assert sales[0].date.monthValue == 3</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Writing typed objects back out is <code>CsvBuilder.toCsv(data, type)</code>. One
+thing to watch there: the column order comes from Jackson’s view of the
+type rather than the declaration order, so if the exact ordering
+matters, supply a schema yourself.</p>
+</div>
+<div class="paragraph">
+<p>The module is incubating in Groovy 6, so we’d love your feedback. The
+<a href="https://docs.groovy-lang.org/docs/latest6/html/documentation/csv-userguide.html">Processing CSV user guide</a> has the full API.</p>
</div>
</div>
</div>
@@ -484,8 +666,11 @@ file.withReader { r ->
<div class="paragraph">
<p>We have looked at writing and reading CSV files to Strings and
domain classes and records. We had a look at handling simple
-cases by hand and also looked at the OpenCSV, Commons CSV and
-Jackson Databind CSV libraries.</p>
+cases by hand, at Groovy 6’s own <code>groovy-csv</code> module, and at the
+OpenCSV, Commons CSV and Jackson Databind CSV libraries.</p>
+</div>
+<div class="paragraph">
+<p>The <code>groovy-csv</code> examples were tested with Groovy 6.0.0-beta-3.</p>
</div>
<div class="paragraph">
<p>Code for these examples:<br>
diff --git a/search/search-index.json b/search/search-index.json
index e364db3a..5097a7ee 100644
--- a/search/search-index.json
+++ b/search/search-index.json
@@ -366,7 +366,7 @@
{
"id": "blog/reading-and-writing-csv-files.html",
"title": "The Apache Groovy programming language - Blogs - Reading and Writing CSV files with Groovy™",
- "content": "The Apache Groovy programming language - Blogs - Reading and Writing CSV files with Groovy™ Socialize Discuss on the mailing list Groovy on X Groovy on Bluesky Groovy on Mastodon Groovy on LinkedIn Events and conferences Source code on GitHub Report issues in Jira Stack Overflow questions Slack Community You are using an outdated browser. Please upgrade your browser to improve your experience. Apache Groovy™ Learn Documentation Download Support Contribute [...]
+ "content": "The Apache Groovy programming language - Blogs - Reading and Writing CSV files with Groovy™ Socialize Discuss on the mailing list Groovy on X Groovy on Bluesky Groovy on Mastodon Groovy on LinkedIn Events and conferences Source code on GitHub Report issues in Jira Stack Overflow questions Slack Community You are using an outdated browser. Please upgrade your browser to improve your experience. Apache Groovy™ Learn Documentation Download Support Contribute [...]
"url": "blog/reading-and-writing-csv-files.html",
"site": "dev"
},
@@ -744,7 +744,7 @@
{
"id": "blog/index.html",
"title": "The Apache Groovy programming language - Blogs",
- "content": "The Apache Groovy programming language - Blogs Socialize Discuss on the mailing list Groovy on X Groovy on Bluesky Groovy on Mastodon Groovy on LinkedIn Events and conferences Source code on GitHub Report issues in Jira Stack Overflow questions Slack Community You are using an outdated browser. Please upgrade your browser to improve your experience. Apache Groovy™ Learn Documentation Download Support Contribute Ecosystem Blog posts Socialize Blogs Groovy 6 featu [...]
+ "content": "The Apache Groovy programming language - Blogs Socialize Discuss on the mailing list Groovy on X Groovy on Bluesky Groovy on Mastodon Groovy on LinkedIn Events and conferences Source code on GitHub Report issues in Jira Stack Overflow questions Slack Community You are using an outdated browser. Please upgrade your browser to improve your experience. Apache Groovy™ Learn Documentation Download Support Contribute Ecosystem Blog posts Socialize Blogs Reading and Wr [...]
"url": "blog/index.html",
"site": "dev"
},