@@ -44,12 +44,12 @@ public class CsvExport
4444 /// <summary>
4545 /// The list of rows
4646 /// </summary>
47- List < List < string > > _rows = new ( ) ;
47+ List < List < object > > _rows = new ( ) ;
4848
4949 /// <summary>
5050 /// The current row
5151 /// </summary>
52- List < string > _currentRow = null ;
52+ List < object > _currentRow = null ;
5353
5454 /// <summary>
5555 /// The string used to separate columns in the output
@@ -113,7 +113,7 @@ public object this[string field]
113113 while ( num >= _currentRow . Count ) //fill the current row with nulls until we have the right size
114114 _currentRow . Add ( null ) ;
115115
116- _currentRow [ num ] = MakeValueCsvFriendly ( value , _columnSeparator ) ; //set the value at position
116+ _currentRow [ num ] = value ; //set the raw value at position
117117 }
118118 }
119119
@@ -158,19 +158,61 @@ public void AddRows<T>(IEnumerable<T> list)
158158 /// </param>
159159 public static string MakeValueCsvFriendly ( object value , string columnSeparator = "," )
160160 {
161- if ( value == null ) return "" ;
162- if ( value is INullable && ( ( INullable ) value ) . IsNull ) return "" ;
161+ var sb = new StringBuilder ( ) ;
162+ WriteCsvFriendlyValue ( value , new StringBuilderCsvWriter ( sb ) , columnSeparator ) ;
163+ return sb . ToString ( ) ;
164+ }
165+
166+ /// <summary>
167+ /// Interface for abstracting write operations to different output targets
168+ /// </summary>
169+ private interface ICsvWriter
170+ {
171+ void Write ( string value ) ;
172+ void Write ( char value ) ;
173+ }
174+
175+ /// <summary>
176+ /// StringBuilder wrapper for ICsvWriter
177+ /// </summary>
178+ private class StringBuilderCsvWriter : ICsvWriter
179+ {
180+ private readonly StringBuilder _sb ;
181+ public StringBuilderCsvWriter ( StringBuilder sb ) => _sb = sb ;
182+ public void Write ( string value ) => _sb . Append ( value ) ;
183+ public void Write ( char value ) => _sb . Append ( value ) ;
184+ }
185+
186+ /// <summary>
187+ /// StreamWriter wrapper for ICsvWriter
188+ /// </summary>
189+ private class StreamWriterCsvWriter : ICsvWriter
190+ {
191+ private readonly StreamWriter _sw ;
192+ public StreamWriterCsvWriter ( StreamWriter sw ) => _sw = sw ;
193+ public void Write ( string value ) => _sw . Write ( value ) ;
194+ public void Write ( char value ) => _sw . Write ( value ) ;
195+ }
196+
197+ /// <summary>
198+ /// Converts a value to CSV-friendly format and writes it directly to an ICsvWriter
199+ /// </summary>
200+ private static void WriteCsvFriendlyValue ( object value , ICsvWriter writer , string columnSeparator = "," )
201+ {
202+ if ( value == null ) return ;
203+ if ( value is INullable && ( ( INullable ) value ) . IsNull ) return ;
163204
164205 if ( value is DateTime date )
165206 {
166207 if ( date . TimeOfDay . TotalSeconds == 0 )
167208 {
168- return date . ToString ( "yyyy-MM-dd" ) ;
209+ writer . Write ( date . ToString ( "yyyy-MM-dd" ) ) ;
169210 }
170211 else
171212 {
172- return date . ToString ( "yyyy-MM-dd HH:mm:ss" ) ;
213+ writer . Write ( date . ToString ( "yyyy-MM-dd HH:mm:ss" ) ) ;
173214 }
215+ return ;
174216 }
175217
176218 var output = value . ToString ( ) . Trim ( ) ;
@@ -179,20 +221,26 @@ public static string MakeValueCsvFriendly(object value, string columnSeparator =
179221 output = output . Substring ( 0 , 30000 ) ;
180222
181223 if ( output . Contains ( columnSeparator ) || output . Contains ( '\" ' ) || output . Contains ( '\n ' ) || output . Contains ( '\r ' ) )
182- output = '"' + output . Replace ( "\" " , "\" \" " ) + '"' ;
183-
184- return output ;
224+ {
225+ writer . Write ( '"' ) ;
226+ writer . Write ( output . Replace ( "\" " , "\" \" " ) ) ;
227+ writer . Write ( '"' ) ;
228+ }
229+ else
230+ {
231+ writer . Write ( output ) ;
232+ }
185233 }
186234
187235 /// <summary>
188236 /// Outputs all rows as a CSV, returning one "line" at a time
189- /// Where "line" is a IEnumerable of string values
237+ /// Where "line" is a IEnumerable of object values
190238 /// </summary>
191- private IEnumerable < IEnumerable < string > > ExportToLines ( )
239+ private IEnumerable < IEnumerable < object > > ExportToLines ( )
192240 {
193241 // The header
194242 if ( _includeHeaderRow )
195- yield return _fields . OrderBy ( f => f . Value ) . Select ( f => MakeValueCsvFriendly ( f . Key , _columnSeparator ) ) ;
243+ yield return _fields . OrderBy ( f => f . Value ) . Select ( f => f . Key ) ;
196244
197245 // The rows
198246 foreach ( var row in _rows )
@@ -207,18 +255,22 @@ private IEnumerable<IEnumerable<string>> ExportToLines()
207255 public string Export ( )
208256 {
209257 StringBuilder sb = new StringBuilder ( ) ;
258+ ICsvWriter writer = new StringBuilderCsvWriter ( sb ) ;
210259
211260 if ( _includeColumnSeparatorDefinitionPreamble )
212261 sb . Append ( "sep=" + _columnSeparator + "\r \n " ) ;
213262
214263 foreach ( var line in ExportToLines ( ) )
215264 {
265+ bool first = true ;
216266 foreach ( var value in line )
217267 {
218- sb . Append ( value ) ;
219- sb . Append ( _columnSeparator ) ;
268+ if ( ! first )
269+ sb . Append ( _columnSeparator ) ;
270+
271+ WriteCsvFriendlyValue ( value , writer , _columnSeparator ) ;
272+ first = false ;
220273 }
221- sb . Length = sb . Length - _columnSeparator . Length ; //remove the trailing comma (shut up)
222274 sb . Append ( "\r \n " ) ;
223275 }
224276
@@ -254,18 +306,21 @@ public MemoryStream ExportAsMemoryStream(Encoding encoding = null)
254306
255307 using ( var sw = new StreamWriter ( ms , encoding , 1024 , leaveOpen : true ) )
256308 {
309+ ICsvWriter writer = new StreamWriterCsvWriter ( sw ) ;
310+
257311 if ( _includeColumnSeparatorDefinitionPreamble )
258312 sw . Write ( "sep=" + _columnSeparator + "\r \n " ) ;
259313
260314 foreach ( var line in ExportToLines ( ) )
261315 {
262- int i = 0 ;
316+ bool first = true ;
263317 foreach ( var value in line )
264318 {
265- sw . Write ( value ) ;
266-
267- if ( ++ i != _fields . Count )
319+ if ( ! first )
268320 sw . Write ( _columnSeparator ) ;
321+
322+ WriteCsvFriendlyValue ( value , writer , _columnSeparator ) ;
323+ first = false ;
269324 }
270325 sw . Write ( "\r \n " ) ;
271326 }
0 commit comments