diff --git a/modules/csv/src/main/java/io/oasp/module/basic/csv/CsvFormat.java b/modules/csv/src/main/java/io/oasp/module/basic/csv/CsvFormat.java
index 1f62e22da..b6164f5c6 100644
--- a/modules/csv/src/main/java/io/oasp/module/basic/csv/CsvFormat.java
+++ b/modules/csv/src/main/java/io/oasp/module/basic/csv/CsvFormat.java
@@ -14,7 +14,10 @@
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
import java.util.Arrays;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -28,12 +31,17 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.PropertyFilter;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
@@ -94,7 +102,7 @@ public class CsvFormat {
/** may be null
*/
protected final String charset;
- protected final DateFormat dateFormat;
+ protected final DateFormat[] dateFormats;
@Deprecated // delete when Pull Request is merged : https://github.com/FasterXML/jackson-dataformats-text/pull/46
private boolean endingLineSeparator = true;
@@ -129,14 +137,14 @@ public CsvFormat() {
*/
@Deprecated // delete when Pull Request is merged : https://github.com/FasterXML/jackson-dataformats-text/pull/46
protected CsvFormat(final List columns, final CsvMapper mapper, final CsvSchema schema, final String charset,
- final DateFormat dateFormat, final boolean endingLineSeparator) {
+ final DateFormat[] dateFormats, final boolean endingLineSeparator) {
Objects.requireNonNull(mapper);
Objects.requireNonNull(schema);
this.columns = columns;
this.mapper = mapper;
this.schema = schema;
this.charset = charset;
- this.dateFormat = dateFormat;
+ this.dateFormats = dateFormats;
this.endingLineSeparator = endingLineSeparator;
}
@@ -160,14 +168,14 @@ protected CsvFormat(final List columns, final CsvMapper mapper, final Cs
*/
@Deprecated // delete when Pull Request is merged : https://github.com/FasterXML/jackson-dataformats-text/pull/46
public CsvFormat(CsvFormat base, CsvSchema newSchema, final boolean endingLineSeparator) {
- this(base.columns, base.mapper, newSchema, base.charset, base.dateFormat, endingLineSeparator);
+ this(base.columns, base.mapper, newSchema, base.charset, base.dateFormats, endingLineSeparator);
}
/**
* @param columns
* @return
*/
- public static CsvMapper buildMapper(final List columns, final DateFormat dateFormat) {
+ public static CsvMapper buildMapper(final List columns, final DateFormat[] dateFormats) {
final CsvMapper mapper = new CsvMapper();
@@ -180,14 +188,59 @@ public static CsvMapper buildMapper(final List columns, final DateFormat
}
// mapper.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true);
- if (dateFormat != null) {
- mapper.setDateFormat(dateFormat);
+ if (dateFormats != null && dateFormats.length > 0) {
+
+ // Multiple formats ?
+ if (dateFormats.length > 1) {
+ SimpleModule module = new SimpleModule();
+ module.addDeserializer(Date.class, new MultiDateDeserializer(dateFormats)); // TODO : withDateFormats
+ mapper.registerModule(module);
+ }
+
+ // We also set default date format which is required for writing
+ mapper.setDateFormat(dateFormats[0]);
}
+
// mapper.configure(Feature.STRICT_CHECK_FOR_QUOTING, true);
return mapper;
}
+ /**
+ * Handle multiple DateFormat
+ *
+ * @author MLAVIGNE
+ */
+ public static final class MultiDateDeserializer extends StdDeserializer {
+
+ private static final long serialVersionUID = 837636597643725045L;
+
+ private DateFormat[] dateFormats;
+
+ /**
+ * The constructor.
+ */
+ public MultiDateDeserializer(DateFormat[] dateFormats) {
+ super(Date.class);
+ this.dateFormats = dateFormats;
+ }
+
+ @Override
+ public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+
+ final String date = jp.getText();
+ for (DateFormat dateFormat : this.dateFormats) {
+ try {
+ return dateFormat.parse(date);
+ } catch (ParseException e) {
+ // TODO : check associated regex instead of throwing/catching Exception
+ }
+ }
+ throw new JsonParseException(jp,
+ String.format("Unparseable date: \"%s\". Supported formats: %s", date, Arrays.toString(this.dateFormats)));
+ }
+ }
+
/**
* @param columns if null
create an {@link CsvSchema#emptySchema() emptySchema} with header
* @return
@@ -290,7 +343,7 @@ public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider
*/
public CsvFormat withColumns(final List columns) {
- final CsvMapper mapper = buildMapper(columns, this.dateFormat);
+ final CsvMapper mapper = buildMapper(columns, this.dateFormats);
CsvSchema schema = buildSchema(columns) // TODO : use copy constructor
.withColumnSeparator(this.schema.getColumnSeparator())
.withLineSeparator(new String(this.schema.getLineSeparator())).withQuoteChar((char) this.schema.getQuoteChar())
@@ -306,7 +359,7 @@ public CsvFormat withColumns(final List columns) {
* schema.withoutEndingLineSeparator(); }
*/
// FIXME keep header
- return new CsvFormat(columns, mapper, schema, this.charset, this.dateFormat, this.endingLineSeparator);
+ return new CsvFormat(columns, mapper, schema, this.charset, this.dateFormats, this.endingLineSeparator);
}
/**
@@ -406,7 +459,7 @@ public CsvFormat withoutQuoteChar() {
public CsvFormat withCharset(final String charset) {
- return new CsvFormat(this.columns, this.mapper, this.schema, charset, this.dateFormat, this.endingLineSeparator);
+ return new CsvFormat(this.columns, this.mapper, this.schema, charset, this.dateFormats, this.endingLineSeparator);
}
public CsvFormat withEncoding(final String encoding) {
@@ -415,12 +468,47 @@ public CsvFormat withEncoding(final String encoding) {
}
/**
- * @param simpleDateFormat
- * @return
+ * @param dateFormat example : {@link SimpleDateFormat}
+ * @return this
*/
public CsvFormat withDateFormat(DateFormat dateFormat) {
- return new CsvFormat(this.columns, buildMapper(this.columns, dateFormat), this.schema, this.charset, dateFormat,
+ return withDateFormats(dateFormat);
+ }
+
+ /**
+ * @param patterns {@link SimpleDateFormat} pattern
+ * @return this
+ */
+ public CsvFormat withDateFormat(String patterns) {
+
+ return withDateFormats(patterns);
+ }
+
+ /**
+ * Jackson does not allow multiple DateFormats. This method will use a MultiDateDeserializer to allow them.
+ *
+ * @param patterns {@link SimpleDateFormat} patterns
+ * @return this CsvFormat instance
+ */
+ public CsvFormat withDateFormats(String... patterns) {
+
+ final DateFormat[] dateFormats = new DateFormat[patterns.length];
+ for (int i = 0; i < patterns.length; i++) {
+ dateFormats[i] = new SimpleDateFormat(patterns[i]);
+ }
+ return withDateFormats(dateFormats);
+ }
+
+ /**
+ * Jackson does not allow multiple DateFormats. This method will use a MultiDateDeserializer to allow them.
+ *
+ * @param dateFormats example : {@link SimpleDateFormat}
+ * @return this CsvFormat instance
+ */
+ public CsvFormat withDateFormats(DateFormat... dateFormats) {
+
+ return new CsvFormat(this.columns, buildMapper(this.columns, dateFormats), this.schema, this.charset, dateFormats,
this.endingLineSeparator);
}
@@ -430,7 +518,7 @@ public CsvFormat withDateFormat(DateFormat dateFormat) {
*/
public CsvFormat withoutDateFormat() {
- return new CsvFormat(this.columns, buildMapper(this.columns, null), this.schema, this.charset, this.dateFormat,
+ return new CsvFormat(this.columns, buildMapper(this.columns, null), this.schema, this.charset, null,
this.endingLineSeparator);
}
@@ -567,6 +655,9 @@ public Reader getEncodedReader(final String src) throws UnsupportedEncodingExcep
}
/**
+ * Don't forget to annotate your ETO with @JsonFilter(CsvFormat.FILTER)
to avoid
+ * "JsonGenerationException: Unrecognized column" exception.
+ *
* @param out
* @param type
* @return
@@ -585,6 +676,9 @@ public ObjectWriter writerFor(E value) { // TODO save writers in CsvFormat f
* this.writerFor(value).writeValue(this.getWriterForCharset(out), value)
*
*
+ * Don't forget to annotate your ETO with @JsonFilter(CsvFormat.FILTER)
to avoid
+ * "JsonGenerationException: Unrecognized column" exception.
+ *
* @param out
* @param value
* @return
@@ -625,6 +719,9 @@ public Writer getEncodedWriter(OutputStream out) throws UnsupportedEncodingExcep
* this.writerFor(value).writeValue(resultFile, value)
*
*
+ * Don't forget to annotate your ETO with @JsonFilter(CsvFormat.FILTER)
to avoid
+ * "JsonGenerationException: Unrecognized column" exception.
+ *
* @param value
* @return
* @throws IOException
@@ -667,6 +764,9 @@ public Writer getEncodedWriter(final File resultFile) throws IOException {
* this.writerFor(value).writeValueAsString(value)
*
*
+ * Don't forget to annotate your ETO with @JsonFilter(CsvFormat.FILTER)
to avoid
+ * "JsonGenerationException: Unrecognized column" exception.
+ *
* @param value
* @return
* @throws IOException
diff --git a/modules/csv/src/test/java/io/oasp/module/basic/csv/CsvFormatTest.java b/modules/csv/src/test/java/io/oasp/module/basic/csv/CsvFormatTest.java
index 3beaea618..a824bdd70 100644
--- a/modules/csv/src/test/java/io/oasp/module/basic/csv/CsvFormatTest.java
+++ b/modules/csv/src/test/java/io/oasp/module/basic/csv/CsvFormatTest.java
@@ -421,7 +421,7 @@ public void testWithCharset() throws Exception {
* {@link com.orange.grace.traducteur.general.service.impl.rest.CsvFormat#withDateFormat(java.text.DateFormat)}.
*/
@Test
- public void testWithDateFormat() throws Exception {
+ public void testWithDateFormatWrite() throws Exception {
final Eto eto = new Eto();
eto.setComment("comment_value1");
@@ -438,6 +438,67 @@ public void testWithDateFormat() throws Exception {
assertThatWrittenContent(eto, format).isEqualTo(sb.toString());
}
+ /**
+ * Test method for
+ * {@link com.orange.grace.traducteur.general.service.impl.rest.CsvFormat#withDateFormat(java.text.DateFormat)}.
+ */
+ @Test
+ public void testWithDateFormat() throws Exception {
+
+ final CsvFormat format = this.twoColumns.withColumns("code,date").withDateFormats("dd/MM/yyyy");
+
+ assertThat(format.readValue("CODE1,22/05/1987", Eto.class).getDate()).isEqualTo("1987-05-22");
+ }
+
+ /**
+ * Test method for
+ * {@link com.orange.grace.traducteur.general.service.impl.rest.CsvFormat#withDateFormat(java.text.DateFormat)}.
+ */
+ @Test
+ public void testWithDateFormats() throws Exception {
+
+ final CsvFormat format = this.twoColumns.withColumns("code,date").withDateFormats("dd/MM/yyyy", "dd-MM-yyyy");
+
+ assertThat(format.readValue("CODE1,22/05/1987", Eto.class).getDate()).isEqualTo("1987-05-22");
+ assertThat(format.readValue("CODE1,22-05-1987", Eto.class).getDate()).isEqualTo("1987-05-22");
+ }
+
+ /**
+ * Test method for
+ * {@link com.orange.grace.traducteur.general.service.impl.rest.CsvFormat#withDateFormat(java.text.DateFormat)}.
+ */
+ @Test(expected = JsonMappingException.class)
+ public void testWithDateFormatsKO() throws Exception {
+
+ final CsvFormat format = this.twoColumns.withColumns("code,date").withDateFormats("dd/MM/yyyy", "dd-MM-yyyy");
+
+ assertThat(format.readValue("CODE1,22/05/1987", Eto.class).getDate()).isEqualTo("1987-05-22");
+ assertThat(format.readValue("CODE1,22-05-1987", Eto.class).getDate()).isEqualTo("1987-05-22");
+ format.readValue("CODE1,22|05|1987", Eto.class); // unknown format
+ }
+
+ /**
+ * Test method for
+ * {@link com.orange.grace.traducteur.general.service.impl.rest.CsvFormat#withDateFormat(java.text.DateFormat)}.
+ */
+ @Test
+ public void testWithDateFormatsWrite() throws Exception {
+
+ final Eto eto = new Eto();
+ eto.setComment("comment_value1");
+ final Calendar cal = Calendar.getInstance();
+ cal.clear();
+ cal.set(2017, 9, 23);
+ eto.setDate(cal.getTime());
+
+ final CsvFormat format =
+ this.twoColumns.withColumns("code,date").withDateFormats("dd/MM/yyyy", "dd-MM-yyyy");
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append(",23/10/2017").append(defaultLineSeparator);
+ assertThatWrittenContent(eto, format).isEqualTo(sb.toString());
+ }
+
/**
* Test method for {@link com.orange.grace.traducteur.general.service.impl.rest.CsvFormat#withoutDateFormat()}.
*/