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

Reply via email to