From 8d6772a3209b124af7e2430f69cdf6a7c765fd44 Mon Sep 17 00:00:00 2001 From: aherbert Date: Tue, 21 Jan 2020 12:22:48 +0000 Subject: [PATCH] [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. --- .../org/apache/commons/csv/CSVRecord.java | 13 ++++- .../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 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