This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new ade0470a5f5e CAMEL-25722: camel-flatpack - marshal writes the first
row of the list (#28030)
ade0470a5f5e is described below
commit ade0470a5f5e1f4c66c40a0bae723344939897c1
Author: allthingssecurity <[email protected]>
AuthorDate: Tue Oct 20 01:35:36 2026 +0530
CAMEL-25722: camel-flatpack - marshal writes the first row of the list
(#28030)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../dataformat/flatpack/FlatpackDataFormat.java | 7 +-
.../flatpack/FlatpackMarshalKeepsFirstRowTest.java | 106 +++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 8 ++
3 files changed, 115 insertions(+), 6 deletions(-)
diff --git
a/components/camel-flatpack/src/main/java/org/apache/camel/dataformat/flatpack/FlatpackDataFormat.java
b/components/camel-flatpack/src/main/java/org/apache/camel/dataformat/flatpack/FlatpackDataFormat.java
index 1c9c09c07c92..a53465316bb4 100644
---
a/components/camel-flatpack/src/main/java/org/apache/camel/dataformat/flatpack/FlatpackDataFormat.java
+++
b/components/camel-flatpack/src/main/java/org/apache/camel/dataformat/flatpack/FlatpackDataFormat.java
@@ -89,14 +89,9 @@ public class FlatpackDataFormat extends ServiceSupport
implements DataFormat, Da
Writer writer = createWriter(exchange, firstRow, stream);
try {
- boolean first = true;
+ // the header line comes from the column names, every map of the
list is a data row
writer.printHeader();
for (Map<String, Object> row : data) {
- if (ignoreFirstRecord && first) {
- // skip first row
- first = false;
- continue;
- }
for (Entry<String, Object> entry : row.entrySet()) {
writer.addRecordEntry(entry.getKey(), entry.getValue());
}
diff --git
a/components/camel-flatpack/src/test/java/org/apache/camel/component/flatpack/FlatpackMarshalKeepsFirstRowTest.java
b/components/camel-flatpack/src/test/java/org/apache/camel/component/flatpack/FlatpackMarshalKeepsFirstRowTest.java
new file mode 100644
index 000000000000..b8479fd3ab3b
--- /dev/null
+++
b/components/camel-flatpack/src/test/java/org/apache/camel/component/flatpack/FlatpackMarshalKeepsFirstRowTest.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.flatpack;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.dataformat.flatpack.FlatpackDataFormat;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Marshalling a list of maps writes every map as a data row, with the default
ignoreFirstRecord=true too; the header
+ * line comes from the column names.
+ */
+class FlatpackMarshalKeepsFirstRowTest extends CamelTestSupport {
+
+ @Test
+ void marshalWritesEveryRowWithDefaultOptions() {
+ String csv = template.requestBody("direct:marshal", rows(),
String.class);
+
+ assertEquals(List.of("ID,NAME", "1,alice", "2,bob"),
csv.lines().toList());
+ }
+
+ @Test
+ void marshalWithDefinitionWritesEveryRow() {
+ List<Map<String, Object>> data = new ArrayList<>();
+ data.add(item("SOME VALVE", "2", "5.00", "20050101"));
+ data.add(item("AN ENGINE", "100", "1000.00", "20040601"));
+
+ String csv = template.requestBody("direct:marshalDefinition", data,
String.class);
+
+ List<String> lines = csv.lines().toList();
+ assertEquals(3, lines.size(), csv);
+ assertEquals("SOME VALVE,2,5.00,20050101", lines.get(1));
+ assertEquals("AN ENGINE,100,1000.00,20040601", lines.get(2));
+ }
+
+ @Test
+ void unmarshalThenMarshalKeepsEveryRecord() {
+ String csv = "ID,NAME\r\n1,alice\r\n2,bob\r\n3,carol\r\n";
+
+ Object list = template.requestBody("direct:unmarshal", csv);
+ String back = template.requestBody("direct:marshal", list,
String.class);
+
+ List<String> lines = back.lines().toList();
+ assertEquals(List.of("ID,NAME", "1,alice", "2,bob", "3,carol"), lines);
+ }
+
+ private static List<Map<String, Object>> rows() {
+ List<Map<String, Object>> data = new ArrayList<>();
+ Map<String, Object> row = new LinkedHashMap<>();
+ row.put("ID", "1");
+ row.put("NAME", "alice");
+ data.add(row);
+ Map<String, Object> row2 = new LinkedHashMap<>();
+ row2.put("ID", "2");
+ row2.put("NAME", "bob");
+ data.add(row2);
+ return data;
+ }
+
+ private static Map<String, Object> item(String desc, String stock, String
price, String date) {
+ Map<String, Object> row = new LinkedHashMap<>();
+ row.put("ITEM_DESC", desc);
+ row.put("IN_STOCK", stock);
+ row.put("PRICE", price);
+ row.put("LAST_RECV_DT", date);
+ return row;
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ FlatpackDataFormat df = new FlatpackDataFormat();
+ from("direct:marshal").marshal(df).convertBodyTo(String.class);
+ from("direct:unmarshal").unmarshal(df);
+
+ FlatpackDataFormat withDefinition = new FlatpackDataFormat();
+ withDefinition.setDefinition("INVENTORY-Delimited.pzmap.xml");
+
from("direct:marshalDefinition").marshal(withDefinition).convertBodyTo(String.class);
+ }
+ };
+ }
+}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 65c07cc7d8ca..33070e296cf3 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -906,6 +906,14 @@ now read as a local date-time without a zone (`getDate()`
returns a `LocalDateTi
definition of an earlier message, if there was one. A calendar that defines a
`TZID` an earlier message has defined
differently is now read with its own definition.
+=== camel-flatpack - marshal writes the first row of the list
+
+With the default `ignoreFirstRecord=true`, marshalling a `List<Map<String,
Object>>` skipped the first map, so the
+first data row was missing from the output, for delimited and fixed-length
files alike (the header line of a delimited
+file is written from the column names, not from the first map). Every map of
the list is now written, and
+`ignoreFirstRecord` only applies to unmarshal. A route that set
`ignoreFirstRecord=false` to work around this is not
+affected; a route that added a dummy first map now gets it in the output too.
+
=== camel-milo - the OPC UA client validates the server certificate
When the endpoint it selects uses a security policy other than `None`, the
`milo-client` and `milo-browse` components