Merge branch 'master' into release
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 711be37..da85ce2 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -5,14 +5,16 @@
 
 INTRODUCTION:
 
-This document contains the release notes for the 1.8 version of Apache Commons CSV.
+This document contains the release notes for the 1.8-SNAPSHOT version of Apache Commons CSV.
 Commons CSV reads and writes files in variations of the Comma Separated Value (CSV) format.
 
 Commons CSV requires at least Java 6.
 
 The Apache Commons CSV library provides a simple interface for reading and writing CSV files of various types.
 
-Feature and bug fix release (Java 8)
+Feature and bug fix release (Java 8).
+This release fixes serialization compatibility of CSVRecord with versions 1.0 to 1.6. New fields added since 
+1.7 are not serialized. Support for Serializable is scheduled to be removed in version 2.0.
 
 Changes in this version include:
 
@@ -32,6 +34,7 @@
 o CSV-245:  Post 1.7 release fixes. Thanks to Alex Herbert.
 o CSV-252:  Upgrade test framework to JUnit 5 Jupiter #49, #50. Thanks to Alex Herbert.
 o CSV-247:  A single empty header is allowed when not allowing empty column headers. #47. Thanks to Alex Herbert, Gary Gregory.
+o CSV-248:  CSVRecord is not Serializable. Thanks to Alex Herbert.
 o           Use test scope for supercsv #48. Thanks to Alex Herbert.
 
 CHANGES
@@ -41,6 +44,7 @@
 o           Update tests from Hamcrest 2.1 to 2.2. Thanks to Gary Gregory.
 o           Update tests from Mockito 3.1.0 to 3.2.4. Thanks to Gary Gregory.
 o           Fix typos in site and test #53. Thanks to Chen.
+o           Fix typo performance test #55. Thanks to Chen.
 
 
 Historical list of changes: https://commons.apache.org/proper/commons-csv/changes-report.html
diff --git a/pom.xml b/pom.xml
index c316439..95e8f80 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@
     <dependency>

       <groupId>org.junit.jupiter</groupId>

       <artifactId>junit-jupiter</artifactId>

-      <version>5.5.2</version>

+      <version>5.6.0</version>

       <scope>test</scope>

     </dependency>

     <dependency>

@@ -140,7 +140,7 @@
     <commons.release.version>1.8</commons.release.version>

     <commons.release.desc>(Java 8)</commons.release.desc>

     <!-- The RC version used in the staging repository URL. -->

-    <commons.rc.version>RC1</commons.rc.version>

+    <commons.rc.version>RC2</commons.rc.version>

     <commons.bc.version>1.7</commons.bc.version>

     <commons.componentid>csv</commons.componentid>

     <commons.module.name>org.apache.commons.csv</commons.module.name>

@@ -258,6 +258,7 @@
             <!-- The ferc.gov files are included discussion in https://issues.apache.org/jira/browse/LEGAL-175. -->

             <exclude>src/test/resources/ferc.gov/contract.txt</exclude>

             <exclude>src/test/resources/ferc.gov/transaction.txt</exclude>

+            <exclude>src/test/resources/**/*.bin</exclude>

           </excludes>

         </configuration>

       </plugin>

@@ -376,6 +377,7 @@
             <exclude>src/test/resources/CSVFileParser/testCSV85_ignoreEmpty.txt</exclude>

             <exclude>src/test/resources/ferc.gov/contract.txt</exclude>

             <exclude>src/test/resources/ferc.gov/transaction.txt</exclude>

+            <exclude>src/test/resources/**/*.bin</exclude>

           </excludes>

         </configuration>

       </plugin>

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index dd217ee..345cd9d 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -38,7 +38,12 @@
     <title>Apache Commons CSV Release Notes</title>

   </properties>

   <body>

-    <release version="1.8" date="2019-01-18" description="Feature and bug fix release (Java 8)">

+    <release version="1.8" date="2019-02-01" description="Feature and bug fix release (Java 8).

+

