Skip to content
This repository was archived by the owner on Jan 22, 2019. It is now read-only.

Commit b437278

Browse files
committed
Merge pull request #97 from nickbabcock/header-verification
Verify CSV headers are in the order as expected
2 parents 1617ee9 + 7894c20 commit b437278

File tree

3 files changed

+125
-2
lines changed

3 files changed

+125
-2
lines changed

src/main/java/com/fasterxml/jackson/dataformat/csv/CsvParser.java

+18-2
Original file line numberDiff line numberDiff line change
@@ -838,8 +838,24 @@ protected void _readHeaderLine() throws IOException {
838838
*/
839839

840840
if (_schema.size() > 0 && !_schema.reordersColumns()) {
841-
//noinspection StatementWithEmptyBody
842-
while (_reader.nextString() != null) { /* does nothing */ }
841+
if (_schema.strictHeaders()) {
842+
String name;
843+
for (CsvSchema.Column column : _schema._columns) {
844+
name = _reader.nextString();
845+
if (name == null) {
846+
_reportError(String.format("Missing header %s", column.getName()));
847+
} else if (!column.getName().equals(name)) {
848+
_reportError(String.format("Expected header %s, actual header %s", column.getName(), name));
849+
}
850+
}
851+
if ((name = _reader.nextString()) != null) {
852+
_reportError(String.format("Extra header %s", name));
853+
}
854+
}
855+
else {
856+
//noinspection StatementWithEmptyBody
857+
while (_reader.nextString() != null) { /* does nothing */ }
858+
}
843859
return;
844860
}
845861

src/main/java/com/fasterxml/jackson/dataformat/csv/CsvSchema.java

+31
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ public class CsvSchema
8383
protected final static int ENCODING_FEATURE_SKIP_FIRST_DATA_ROW = 0x0002;
8484
protected final static int ENCODING_FEATURE_ALLOW_COMMENTS = 0x0004;
8585
protected final static int ENCODING_FEATURE_REORDER_COLUMNS = 0x0008;
86+
protected final static int ENCODING_FEATURE_STRICT_HEADERS = 0x0016;
8687

8788
protected final static int DEFAULT_ENCODING_FEATURES = 0;
8889

@@ -471,6 +472,22 @@ public Builder setReorderColumns(boolean b) {
471472
return this;
472473
}
473474

475+
476+
/**
477+
* Use in combination with setUseHeader. When use header flag is
478+
* is set, this setting will ensure the headers are in the order
479+
* of the schema
480+
*
481+
* @param b Enable / Disable this setting
482+
* @return This Builder instance
483+
*
484+
* @since 2.7
485+
*/
486+
public Builder setStrictHeaders(boolean b) {
487+
_feature(ENCODING_FEATURE_STRICT_HEADERS, b);
488+
return this;
489+
}
490+
474491
/**
475492
* Method for specifying whether Schema should indicate that
476493
* the first line that is not a header (if header handling enabled)
@@ -804,6 +821,19 @@ public CsvSchema withColumnReordering(boolean state) {
804821
return _withFeature(ENCODING_FEATURE_REORDER_COLUMNS, state);
805822
}
806823

824+
/**
825+
* Returns a clone of this instance by changing or setting the
826+
* strict headers flag
827+
*
828+
* @param state New value for setting
829+
* @return A copy of itself, ensuring the setting for
830+
* the strict headers feature.
831+
* @since 2.7
832+
*/
833+
public CsvSchema withStrictColumns(boolean state) {
834+
return _withFeature(ENCODING_FEATURE_STRICT_HEADERS, state);
835+
}
836+
807837
/**
808838
* Helper method for constructing and returning schema instance that
809839
* is similar to this one, except that it will be using header line.
@@ -1001,6 +1031,7 @@ public String getSchemaType() {
10011031
public boolean reordersColumns() { return (_features & ENCODING_FEATURE_REORDER_COLUMNS) != 0; }
10021032
public boolean skipsFirstDataRow() { return (_features & ENCODING_FEATURE_SKIP_FIRST_DATA_ROW) != 0; }
10031033
public boolean allowsComments() { return (_features & ENCODING_FEATURE_ALLOW_COMMENTS) != 0; }
1034+
public boolean strictHeaders() { return (_features & ENCODING_FEATURE_STRICT_HEADERS) != 0; }
10041035

10051036
/**
10061037
* @deprecated Use {@link #usesHeader()} instead

src/test/java/com/fasterxml/jackson/dataformat/csv/deser/BasicParserTest.java

+76
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.*;
66

77
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
8+
import com.fasterxml.jackson.core.JsonProcessingException;
89
import com.fasterxml.jackson.core.JsonToken;
910
import com.fasterxml.jackson.databind.*;
1011
import com.fasterxml.jackson.dataformat.csv.*;
@@ -328,4 +329,79 @@ public void testColumnReordering() throws IOException {
328329
assertEquals(JsonToken.END_OBJECT, parser.nextToken());
329330
parser.close();
330331
}
332+
333+
public void testColumnFailsOnOutOfOrder() throws IOException {
334+
CsvFactory factory = new CsvFactory();
335+
String CSV = "b,a,c\nvb,va,vc\n";
336+
337+
CsvSchema schema = CsvSchema.builder()
338+
.addColumn("a")
339+
.addColumn("b")
340+
.addColumn("c")
341+
.setLineSeparator('\n')
342+
.setUseHeader(true)
343+
.setStrictHeaders(true)
344+
.build();
345+
346+
CsvParser parser = factory.createParser(CSV);
347+
parser.setSchema(schema);
348+
349+
try {
350+
parser.nextToken();
351+
fail("Should have failed");
352+
} catch (JsonProcessingException e) {
353+
verifyException(e, "Expected header a, actual header b");
354+
}
355+
parser.close();
356+
}
357+
358+
public void testColumnFailsOnTooFew() throws IOException {
359+
CsvFactory factory = new CsvFactory();
360+
String CSV = "a,b\nvb,va,vc\n";
361+
362+
CsvSchema schema = CsvSchema.builder()
363+
.addColumn("a")
364+
.addColumn("b")
365+
.addColumn("c")
366+
.setLineSeparator('\n')
367+
.setUseHeader(true)
368+
.setStrictHeaders(true)
369+
.build();
370+
371+
CsvParser parser = factory.createParser(CSV);
372+
parser.setSchema(schema);
373+
374+
try {
375+
parser.nextToken();
376+
fail("Should have failed");
377+
} catch (JsonProcessingException e) {
378+
verifyException(e, "Missing header c");
379+
}
380+
parser.close();
381+
}
382+
383+
public void testColumnFailsOnTooMany() throws IOException {
384+
CsvFactory factory = new CsvFactory();
385+
String CSV = "a,b,c,d\nvb,va,vc\n";
386+
387+
CsvSchema schema = CsvSchema.builder()
388+
.addColumn("a")
389+
.addColumn("b")
390+
.addColumn("c")
391+
.setLineSeparator('\n')
392+
.setUseHeader(true)
393+
.setStrictHeaders(true)
394+
.build();
395+
396+
CsvParser parser = factory.createParser(CSV);
397+
parser.setSchema(schema);
398+
399+
try {
400+
parser.nextToken();
401+
fail("Should have failed");
402+
} catch (JsonProcessingException e) {
403+
verifyException(e, "Extra header d");
404+
}
405+
parser.close();
406+
}
331407
}

0 commit comments

Comments
 (0)