You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2020/01/21 13:00:06 UTC

[commons-csv] branch master updated (7c5c089 -> 12a2ff4)

This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/commons-csv.git.


    from 7c5c089  Let a null input to CSVRecord#get(Enum) fail in CSVRecord#get(String).
     new 8d6772a  [CSV-248] Test the parser and map functionality after deserialization
     new 66c34da  [CSV-248] Test CSVRecord deserialization from binary format.
     new bd17fc3  Remove trailing whitespace.
     new 12a2ff4  Document that the list of header names will not contain null names.

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../java/org/apache/commons/csv/CSVParser.java     |  11 +++
 .../java/org/apache/commons/csv/CSVRecord.java     |  13 +++-
 src/site/xdoc/user-guide.xml                       |   2 +-
 .../java/org/apache/commons/csv/CSVBenchmark.java  |  18 ++---
 .../java/org/apache/commons/csv/CSVParserTest.java |  14 ++++
 .../java/org/apache/commons/csv/CSVRecordTest.java |  35 ++++++++-
 .../apache/commons/csv/issues/JiraCsv248Test.java  |  80 +++++++++++++++++++++
 src/test/resources/CSV-248/csvRecord.bin           | Bin 0 -> 485 bytes
 8 files changed, 159 insertions(+), 14 deletions(-)
 create mode 100644 src/test/java/org/apache/commons/csv/issues/JiraCsv248Test.java
 create mode 100644 src/test/resources/CSV-248/csvRecord.bin


[commons-csv] 02/04: [CSV-248] Test CSVRecord deserialization from binary format.

Posted by ah...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-csv.git

commit 66c34da258f068046536d84e712a90a5f7f6e2a0
Author: aherbert <ah...@apache.org>
AuthorDate: Tue Jan 21 12:42:58 2020 +0000

    [CSV-248] Test CSVRecord deserialization from binary format.
    
    The serialised form was created using version 1.6.
---
 .../java/org/apache/commons/csv/CSVRecordTest.java |  17 -----
 .../apache/commons/csv/issues/JiraCsv248Test.java  |  80 +++++++++++++++++++++
 src/test/resources/CSV-248/csvRecord.bin           | Bin 0 -> 485 bytes
 3 files changed, 80 insertions(+), 17 deletions(-)

diff --git a/src/test/java/org/apache/commons/csv/CSVRecordTest.java b/src/test/java/org/apache/commons/csv/CSVRecordTest.java
index 8d11d53..d6fedb0 100644
--- a/src/test/java/org/apache/commons/csv/CSVRecordTest.java
+++ b/src/test/java/org/apache/commons/csv/CSVRecordTest.java
@@ -232,23 +232,6 @@ public class CSVRecordTest {
         }
     }
 
-    /**
-     * Test deserialisation of a record created using version 1.6.
-     *
-     * @throws IOException Signals that an I/O exception has occurred.
-     */
-    @Test
-    public void testDeserialisation() throws IOException {
-        CSVRecord shortRec;
-        try (final CSVParser parser = CSVParser.parse("A,B\n#my comment\nOne,Two", CSVFormat.DEFAULT.withHeader().withCommentMarker('#'))) {
-            shortRec = parser.iterator().next();
-        }
-        try (FileOutputStream out = new FileOutputStream("/tmp/csvRecord.ser");
-            ObjectOutputStream oos = new ObjectOutputStream(out)) {
-            oos.writeObject(shortRec);
-        }
-    }
-
     @Test
     public void testToMap() {
         final Map<String, String> map = this.recordWithHeader.toMap();
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..c39842f
--- /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 create 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 (IllegalStateException expected) {
+                // OK
+            }
+        }
+    }
+
+    private static InputStream getTestInput() {
+        return ClassLoader.getSystemClassLoader().getResourceAsStream("CSV-248/csvRecord.bin");
+    }
+}
\ No newline at end of file
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
Binary files /dev/null and b/src/test/resources/CSV-248/csvRecord.bin differ


[commons-csv] 03/04: Remove trailing whitespace.

Posted by ah...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-csv.git

commit bd17fc385057980ca90c28473f4c2e888de66d9d
Author: aherbert <ah...@apache.org>
AuthorDate: Tue Jan 21 12:43:30 2020 +0000

    Remove trailing whitespace.
---
 src/main/java/org/apache/commons/csv/CSVRecord.java    |  2 +-
 src/site/xdoc/user-guide.xml                           |  2 +-
 src/test/java/org/apache/commons/csv/CSVBenchmark.java | 18 +++++++++---------
 .../java/org/apache/commons/csv/CSVRecordTest.java     |  2 +-
 4 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/main/java/org/apache/commons/csv/CSVRecord.java b/src/main/java/org/apache/commons/csv/CSVRecord.java