+This release fixes serialization compatibility of CSVRecord with versions 1.0 to 1.6.

+New fields added since 1.7 are not serialized. Support for Serializable is scheduled to be

+removed in version 2.0.

+">

       <action issue="CSV-255" type="add" dev="ggregory" due-to="0x100">Add CSVRecord.isSet(int) method #52.</action>

       <action issue="CSV-135" type="fix" dev="sebb" due-to="Mateusz Zakarczemny">Char escape doesn't work properly with quoting.</action>

       <action issue="CSV-244" type="fix" dev="sebb">Test case failures following CSVFormat#equals() update.</action>

@@ -48,11 +53,13 @@
       <action issue="CSV-245" type="fix" dev="ggregory" due-to="Alex Herbert">Post 1.7 release fixes.</action>

       <action issue="CSV-252" type="fix" dev="ggregory" due-to= "Alex Herbert">Upgrade test framework to JUnit 5 Jupiter #49, #50.</action>

       <action issue="CSV-247" type="fix" dev="ggregory" due-to="Alex Herbert, Gary Gregory">A single empty header is allowed when not allowing empty column headers. #47.</action>

+      <action issue="CSV-248" type="fix" dev="ggregory" due-to="Alex Herbert">CSVRecord is not Serializable.</action>

       <action                 type="fix" dev="ggregory" due-to="Alex Herbert">Use test scope for supercsv #48.</action>

       <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from H2 1.4.199 to 1.4.200.</action>

       <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from Hamcrest 2.1 to 2.2.</action>

       <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from Mockito 3.1.0 to 3.2.4.</action>

       <action                 type="update" dev="ggregory" due-to="Chen">Fix typos in site and test #53.</action>

+      <action                 type="update" dev="ggregory" due-to="Chen">Fix typo performance test #55.</action>

     </release>

     <release version="1.7" date="2019-06-01" description="Feature and bug fix release (Java 8)">

       <action issue="CSV-233" type="add" dev="ggregory" due-to="Gary Gregory">Add predefined CSVFormats for printing MongoDB CSV and TSV.</action>

diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java
index 3803d96..c189dc4 100644
--- a/src/main/java/org/apache/commons/csv/CSVParser.java
+++ b/src/main/java/org/apache/commons/csv/CSVParser.java
@@ -555,6 +555,11 @@
      * <p>
      * The map keys are column names. The map values are 0-based indices.
      * </p>
