diff --git a/src/main/java/org/apache/commons/csv/CSVRecord.java b/src/main/java/org/apache/commons/csv/CSVRecord.java index 471e94db..e32cd5a7 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 { /** * Returns a value by name. * + *

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 { * 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 { } private Map getHeaderMapRaw() { - return parser.getHeaderMapRaw(); + return parser == null ? null : parser.getHeaderMapRaw(); } /** * Returns the parser. * + *

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 476bac22..8d11d538 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