Skip to content

Commit

Permalink
Escape the escape char when writing.
Browse files Browse the repository at this point in the history
  • Loading branch information
JoshClose committed Nov 10, 2022
1 parent 72db5e1 commit 90dcb88
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/CsvHelper/CsvWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ public class CsvWriter : IWriter
private readonly char injectionEscapeCharacter;
private readonly InjectionOptions injectionOptions;
private readonly CsvMode mode;
private readonly string escapeString;
private readonly string escapeQuoteString;
private readonly string escapeDelimiterString;
private readonly string escapeNewlineString;
private readonly string escapeEscapeString;

private bool disposed;
private bool hasHeaderBeenWritten;
Expand Down Expand Up @@ -112,6 +114,7 @@ public CsvWriter(TextWriter writer, IWriterConfiguration configuration, bool lea
escapeDelimiterString = new string(configuration.Delimiter.SelectMany(c => new[] { configuration.Escape, c }).ToArray());
escapeNewlineString = new string(configuration.NewLine.SelectMany(c => new[] { configuration.Escape, c }).ToArray());
escapeQuoteString = new string(new[] { configuration.Escape, configuration.Quote });
escapeEscapeString = new string(new[] { configuration.Escape, configuration.Escape });
hasHeaderRecord = configuration.HasHeaderRecord;
includePrivateMembers = configuration.IncludePrivateMembers;
injectionCharacters = configuration.InjectionCharacters;
Expand All @@ -121,6 +124,7 @@ public CsvWriter(TextWriter writer, IWriterConfiguration configuration, bool lea
newLine = configuration.NewLine;
quote = configuration.Quote;
quoteString = configuration.Quote.ToString();
escapeString = configuration.Escape.ToString();
injectionOptions = configuration.InjectionOptions;
shouldQuote = configuration.ShouldQuote;
trimOptions = configuration.TrimOptions;
Expand Down Expand Up @@ -165,13 +169,19 @@ public virtual void WriteField(string field, bool shouldQuote)
// All quotes must be escaped.
if (shouldQuote)
{
if (escapeString != quoteString)
{
field = field?.Replace(escapeString, escapeEscapeString);
}

field = field?.Replace(quoteString, escapeQuoteString);
field = quote + field + quote;
}
}
else if (mode == CsvMode.Escape)
{
field = field?
.Replace(escapeString, escapeEscapeString)
.Replace(quoteString, escapeQuoteString)
.Replace(delimiter, escapeDelimiterString)
.Replace(newLine, escapeNewlineString);
Expand Down
63 changes: 63 additions & 0 deletions tests/CsvHelper.Tests/Writing/WriteCustomEscapeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using CsvHelper.Configuration;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using System.Xml.Linq;
using Xunit;

namespace CsvHelper.Tests.Writing
{
public class WriteCustomEscapeTests
{
[Fact]
public void WriteField_CustomEscapeChar_ModeRFC4180_EscapesQuotesAndEscapeCharacter()
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false,
Escape = '\\',
};
using (var writer = new StringWriter())
using (var csv = new CsvWriter(writer, config))
{
// {"json":"{\"name\":\"foo\"}"}
// json string -> csv field
// "{\"json\":\"{\\\"name\\\":\\\"foo\\\"}\"}"
csv.WriteField(@"{""json"":""{\""name\"":\""foo\""}""}");
csv.Flush();

var expected = @"""{\""json\"":\""{\\\""name\\\"":\\\""foo\\\""}\""}""";
Assert.Equal(expected, writer.ToString());
}
}

[Fact]
public void WriteField_CustomEscapeChar_ModeEscape_EscapesQuotesAndEscapeCharacter()
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false,
Escape = '\\',
Mode = CsvMode.Escape,
};
using (var writer = new StringWriter())
using (var csv = new CsvWriter(writer, config))
{
// {"json":"{\"name\":\"foo\"}"}
// json string -> csv field
// {\"json\":\"{\\\"name\\\":\\\"foo\\\"}\"}
csv.WriteField(@"{""json"":""{\""name\"":\""foo\""}""}");
csv.Flush();

var expected = @"{\""json\"":\""{\\\""name\\\"":\\\""foo\\\""}\""}";
Assert.Equal(expected, writer.ToString());
}
}
}
}

0 comments on commit 90dcb88

Please sign in to comment.