+     * <p>
+     * Note: The map can only provide a one-to-one mapping when the format did not
+     * contain null or duplicate column names.
+     * </p>
+     *
      * @return a copy of the header map.
      */
     public Map<String, Integer> getHeaderMap() {
@@ -577,8 +582,14 @@
 
     /**
      * Returns a read-only list of header names that iterates in column order.
+     * <p>
+     * Note: The list provides strings that can be used as keys in the header map.
+     * The list will not contain null column names if they were present in the input
+     * format.
+     * </p>
      *
      * @return read-only list of header names that iterates in column order.
+     * @see #getHeaderMap()
      * @since 1.7
      */
     public List<String> getHeaderNames() {
diff --git a/src/main/java/org/apache/commons/csv/CSVRecord.java b/src/main/java/org/apache/commons/csv/CSVRecord.java
index e75403e..ed90f36 100644
--- a/src/main/java/org/apache/commons/csv/CSVRecord.java
+++ b/src/main/java/org/apache/commons/csv/CSVRecord.java
@@ -24,9 +24,19 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 
 /**
  * A CSV record parsed from a CSV file.
+ *
+ * <p>
+ * Note: Support for {@link Serializable} is scheduled to be removed in version 2.0.
+ * In version 1.8 the mapping between the column header and the column index was
+ * removed from the serialised state. The class maintains serialization compatibility
+ * with versions pre-1.8 for the record values; these must be accessed by index
+ * following deserialization. There will be loss of any functionally linked to the header
+ * mapping when transferring serialised forms pre-1.8 to 1.8 and vice versa.
+ * </p>
  */
 public final class CSVRecord implements Serializable, Iterable<String> {
 
@@ -45,8 +55,8 @@
     /** The values of the record */
     private final String[] values;
 
-    /** The parser that originates this record. */
-    private final CSVParser parser;
+    /** The parser that originates this record. This is not serialized. */
+    private final transient CSVParser parser;
 
     CSVRecord(final CSVParser parser, final String[] values, final String comment, final long recordNumber,
             final long characterPosition) {
@@ -65,7 +75,7 @@
      * @return the String at the given enum String
      */
     public String get(final Enum<?> e) {
-        return get(e.toString());
+        return get(Objects.toString(e, null));
     }
 
     /**
@@ -82,6 +92,14 @@
     /**
      * Returns a value by name.
      *
+     * <p>
+     * Note: This requires a field mapping obtained from the original parser.
+     * A check using {@link #isMapped(String)} should be used to determine if a
+     * mapping exists from the provided {@code name} to a field index. In this case an
+     * exception will only be thrown if the record does not contain a field corresponding
+     * to the mapping, that is the record length is not consistent with the mapping size.
+     * </p>
+     *
      * @param name
      *            the name of the column to be retrieved.
      * @return the column value, maybe null depending on {@link CSVFormat#getNullString()}.
@@ -89,7 +107,9 @@
      *             if no header mapping was provided
      * @throws IllegalArgumentException
      *             if {@code name} is not mapped or if the record is inconsistent
+     * @see #isMapped(String)
      * @see #isConsistent()
+     * @see #getParser()
      * @see CSVFormat#withNullString(String)
      */
     public String get(final String name) {
@@ -135,12 +155,17 @@
     }
 
     private Map<String, Integer> getHeaderMapRaw() {
-        return parser.getHeaderMapRaw();
+        return parser == null ? null : parser.getHeaderMapRaw();
     }
 
     /**
      * Returns the parser.
      *
+     * <p>
+     * Note: The parser is not part of the serialized state of the record. A null check
+     * should be used when the record may have originated from a serialized form.
+     * </p>
+     *
      * @return the parser.
      * @since 1.7
      */
diff --git a/src/site/xdoc/user-guide.xml b/src/site/xdoc/user-guide.xml
index 9ccbf64..254b461 100644
--- a/src/site/xdoc/user-guide.xml
+++ b/src/site/xdoc/user-guide.xml
@@ -32,7 +32,7 @@
 
       Parsing files with Apache Commons CSV is relatively straight forward.
       The CSVFormat class provides some commonly used CSV variants:
-      
+
       <dl>
         <dt><a href="https://commons.apache.org/proper/commons-csv/apidocs/org/apache/commons/csv/CSVFormat.html#DEFAULT">DEFAULT</a></dt><dd>Standard Comma Separated Value format, as for RFC4180 but allowing empty lines.</dd>
         <dt><a href="https://commons.apache.org/proper/commons-csv/apidocs/org/apache/commons/csv/CSVFormat.html#EXCEL">EXCEL</a></dt><dd>The Microsoft Excel CSV format.</dd>
diff --git a/src/test/java/org/apache/commons/csv/CSVBenchmark.java b/src/test/java/org/apache/commons/csv/CSVBenchmark.java
index d055e69..6d951ba 100644
--- a/src/test/java/org/apache/commons/csv/CSVBenchmark.java
+++ b/src/test/java/org/apache/commons/csv/CSVBenchmark.java
@@ -79,7 +79,7 @@
         while ((line = in.readLine()) != null) {
             count++;
         }
-        
+
         bh.consume(count);
         in.close();
         return count;
@@ -94,7 +94,7 @@
             final String[] values = StringUtils.split(line, ',');
             count += values.length;
         }
-        
+
         bh.consume(count);
         in.close();
         return count;
@@ -103,7 +103,7 @@
     @Benchmark
     public int parseCommonsCSV(final Blackhole bh) throws Exception {
         final BufferedReader in = getReader();
-        
+
         final CSVFormat format = CSVFormat.DEFAULT.withHeader();
 
         int count = 0;
@@ -119,7 +119,7 @@
     @Benchmark
     public int parseGenJavaCSV(final Blackhole bh) throws Exception {
         final BufferedReader in = getReader();
-        
+
         final CsvReader reader = new CsvReader(in);
         reader.setFieldDelimiter(',');
 
@@ -137,7 +137,7 @@
     @Benchmark
     public int parseJavaCSV(final Blackhole bh) throws Exception {
         final BufferedReader in = getReader();
-        
+
         final com.csvreader.CsvReader reader = new com.csvreader.CsvReader(in, ',');
         reader.setRecordDelimiter('\n');
 
@@ -154,7 +154,7 @@
     @Benchmark
     public int parseOpenCSV(final Blackhole bh) throws Exception {
         final BufferedReader in = getReader();
-        
+
         final com.opencsv.CSVReader reader = new com.opencsv.CSVReader(in, ',');
 
         int count = 0;
@@ -170,10 +170,10 @@
     @Benchmark
     public int parseSkifeCSV(final Blackhole bh) throws Exception {
         final BufferedReader in = getReader();
-        
+
         final org.skife.csv.CSVReader reader = new org.skife.csv.SimpleReader();
         reader.setSeperator(',');
-        
+
         final CountingReaderCallback callback = new CountingReaderCallback();
         reader.parse(in, callback);
 
@@ -194,7 +194,7 @@
     @Benchmark
     public int parseSuperCSV(final Blackhole bh) throws Exception {
         final BufferedReader in = getReader();
-        
+
         final CsvListReader reader = new CsvListReader(in, CsvPreference.STANDARD_PREFERENCE);
 
         int count = 0;
diff --git a/src/test/java/org/apache/commons/csv/CSVFileParserTest.java b/src/test/java/org/apache/commons/csv/CSVFileParserTest.java
index 52813a3..8e274f9 100644
--- a/src/test/java/org/apache/commons/csv/CSVFileParserTest.java
+++ b/src/test/java/org/apache/commons/csv/CSVFileParserTest.java
@@ -42,7 +42,7 @@
 
     private static final File BASE = new File("src/test/resources/CSVFileParser");
 
-    private String readTestData(BufferedReader reader) throws IOException {
+    private String readTestData(final BufferedReader reader) throws IOException {
         String line;
         do {
             line = reader.readLine();
@@ -61,7 +61,7 @@
 
     @ParameterizedTest
     @MethodSource("generateData")
-    public void testCSVFile(File testFile) throws Exception {
+    public void testCSVFile(final File testFile) throws Exception {
         try (FileReader fr = new FileReader(testFile); BufferedReader testData = new BufferedReader(fr)) {
             String line = readTestData(testData);
             assertNotNull("file must contain config line", line);
@@ -108,7 +108,7 @@
 
     @ParameterizedTest
     @MethodSource("generateData")
-    public void testCSVUrl(File testFile) throws Exception {
+    public void testCSVUrl(final File testFile) throws Exception {
         try (FileReader fr = new FileReader(testFile); BufferedReader testData = new BufferedReader(fr)) {
             String line = readTestData(testData);
             assertNotNull("file must contain config line", line);
diff --git a/src/test/java/org/apache/commons/csv/CSVFormatTest.java b/src/test/java/org/apache/commons/csv/CSVFormatTest.java
index c7f9885..d7173a7 100644
--- a/src/test/java/org/apache/commons/csv/CSVFormatTest.java
+++ b/src/test/java/org/apache/commons/csv/CSVFormatTest.java
@@ -64,7 +64,7 @@
         return format.withDelimiter(format.getDelimiter());
     }
 
-    private void assertNotEquals(String name, String type, Object left, Object right) {
+    private void assertNotEquals(final String name, final String type, final Object left, final Object right) {
         if (left.equals(right) || right.equals(left)) {
             fail("Objects must not compare equal for " + name + "(" + type + ")");
         }
@@ -153,12 +153,12 @@
 
     @Test
     public void testEqualsHash() throws Exception {
-        Method[] methods = CSVFormat.class.getDeclaredMethods();
-        for (Method method : methods) {
+        final Method[] methods = CSVFormat.class.getDeclaredMethods();
+        for (final Method method : methods) {
             if (Modifier.isPublic(method.getModifiers())) {
                final String name = method.getName();
                if (name.startsWith("with")) {
-                   for (Class<?> cls : method.getParameterTypes()) {
+                   for (final Class<?> cls : method.getParameterTypes()) {
                        final String type = cls.getCanonicalName();
                        if ("boolean".equals(type)) {
                            final Object defTrue = method.invoke(CSVFormat.DEFAULT, new Object[] {Boolean.TRUE});
@@ -550,7 +550,7 @@
 
         final CSVFormat csvFormat = CSVFormat.MYSQL;
 
-        NullPointerException e = assertThrows(NullPointerException.class, () -> csvFormat.format((Object[]) null));
+        final NullPointerException e = assertThrows(NullPointerException.class, () -> csvFormat.format((Object[]) null));
         assertEquals(CSVFormat.class.getName(), e.getStackTrace()[0].getClassName());
     }
 
diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java
index 6b0dfc3..582652f 100644
--- a/src/test/java/org/apache/commons/csv/CSVParserTest.java
+++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java
@@ -725,6 +725,20 @@
     }
 
     @Test
+    public void testHeadersWithNullColumnName() throws IOException {
+        final Reader in = new StringReader("header1,null,header3\n1,2,3\n4,5,6");
+        final Iterator<CSVRecord> records = CSVFormat.DEFAULT
+            .withHeader()
+            .withNullString("null")
+            .withAllowMissingColumnNames()
+            .parse(in).iterator();
+        final CSVRecord record = records.next();
+        // Expect the null header to be missing
+        assertEquals(Arrays.asList("header1", "header3"), record.getParser().getHeaderNames());
+        assertEquals(2, record.getParser().getHeaderMap().size());
+    }
+
+    @Test
     public void testIgnoreCaseHeaderMapping() throws Exception {
         final Reader reader = new StringReader("1,2,3");
         final Iterator<CSVRecord> records = CSVFormat.DEFAULT.withHeader("One", "TWO", "three").withIgnoreHeaderCase()
diff --git a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java
index b3bc910..cb26848 100644
--- a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java
+++ b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java
@@ -289,15 +289,25 @@
 
     @Test
     public void testCSV135() throws IOException {
-        List<String> l = new LinkedList<String>();
-        l.add("\"\"");   // ""
-        l.add("\\\\");   // \\
-        l.add("\\\"\\"); // \"\
-        tryFormat(l, null, null, "\"\",\\\\,\\\"\\"); // "",\\,\"\ (unchanged)
-        tryFormat(l, '"',  null, "\"\"\"\"\"\",\\\\,\"\\\"\"\\\"");              // """""",\\,"\""\" (quoted, and embedded DQ doubled)
-        tryFormat(l, null, '\\', "\"\",\\\\\\\\,\\\\\"\\\\");                    // "",\\\\,\\"\\ (escapes escaped, not quoted)
-        tryFormat(l, '"',  '\\', "\"\\\"\\\"\",\"\\\\\\\\\",\"\\\\\\\"\\\\\"");  // "\"\"","\\\\","\\\"\\" (quoted, and embedded DQ & escape escaped)
-        tryFormat(l, '"',  '"',  "\"\"\"\"\"\",\\\\,\"\\\"\"\\\"");              // """""",\\,"\""\" (quoted, embedded DQ escaped)
+        final List<String> list = new LinkedList<>();
+        list.add("\"\"");   // ""
+        list.add("\\\\");   // \\
+        list.add("\\\"\\"); // \"\
+        //
+        // "",\\,\"\ (unchanged)
+        tryFormat(list, null, null, "\"\",\\\\,\\\"\\");
+        //
+        // """""",\\,"\""\" (quoted, and embedded DQ doubled)
+        tryFormat(list, '"',  null, "\"\"\"\"\"\",\\\\,\"\\\"\"\\\"");
+        //
+        // "",\\\\,\\"\\ (escapes escaped, not quoted)
+        tryFormat(list, null, '\\', "\"\",\\\\\\\\,\\\\\"\\\\");
+        //
+        // "\"\"","\\\\","\\\"\\" (quoted, and embedded DQ & escape escaped)
+        tryFormat(list, '"',  '\\', "\"\\\"\\\"\",\"\\\\\\\\\",\"\\\\\\\"\\\\\"");
+        //
+        // """""",\\,"\""\" (quoted, embedded DQ escaped)
+        tryFormat(list, '"',  '"',  "\"\"\"\"\"\",\\\\,\"\\\"\"\\\"");
     }
 
     @Test
@@ -772,7 +782,8 @@
     @Test
     public void testMySqlNullOutput() throws IOException {
         Object[] s = new String[] { "NULL", null };
-        CSVFormat format = CSVFormat.MYSQL.withQuote(DQUOTE_CHAR).withNullString("NULL").withQuoteMode(QuoteMode.NON_NUMERIC);
+        CSVFormat format = CSVFormat.MYSQL.withQuote(DQUOTE_CHAR).withNullString("NULL")
+            .withQuoteMode(QuoteMode.NON_NUMERIC);
         StringWriter writer = new StringWriter();
         try (final CSVPrinter printer = new CSVPrinter(writer, format)) {
             printer.printRecord(s);
@@ -1519,10 +1530,10 @@
         return CSVParser.parse(expected, format).getRecords().get(0).values();
     }
 
-    private void tryFormat(List<String> l, Character quote, Character escape, String expected) throws IOException {
-        CSVFormat format = CSVFormat.DEFAULT.withQuote(quote).withEscape(escape).withRecordSeparator(null);
-        Appendable out = new StringBuilder();
-        CSVPrinter printer = new CSVPrinter(out, format);
+    private void tryFormat(final List<String> l, final Character quote, final Character escape, final String expected) throws IOException {
+        final CSVFormat format = CSVFormat.DEFAULT.withQuote(quote).withEscape(escape).withRecordSeparator(null);
+        final Appendable out = new StringBuilder();
+        final CSVPrinter printer = new CSVPrinter(out, format);
         printer.printRecord(l);
         printer.close();
         assertEquals(expected, out.toString());
diff --git a/src/test/java/org/apache/commons/csv/CSVRecordTest.java b/src/test/java/org/apache/commons/csv/CSVRecordTest.java
index 06e771c..d66acfa 100644
--- a/src/test/java/org/apache/commons/csv/CSVRecordTest.java
+++ b/src/test/java/org/apache/commons/csv/CSVRecordTest.java
@@ -23,10 +23,15 @@
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
@@ -41,9 +46,9 @@
         UNKNOWN_COLUMN
     }
 
-    private String[] values;
-    private CSVRecord record, recordWithHeader;
     private Map<String, Integer> headerMap;
+    private CSVRecord record, recordWithHeader;
+    private String[] values;
 
     @BeforeEach
     public void setUp() throws Exception {
@@ -90,6 +95,11 @@
     }
 
     @Test
+    public void testGetNullEnum() {
+        assertThrows(IllegalArgumentException.class, () -> recordWithHeader.get((Enum<?>) null));
+    }
+
+    @Test
     public void testGetUnmappedName() {
         assertThrows(IllegalArgumentException.class, () -> assertNull(recordWithHeader.get("fourth")));
     }
@@ -134,13 +144,6 @@
     }
 
     @Test
-    public void testIsSetString() {
-        assertFalse(record.isSet("first"));
-        assertTrue(recordWithHeader.isSet("first"));
-        assertFalse(recordWithHeader.isSet("fourth"));
-    }
-
-    @Test
     public void testIsSetInt() {
         assertFalse(record.isSet(-1));
         assertTrue(record.isSet(0));
@@ -151,6 +154,13 @@
     }
 
     @Test
+    public void testIsSetString() {
+        assertFalse(record.isSet("first"));
+        assertTrue(recordWithHeader.isSet("first"));
+        assertFalse(recordWithHeader.isSet("fourth"));
+    }
+
+    @Test
     public void testIterator() {
         int i = 0;
         for (final String value : record) {
@@ -185,17 +195,47 @@
     }
 
     @Test
-    public void testToMap() {
-        final Map<String, String> map = this.recordWithHeader.toMap();
-        this.validateMap(map, true);
+    public void testSerialization() throws IOException, ClassNotFoundException {
+        CSVRecord shortRec;
+        try (final CSVParser parser = CSVParser.parse("A,B\n#my comment\nOne,Two", CSVFormat.DEFAULT.withHeader().withCommentMarker('#'))) {
+            shortRec = parser.iterator().next();
+        }
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        try (ObjectOutputStream oos = new ObjectOutputStream(out)) {
+            oos.writeObject(shortRec);
+        }
+        final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+        try (ObjectInputStream ois = new ObjectInputStream(in)) {
+            final Object object = ois.readObject();
+            assertTrue(object instanceof CSVRecord);
+            final CSVRecord rec = (CSVRecord) object;
+            assertEquals(1L, rec.getRecordNumber());
+            assertEquals("One", rec.get(0));
+            assertEquals("Two", rec.get(1));
+            assertEquals(2, rec.size());
+            assertEquals(shortRec.getCharacterPosition(), rec.getCharacterPosition());
+            assertEquals("my comment", rec.getComment());
+            // The parser is not serialized
+            assertNull(rec.getParser());
+            // Check all header map functionality is absent
+            assertTrue(rec.isConsistent());
+            assertFalse(rec.isMapped("A"));
+            assertFalse(rec.isSet("A"));
+            assertEquals(0, rec.toMap().size());
+            // This will throw
+            try {
+                rec.get("A");
+                org.junit.jupiter.api.Assertions.fail("Access by name is not expected after deserialisation");
+            } catch (final IllegalStateException expected) {
+                // OK
+            }
+        }
     }
 
     @Test
-    public void testToMapWithShortRecord() throws Exception {
-        try (final CSVParser parser = CSVParser.parse("a,b", CSVFormat.DEFAULT.withHeader("A", "B", "C"))) {
-            final CSVRecord shortRec = parser.iterator().next();
-            shortRec.toMap();
-        }
+    public void testToMap() {
+        final Map<String, String> map = this.recordWithHeader.toMap();
+        this.validateMap(map, true);
     }
 
     @Test
@@ -208,6 +248,14 @@
         }
     }
 
+    @Test
+    public void testToMapWithShortRecord() throws Exception {
+        try (final CSVParser parser = CSVParser.parse("a,b", CSVFormat.DEFAULT.withHeader("A", "B", "C"))) {
+            final CSVRecord shortRec = parser.iterator().next();
+            shortRec.toMap();
+        }
+    }
+
     private void validateMap(final Map<String, String> map, final boolean allowsNulls) {
         assertTrue(map.containsKey("first"));
         assertTrue(map.containsKey("second"));
diff --git a/src/test/java/org/apache/commons/csv/issues/JiraCsv213Test.java b/src/test/java/org/apache/commons/csv/issues/JiraCsv213Test.java
index 88dc70b..b612b62 100644
--- a/src/test/java/org/apache/commons/csv/issues/JiraCsv213Test.java
+++ b/src/test/java/org/apache/commons/csv/issues/JiraCsv213Test.java
@@ -24,7 +24,6 @@
 
 import org.apache.commons.csv.CSVFormat;
 import org.apache.commons.csv.CSVParser;
-import org.apache.commons.csv.CSVRecord;
 import org.apache.commons.csv.QuoteMode;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
diff --git a/src/test/java/org/apache/commons/csv/issues/JiraCsv248Test.java b/src/test/java/org/apache/commons/csv/issues/JiraCsv248Test.java
new file mode 100644
index 0000000..bf640b6
--- /dev/null
+++ b/src/test/java/org/apache/commons/csv/issues/JiraCsv248Test.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.csv.issues;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.commons.csv.CSVRecord;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+
+public class JiraCsv248Test {
+    /**
+     * Test deserialisation of a CSVRecord created using version 1.6.
+     *
+     * <p>This test asserts that serialization from 1.8 onwards is consistent with
+     * previous versions. Serialization was broken in version 1.7.
+     *
+     * @throws IOException Signals that an I/O exception has occurred.
+     * @throws ClassNotFoundException If the CSVRecord cannot be deserialized
+     */
+    @Test
+    public void testJiraCsv248() throws IOException, ClassNotFoundException {
+        // Record was originally created using CSV version 1.6 with the following code:
+        //try (final CSVParser parser = CSVParser.parse("A,B\n#my comment\nOne,Two", CSVFormat.DEFAULT.withHeader().withCommentMarker('#'))) {
+        //    CSVRecord rec = parser.iterator().next();
+        //}
+        try (InputStream in = getTestInput();
+            ObjectInputStream ois = new ObjectInputStream(in)) {
+            final Object object = ois.readObject();
+            assertTrue(object instanceof CSVRecord);
+            final CSVRecord rec = (CSVRecord) object;
+            assertEquals(1L, rec.getRecordNumber());
+            assertEquals("One", rec.get(0));
+            assertEquals("Two", rec.get(1));
+            assertEquals(2, rec.size());
+            // The comment and whitespace are ignored so this is not 17 but 4
+            assertEquals(4, rec.getCharacterPosition());
+            assertEquals("my comment", rec.getComment());
+            // The parser is not serialized
+            assertNull(rec.getParser());
+            // Check all header map functionality is absent
+            assertTrue(rec.isConsistent());
+            assertFalse(rec.isMapped("A"));
+            assertFalse(rec.isSet("A"));
+            assertEquals(0, rec.toMap().size());
+            // This will throw
+            try {
+                rec.get("A");
+                org.junit.jupiter.api.Assertions.fail("Access by name is not expected after deserialisation");
+            } catch (final IllegalStateException expected) {
+                // OK
+            }
+        }
+    }
+
+    private static InputStream getTestInput() {
+        return ClassLoader.getSystemClassLoader().getResourceAsStream("CSV-248/csvRecord.bin");
+    }
+}
diff --git a/src/test/java/org/apache/commons/csv/perf/PerformanceTest.java b/src/test/java/org/apache/commons/csv/perf/PerformanceTest.java
index d35f598..3761325 100644
--- a/src/test/java/org/apache/commons/csv/perf/PerformanceTest.java
+++ b/src/test/java/org/apache/commons/csv/perf/PerformanceTest.java
@@ -38,7 +38,7 @@
 /**
  * Tests performance.
  *
- * To run this test, use: mvn test -Dtest=PeformanceTest
+ * To run this test, use: mvn test -Dtest=PerformanceTest
  */
 @SuppressWarnings("boxing") // test code
 public class PerformanceTest {
@@ -112,12 +112,11 @@
     @Test
     public void testReadBigFile() throws Exception {
         long bestTime = Long.MAX_VALUE;
+        long count;
         for (int i = 0; i < this.max; i++) {
             final long startMillis;
-            long count;
             try (final BufferedReader in = this.createBufferedReader()) {
                 startMillis = System.currentTimeMillis();
-                count = 0;
                 count = this.readAll(in);
             }
             final long totalMillis = System.currentTimeMillis() - startMillis;
diff --git a/src/test/resources/CSV-248/csvRecord.bin b/src/test/resources/CSV-248/csvRecord.bin
new file mode 100644
index 0000000..36047d5
--- /dev/null
+++ b/src/test/resources/CSV-248/csvRecord.bin
Binary files differ