This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push: new 7b36d71 CAMEL-12332: camel-csv - Add support for ordered Map in unmarshal 7b36d71 is described below commit 7b36d71ced0c6afaa63f0b327f11a86e0bd66587 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Wed Mar 7 16:43:35 2018 +0100 CAMEL-12332: camel-csv - Add support for ordered Map in unmarshal --- .../camel/model/dataformat/CsvDataFormat.java | 18 +++++++++- .../camel-csv/src/main/docs/csv-dataformat.adoc | 5 +-- .../apache/camel/dataformat/csv/CsvDataFormat.java | 21 ++++++++++++ .../camel/dataformat/csv/CsvRecordConverters.java | 40 ++++++++++++++++++++++ .../camel/dataformat/csv/CsvUnmarshaller.java | 4 ++- .../camel/dataformat/csv/CsvUnmarshalTest.java | 37 ++++++++++++++++++++ .../csv/springboot/CsvDataFormatConfiguration.java | 18 ++++++++-- 7 files changed, 137 insertions(+), 6 deletions(-) diff --git a/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java b/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java index 7eeb27c..42ca8e9 100644 --- a/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java +++ b/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java @@ -91,6 +91,8 @@ public class CsvDataFormat extends DataFormatDefinition { @XmlAttribute private Boolean useMaps; @XmlAttribute + private Boolean useOrderedMaps; + @XmlAttribute private String recordConverterRef; public CsvDataFormat() { @@ -187,6 +189,9 @@ public class CsvDataFormat extends DataFormatDefinition { if (useMaps != null) { setProperty(camelContext, dataFormat, "useMaps", useMaps); } + if (useOrderedMaps != null) { + setProperty(camelContext, dataFormat, "useOrderedMaps", useOrderedMaps); + } if (ObjectHelper.isNotEmpty(recordConverterRef)) { Object recordConverter = CamelContextHelper.mandatoryLookup(camelContext, recordConverterRef); setProperty(camelContext, dataFormat, "recordConverter", recordConverter); @@ -438,12 +443,23 @@ public class CsvDataFormat extends DataFormatDefinition { } /** - * Whether the unmarshalling should produce maps for the lines values instead of lists. It requires to have header (either defined or collected). + * Whether the unmarshalling should produce maps (HashMap)for the lines values instead of lists. It requires to have header (either defined or collected). */ public void setUseMaps(Boolean useMaps) { this.useMaps = useMaps; } + public Boolean getUseOrderedMaps() { + return useOrderedMaps; + } + + /** + * Whether the unmarshalling should produce ordered maps (LinkedHashMap) for the lines values instead of lists. It requires to have header (either defined or collected). + */ + public void setUseOrderedMaps(Boolean useOrderedMaps) { + this.useOrderedMaps = useOrderedMaps; + } + public String getRecordConverterRef() { return recordConverterRef; } diff --git a/components/camel-csv/src/main/docs/csv-dataformat.adoc b/components/camel-csv/src/main/docs/csv-dataformat.adoc index 436ac90..18c2853 100644 --- a/components/camel-csv/src/main/docs/csv-dataformat.adoc +++ b/components/camel-csv/src/main/docs/csv-dataformat.adoc @@ -12,7 +12,7 @@ exported/imported by Excel. ### Options // dataformat options: START -The CSV dataformat supports 27 options which are listed below. +The CSV dataformat supports 28 options which are listed below. @@ -43,7 +43,8 @@ The CSV dataformat supports 27 options which are listed below. | trim | false | Boolean | Sets whether or not to trim leading and trailing blanks. | trailingDelimiter | false | Boolean | Sets whether or not to add a trailing delimiter. | lazyLoad | false | Boolean | Whether the unmarshalling should produce an iterator that reads the lines on the fly or if all the lines must be read at one. -| useMaps | false | Boolean | Whether the unmarshalling should produce maps for the lines values instead of lists. It requires to have header (either defined or collected). +| useMaps | false | Boolean | Whether the unmarshalling should produce maps (HashMap) for the lines values instead of lists. It requires to have header (either defined or collected). +| useOrderedMaps | false | Boolean | Whether the unmarshalling should produce ordered maps (LinkedHashMap) for the lines values instead of lists. It requires to have header (either defined or collected). | recordConverterRef | | String | Refers to a custom CsvRecordConverter to lookup from the registry to use. | contentTypeHeader | false | Boolean | Whether the data format should set the Content-Type header with the type from the data format if the data format is capable of doing so. For example application/xml for data formats marshalling to XML, or application/json for data formats marshalling to JSon etc. |=== diff --git a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java index 41c9529..c6f1e1a 100644 --- a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java +++ b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java @@ -64,6 +64,7 @@ public class CsvDataFormat extends ServiceSupport implements DataFormat, DataFor // Unmarshal options private boolean lazyLoad; private boolean useMaps; + private boolean useOrderedMaps; private CsvRecordConverter<?> recordConverter; private volatile CsvMarshaller marshaller; @@ -674,6 +675,26 @@ public class CsvDataFormat extends ServiceSupport implements DataFormat, DataFor } /** + * Indicates whether or not the unmarshalling should produce ordered maps instead of lists. + * + * @return {@code true} for maps, {@code false} for lists + */ + public boolean isUseOrderedMaps() { + return useOrderedMaps; + } + + /** + * Sets whether or not the unmarshalling should produce ordered maps instead of lists. + * + * @param useOrderedMaps {@code true} for maps, {@code false} for lists + * @return Current {@code CsvDataFormat}, fluent API + */ + public CsvDataFormat setUseOrderedMaps(boolean useOrderedMaps) { + this.useOrderedMaps = useOrderedMaps; + return this; + } + + /** * Gets the record converter to use. If {@code null} then it will use {@link CsvDataFormat#isUseMaps()} for finding * the proper converter. * diff --git a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvRecordConverters.java b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvRecordConverters.java index 120de7d..4884cbc 100644 --- a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvRecordConverters.java +++ b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvRecordConverters.java @@ -16,7 +16,10 @@ */ package org.apache.camel.dataformat.csv; +import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -71,4 +74,41 @@ final class CsvRecordConverters { return record.toMap(); } } + + /** + * Returns a converter that transforms the CSV record into an ordered map. + * + * @return converter that transforms the CSV record into an ordered map + */ + public static CsvRecordConverter<Map<String, String>> orderedMapConverter() { + return OrderedMapCsvRecordConverter.SINGLETON; + } + + private static class OrderedMapCsvRecordConverter implements CsvRecordConverter<Map<String, String>> { + private static final OrderedMapCsvRecordConverter SINGLETON = new OrderedMapCsvRecordConverter(); + + @Override + @SuppressWarnings("unchecked") + public Map<String, String> convertRecord(CSVRecord record) { + Map<String, String> answer = new LinkedHashMap<>(); + + // use reflection because CSVRecord does not return maps ordered + try { + Field field = record.getClass().getDeclaredField("mapping"); + field.setAccessible(true); + Map<String, Integer> mapping = (Map<String, Integer>) field.get(record); + if (mapping != null) { + for (Object o : mapping.entrySet()) { + Map.Entry<String, Integer> entry = (Map.Entry) o; + int col = entry.getValue(); + answer.put(entry.getKey(), record.get(col)); + } + } + } catch (Exception e) { + e.printStackTrace(); + // ignore + } + return answer; + } + } } diff --git a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvUnmarshaller.java b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvUnmarshaller.java index da0ed54..9bde056 100644 --- a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvUnmarshaller.java +++ b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvUnmarshaller.java @@ -45,7 +45,7 @@ abstract class CsvUnmarshaller { public static CsvUnmarshaller create(CSVFormat format, CsvDataFormat dataFormat) { // If we want to use maps, thus the header must be either fixed or automatic - if (dataFormat.isUseMaps() && format.getHeader() == null) { + if ((dataFormat.isUseMaps() || dataFormat.isUseOrderedMaps()) && format.getHeader() == null) { format = format.withHeader(); } // If we want to skip the header record it must automatic otherwise it's not working @@ -72,6 +72,8 @@ abstract class CsvUnmarshaller { private static CsvRecordConverter<?> extractConverter(CsvDataFormat dataFormat) { if (dataFormat.getRecordConverter() != null) { return dataFormat.getRecordConverter(); + } else if (dataFormat.isUseOrderedMaps()) { + return CsvRecordConverters.orderedMapConverter(); } else if (dataFormat.isUseMaps()) { return CsvRecordConverters.mapConverter(); } else { diff --git a/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvUnmarshalTest.java b/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvUnmarshalTest.java index a39b70f..b7323c4 100644 --- a/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvUnmarshalTest.java +++ b/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvUnmarshalTest.java @@ -17,6 +17,9 @@ package org.apache.camel.dataformat.csv; import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -92,6 +95,35 @@ public class CsvUnmarshalTest extends CamelTestSupport { assertEquals(2, body.size()); assertEquals(asMap("A", "1", "B", "2", "C", "3"), body.get(0)); assertEquals(asMap("A", "one", "B", "two", "C", "three"), body.get(1)); + // should be unordered map + Map map = (Map) body.get(0); + assertIsInstanceOf(HashMap.class, map); + } + + @Test + public void shouldUseOrderedMaps() throws Exception { + output.expectedMessageCount(1); + + template.sendBody("direct:orderedmap", CSV_SAMPLE); + output.assertIsSatisfied(); + + List<?> body = assertIsInstanceOf(List.class, output.getExchanges().get(0).getIn().getBody()); + assertEquals(2, body.size()); + assertEquals(asMap("A", "1", "B", "2", "C", "3"), body.get(0)); + assertEquals(asMap("A", "one", "B", "two", "C", "three"), body.get(1)); + + Map map = (Map) body.get(0); + assertIsInstanceOf(LinkedHashMap.class, map); + Iterator<Map.Entry> it = map.entrySet().iterator(); + Map.Entry e = it.next(); + assertEquals("A", e.getKey()); + assertEquals("1", e.getValue()); + e = it.next(); + assertEquals("B", e.getKey()); + assertEquals("2", e.getValue()); + e = it.next(); + assertEquals("C", e.getKey()); + assertEquals("3", e.getValue()); } @Test @@ -146,6 +178,11 @@ public class CsvUnmarshalTest extends CamelTestSupport { .unmarshal(new CsvDataFormat().setUseMaps(true)) .to("mock:output"); + // Use ordered maps + from("direct:orderedmap") + .unmarshal(new CsvDataFormat().setUseOrderedMaps(true)) + .to("mock:output"); + // Use lazy load and maps from("direct:lazy_map") .unmarshal(new CsvDataFormat().setLazyLoad(true).setUseMaps(true)) diff --git a/platforms/spring-boot/components-starter/camel-csv-starter/src/main/java/org/apache/camel/dataformat/csv/springboot/CsvDataFormatConfiguration.java b/platforms/spring-boot/components-starter/camel-csv-starter/src/main/java/org/apache/camel/dataformat/csv/springboot/CsvDataFormatConfiguration.java index f01a3ea..f480eb4 100644 --- a/platforms/spring-boot/components-starter/camel-csv-starter/src/main/java/org/apache/camel/dataformat/csv/springboot/CsvDataFormatConfiguration.java +++ b/platforms/spring-boot/components-starter/camel-csv-starter/src/main/java/org/apache/camel/dataformat/csv/springboot/CsvDataFormatConfiguration.java @@ -132,12 +132,18 @@ public class CsvDataFormatConfiguration */ private Boolean lazyLoad = false; /** - * Whether the unmarshalling should produce maps for the lines values - * instead of lists. It requires to have header (either defined or + * Whether the unmarshalling should produce maps (HashMap) for the lines + * values instead of lists. It requires to have header (either defined or * collected). */ private Boolean useMaps = false; /** + * Whether the unmarshalling should produce ordered maps (LinkedHashMap) for + * the lines values instead of lists. It requires to have header (either + * defined or collected). + */ + private Boolean useOrderedMaps = false; + /** * Refers to a custom CsvRecordConverter to lookup from the registry to use. */ private String recordConverterRef; @@ -349,6 +355,14 @@ public class CsvDataFormatConfiguration this.useMaps = useMaps; } + public Boolean getUseOrderedMaps() { + return useOrderedMaps; + } + + public void setUseOrderedMaps(Boolean useOrderedMaps) { + this.useOrderedMaps = useOrderedMaps; + } + public String getRecordConverterRef() { return recordConverterRef; } -- To stop receiving notification emails like this one, please contact davscl...@apache.org.