You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@camel.apache.org by "Claus Ibsen (Jira)" <ji...@apache.org> on 2026/10/06 19:39:00 UTC

[jira] [Resolved] (CAMEL-25150) camel-bindy - with quotingEscaped=true a CSV field whose value ends with a quote (He said "hi", 12") is not closed on unmarshal, so it swallows the next fields and the columns shift (regression of CAMEL-22068)

     [ https://issues.apache.org/jira/browse/CAMEL-25150?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Claus Ibsen resolved CAMEL-25150.
---------------------------------
    Fix Version/s: 4.23.0
       Resolution: Fixed

The fix is merged on main, so it is in Camel 4.23.0:
* cb8769029093 CAMEL-25150: camel-bindy - with quotingEscaped a CSV field ending with a quote is closed again

Resolving, as the ticket was not updated when the PR was merged.

_Claude Code on behalf of Claus Ibsen_

> camel-bindy - with quotingEscaped=true a CSV field whose value ends with a quote (He said "hi", 12") is not closed on unmarshal, so it swallows the next fields and the columns shift (regression of CAMEL-22068)
> -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-25150
>                 URL: https://issues.apache.org/jira/browse/CAMEL-25150
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-bindy
>            Reporter: shashank
>            Priority: Minor
>              Labels: regression
>             Fix For: 4.23.0
>
>
> With {{@CsvRecord(quoting = true, quotingEscaped = true)}} Bindy writes a quote inside a field as a backslash and the quote (CAMEL-7519): the value {{He said "hi"}} is written {{"He said \"hi\""}}. Unmarshal cannot read this field back when the quote character is the double quote and the value ends with an odd number of quotes:
> {noformat}
> line:      "123","He said \"hi\"","10"
> expected:  firstField=123, secondField=He said "hi", number=10
> actual:    firstField=123, secondField=He said "hi"",10  (the third column is merged into the second), number=null
> {noformat}
> The cause is the loop that CAMEL-22068 added to {{BindyCsvDataFormat.unquoteTokens}} to support RFC 4180 doubled quotes: for a token that ends with the quote, it counts the quotes in front of the final one, skipping backslashes, and treats the final quote as escaped when the count is odd. That is the RFC 4180 rule ({{""}} is one quote in the value), but it also runs when {{quotingEscaped=true}}, where the value's quote is written {{\"}}. For {{...\""}} the loop skips the backslash, counts one quote, decides that the closing quote is escaped, and keeps the field open until a later token ends with a quote. The result is silent data corruption: the value gets the separator and the following columns appended, the following fields are shifted or empty, and a typed field (number, date) that receives the wrong text fails.
> Values with an even number of trailing quotes (the value {{""foo""}} of the existing test) and values without a trailing quote are read correctly, which is why the existing tests pass. The single quote as quote character is not affected, because the loop only runs for the double quote.
> Before 4.8.8 / 4.10.5 / 4.12.0 a token ending with the quote always closed the field, and these values were read back correctly. A brute force over all values of up to 3 characters from {{a , " \ space}} in each of three columns (marshal, then unmarshal): 120 of 468 records fail today, 33 with the unquote code of 4.10.0, 33 with the fix below. The 33 remaining ones are values with an escaped quote directly in front of the separator ({{a",b}}), which already failed before CAMEL-22068 and are not a regression.
> h3. Reproduction
> {code:java}
> @CsvRecord(separator = ",", quote = "\"", quoting = true, quotingEscaped = true)
> public static class Row {
>     @DataField(pos = 1) private String firstField;
>     @DataField(pos = 2) private String secondField;
>     @DataField(pos = 3, pattern = "########.##") private BigDecimal number;
> }
> // marshal of (123, He said "hi", 10) gives "123","He said \"hi\"","10"
> // unmarshal of that line gives secondField = He said "hi"",10 and number = null
> {code}
> A unit test fails on main (three runs) for the values {{He said "hi"}}, {{12"}}, {{"}} and {{a "b" "c"}}. A small formal model (Lean 4) of the loop shows that for every value that ends with {{n}} quotes after another character the field is closed exactly when {{n}} is even, and that with the fix every quoted field without a separator in it is read back as written.
> h3. Affected versions
> The loop is in 4.8.8, 4.10.5, 4.12.0 and every later release up to main; 4.8.7 and 4.10.4 do not have it.
> h3. Proposed fix
> Apply the doubled-quote rule only when {{quotingEscaped}} is false:
> {code:java}
> if (quote.equals("\"") && !quotingEscaped) {
>     // RFC 4180 doubled quotes
>     ...
> }
> {code}
> With {{quotingEscaped=true}} a token that ends with the quote closes the field again, as before CAMEL-22068. RFC 4180 input ({{quotingEscaped=false}}, the default) is read exactly as today, including the cases of CAMEL-22068. Values that end with a backslash ({{C:\temp\}}) keep working.
> The one input that is read differently is RFC 4180 style input in {{quotingEscaped=true}} mode with a separator inside the quotes and an odd number of quotes in front of it ({{"x"",y"}}): today it stays one field (with the doubled quote kept, since this mode does not unescape {{""}}), with the fix it is split, as in 4.8.7 and 4.10.4. Such input is not the format {{quotingEscaped}} describes.
> Duplicate check (2026-09-30): JIRA "quotingEscaped", "bindy" with "escape" and "quote": CAMEL-7519, CAMEL-12274, CAMEL-22068, CAMEL-19143, all fixed, none about this case. No open pull request touches {{unquoteTokens}}.
> _Filed with Claude Code on behalf of allthingssecurity._



--
This message was sent by Atlassian Jira
(v8.20.10#820010)