index e32cd5a..a8cad90 100644
--- a/src/main/java/org/apache/commons/csv/CSVRecord.java
+++ b/src/main/java/org/apache/commons/csv/CSVRecord.java
@@ -151,7 +151,7 @@ public final class CSVRecord implements Serializable, Iterable<String> {
      * 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. 
+     * should be used when the record may have originated from a serialized form.
      *
      * @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 @@ limitations under the License.
 
       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 @@ public class CSVBenchmark {
         while ((line = in.readLine()) != null) {
             count++;
         }
-        
+
         bh.consume(count);
         in.close();
         return count;
@@ -94,7 +94,7 @@ public class CSVBenchmark {
             final String[] values = StringUtils.split(line, ',');
             count += values.length;
         }
-        
+
         bh.consume(count);
         in.close();
         return count;
@@ -103,7 +103,7 @@ public class CSVBenchmark {
     @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 @@ public class CSVBenchmark {
     @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 @@ public class CSVBenchmark {
     @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 @@ public class CSVBenchmark {
     @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 @@ public class CSVBenchmark {
     @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 @@ public class CSVBenchmark {
     @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/CSVRecordTest.java b/src/test/java/org/apache/commons/csv/CSVRecordTest.java
index d6fedb0..8ed51c3 100644
--- a/src/test/java/org/apache/commons/csv/CSVRecordTest.java
+++ b/src/test/java/org/apache/commons/csv/CSVRecordTest.java
@@ -63,7 +63,7 @@ public class CSVRecordTest {
             headerMap = parser.getHeaderMap();
         }
     }
-    
+
     @Test
     public void testGetInt() {
         assertEquals(values[0], record.get(0));


[commons-csv] 04/04: Document that the list of header names will not contain null names.

Posted by ah...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-csv.git

commit 12a2ff42d3c39cf3a33cb07bb57ede81656a02a4
Author: aherbert <ah...@apache.org>
AuthorDate: Tue Jan 21 13:00:00 2020 +0000

    Document that the list of header names will not contain null names.
    
    Added a test to demonstrate missing null headers from the list.
---
 src/main/java/org/apache/commons/csv/CSVParser.java     | 11 +++++++++++
 src/test/java/org/apache/commons/csv/CSVParserTest.java | 14 ++++++++++++++
 2 files changed, 25 insertions(+)

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 @@ public final class CSVParser implements Iterable<CSVRecord>, Closeable {
      * <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 @@ public final class CSVParser implements Iterable<CSVRecord>, Closeable {
 
     /**
      * 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/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 @@ public class CSVParserTest {
     }
 
     @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()


Re: [commons-csv] 01/04: [CSV-248] Test the parser and map functionality after deserialization

Posted by Alex Herbert <al...@gmail.com>.
On 21/01/2020 15:35, Gary Gregory wrote:
>>
>>
>> diff --git a/src/main/java/org/apache/commons/csv/CSVRecord.java
>> b/src/main/java/org/apache/commons/csv/CSVRecord.java
>> index 471e94d..e32cd5a 100644
>> --- a/src/main/java/org/apache/commons/csv/CSVRecord.java
>> +++ b/src/main/java/org/apache/commons/csv/CSVRecord.java
>> @@ -83,6 +83,12 @@ public final class CSVRecord implements Serializable,
>> Iterable<String> {
>>       /**
>>        * 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 provide {@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.
>> +     *
>>        * @param name
>>
>>
> Please close all HTML tags.

Done in a later commit.

On 21/01/2020 15:37, Gary Gregory wrote:
>> +    @Test
>> +    public void testDeserialisation() throws IOException {
>> +        CSVRecord shortRec;
>> +        try (final CSVParser parser = CSVParser.parse("A,B\n#my
>> comment\nOne,Two", CSVFormat.DEFAULT.withHeader().withCommentMarker('#'))) {
>> +            shortRec = parser.iterator().next();
>> +        }
>> +        try (FileOutputStream out = new
>> FileOutputStream("/tmp/csvRecord.ser");
>> +            ObjectOutputStream oos = new ObjectOutputStream(out)) {
>> +            oos.writeObject(shortRec);
>> +        }
>>       }
>>
>   This can't be right: "/tmp/csvRecord.ser"

It wasn't meant to be there. It is how to create the serialisation 
binary file. I wrote it in version 1.8 then took the same code to 1.6 
and forgot to remove it from 1.8.

A later one removed this. When I noticed it in the commit summary I had 
already pushed and it was too late to rebase and fix up the commit history.

>
> @@ -77,4 +77,4 @@ public class JiraCsv248Test {
>       private static InputStream getTestInput() {
>           return
> ClassLoader.getSystemClassLoader().getResourceAsStream("CSV-248/csvRecord.bin");
>       }
> -}
> \ No newline at end of file
> +}
This has been fixed.

2) Should we have one file and test per version 1.7 back to 1.0?

You cannot test version 1.7 as that could not serialize the CSVRecord.

I tried 1.6 as that was the most recent. I suppose should have used 1.0. Any users wanting to use an intermediate release and find it to be broken for serialisation can upgrade to 1.8 where it should be fixed.

I've just generated the data using the code under tag CSV_1.0 which requires pre-1.7 code (not try with resources):

@Test
public void testDeserialisation() throws IOException {
     CSVRecord shortRec;
     CSVParser parser = CSVParser.parse("A,B\n#my comment\nOne,Two", CSVFormat.DEFAULT.withHeader().withCommentMarker('#'));
     shortRec = parser.iterator().next();
     parser.close();
     FileOutputStream out = new FileOutputStream("/tmp/csvRecord.ser");
     ObjectOutputStream oos = new ObjectOutputStream(out);
     oos.writeObject(shortRec);
     oos.close();
     out.close();
  }

The file is binary different from the current file. The test passes. Shall I commit the old serialised version?

Alex



Re: [commons-csv] 01/04: [CSV-248] Test the parser and map functionality after deserialization

Posted by Gary Gregory <ga...@gmail.com>.
>
>
>
> diff --git a/src/main/java/org/apache/commons/csv/CSVRecord.java
> b/src/main/java/org/apache/commons/csv/CSVRecord.java
> index 471e94d..e32cd5a 100644
> --- a/src/main/java/org/apache/commons/csv/CSVRecord.java
> +++ b/src/main/java/org/apache/commons/csv/CSVRecord.java
> @@ -83,6 +83,12 @@ public final class CSVRecord implements Serializable,
> Iterable<String> {
>      /**
>       * 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 provide {@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.
> +     *
>       * @param name
>
>
Please close all HTML tags.

Gary

Re: [commons-csv] 01/04: [CSV-248] Test the parser and map functionality after deserialization

Posted by Gary Gregory <ga...@gmail.com>.
>
>
> +    @Test
> +    public void testDeserialisation() throws IOException {
> +        CSVRecord shortRec;
> +        try (final CSVParser parser = CSVParser.parse("A,B\n#my
> comment\nOne,Two", CSVFormat.DEFAULT.withHeader().withCommentMarker('#'))) {
> +            shortRec = parser.iterator().next();
> +        }
> +        try (FileOutputStream out = new
> FileOutputStream("/tmp/csvRecord.ser");
> +            ObjectOutputStream oos = new ObjectOutputStream(out)) {
> +            oos.writeObject(shortRec);
> +        }
>      }
>

 This can't be right: "/tmp/csvRecord.ser"

Gary

[commons-csv] 01/04: [CSV-248] Test the parser and map functionality after deserialization

Posted by ah...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-csv.git

commit 8d6772a3209b124af7e2430f69cdf6a7c765fd44
Author: aherbert <ah...@apache.org>
AuthorDate: Tue Jan 21 12:22:48 2020 +0000

    [CSV-248] Test the parser and map functionality after deserialization
    
    Methods with unexpected return values (null or exceptions) have been
    documented. All other methods will just fail as if the record came from
    a parser without a header.
---
 .../java/org/apache/commons/csv/CSVRecord.java     | 13 +++++-
 .../java/org/apache/commons/csv/CSVRecordTest.java | 50 +++++++++++++++++++++-
 2 files changed, 60 insertions(+), 3 deletions(-)

diff --git a/src/main/java/org/apache/commons/csv/CSVRecord.java b/src/main/java/org/apache/commons/csv/CSVRecord.java
index 471e94d..e32cd5a 100644
--- a/src/main/java/org/apache/commons/csv/CSVRecord.java
+++ b/src/main/java/org/apache/commons/csv/CSVRecord.java
@@ -83,6 +83,12 @@ public final class CSVRecord implements Serializable, Iterable<String> {
     /**
      * 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 provide {@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.
+     *
      * @param name
      *            the name of the column to be retrieved.
      * @return the column value, maybe null depending on {@link CSVFormat#getNullString()}.
@@ -90,7 +96,9 @@ public final class CSVRecord implements Serializable, Iterable<String> {
      *             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) {
@@ -136,12 +144,15 @@ public final class CSVRecord implements Serializable, Iterable<String> {
     }
 
     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. 
+     *
      * @return the parser.
      * @since 1.7
      */
diff --git a/src/test/java/org/apache/commons/csv/CSVRecordTest.java b/src/test/java/org/apache/commons/csv/CSVRecordTest.java
index 476bac2..8d11d53 100644
--- a/src/test/java/org/apache/commons/csv/CSVRecordTest.java
+++ b/src/test/java/org/apache/commons/csv/CSVRecordTest.java
@@ -23,12 +23,15 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 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;
@@ -192,15 +195,58 @@ public class CSVRecordTest {
     }
 
     @Test
-    public void testSerialization() throws IOException {
+    public void testSerialization() throws IOException, ClassNotFoundException {
         CSVRecord shortRec;
-        try (final CSVParser parser = CSVParser.parse("a,b", CSVFormat.newFormat(','))) {
+        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 (IllegalStateException expected) {
+                // OK
+            }
+        }
+    }
+
+    /**
+     * Test deserialisation of a record created using version 1.6.
+     *
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    @Test
+    public void testDeserialisation() throws IOException {
+        CSVRecord shortRec;
+        try (final CSVParser parser = CSVParser.parse("A,B\n#my comment\nOne,Two", CSVFormat.DEFAULT.withHeader().withCommentMarker('#'))) {
+            shortRec = parser.iterator().next();
+        }
+        try (FileOutputStream out = new FileOutputStream("/tmp/csvRecord.ser");
+            ObjectOutputStream oos = new ObjectOutputStream(out)) {
+            oos.writeObject(shortRec);
+        }
     }
 
     @Test