22using System . Collections . Generic ;
33using System . IO ;
44using System . Linq ;
5+ using System . Reflection ;
56using ServiceStack . Text . Common ;
67
78namespace ServiceStack . Text
@@ -144,6 +145,7 @@ public class CsvWriter<T>
144145 public static List < string > Headers { get ; set ; }
145146
146147 internal static List < GetMemberDelegate < T > > PropertyGetters ;
148+ internal static List < PropertyInfo > PropertyInfos ;
147149
148150 private static readonly WriteObjectDelegate OptimizedWriter ;
149151
@@ -163,12 +165,15 @@ internal static void Reset()
163165 Headers = new List < string > ( ) ;
164166
165167 PropertyGetters = new List < GetMemberDelegate < T > > ( ) ;
168+ PropertyInfos = new List < PropertyInfo > ( ) ;
166169 foreach ( var propertyInfo in TypeConfig < T > . Properties )
167170 {
168171 if ( ! propertyInfo . CanRead || propertyInfo . GetGetMethod ( nonPublic : true ) == null ) continue ;
169172 if ( ! TypeSerializer . CanCreateFromString ( propertyInfo . PropertyType ) ) continue ;
170173
171174 PropertyGetters . Add ( propertyInfo . CreateGetter < T > ( ) ) ;
175+ PropertyInfos . Add ( propertyInfo ) ;
176+
172177 var propertyName = propertyInfo . Name ;
173178 var dcsDataMemberName = propertyInfo . GetDataMemberName ( ) ;
174179 if ( dcsDataMemberName != null )
@@ -257,6 +262,7 @@ public static void WriteObjectRow(TextWriter writer, object record)
257262 public static void Write ( TextWriter writer , IEnumerable < T > records )
258263 {
259264 if ( writer == null ) return ; //AOT
265+ if ( records == null ) return ;
260266
261267 if ( typeof ( T ) == typeof ( Dictionary < string , string > ) || typeof ( T ) == typeof ( IDictionary < string , string > ) )
262268 {
@@ -277,10 +283,56 @@ public static void Write(TextWriter writer, IEnumerable<T> records)
277283 return ;
278284 }
279285
280- if ( ! CsvConfig < T > . OmitHeaders && Headers . Count > 0 )
286+ var recordsList = records . ToList ( ) ;
287+
288+ var headers = Headers ;
289+ var propGetters = PropertyGetters ;
290+ var treatAsSingleRow = typeof ( T ) . IsValueType || typeof ( T ) == typeof ( string ) ;
291+
292+ if ( ! treatAsSingleRow && JsConfig . ExcludeDefaultValues )
293+ {
294+ var hasValues = new bool [ headers . Count ] ;
295+ var defaultValues = new object [ headers . Count ] ;
296+ for ( var i = 0 ; i < PropertyInfos . Count ; i ++ )
297+ {
298+ defaultValues [ i ] = PropertyInfos [ i ] . PropertyType . GetDefaultValue ( ) ;
299+ }
300+
301+ foreach ( var record in recordsList )
302+ {
303+ for ( var i = 0 ; i < propGetters . Count ; i ++ )
304+ {
305+ var propGetter = propGetters [ i ] ;
306+ var value = propGetter ( record ) ;
307+
308+ if ( value != null && ! value . Equals ( defaultValues [ i ] ) )
309+ hasValues [ i ] = true ;
310+ }
311+ }
312+
313+ if ( hasValues . Any ( x => x == false ) )
314+ {
315+ var newHeaders = new List < string > ( ) ;
316+ var newGetters = new List < GetMemberDelegate < T > > ( ) ;
317+
318+ for ( int i = 0 ; i < hasValues . Length ; i ++ )
319+ {
320+ if ( hasValues [ i ] )
321+ {
322+ newHeaders . Add ( headers [ i ] ) ;
323+ newGetters . Add ( propGetters [ i ] ) ;
324+ }
325+ }
326+
327+ headers = newHeaders ;
328+ propGetters = newGetters ;
329+ }
330+ }
331+
332+ if ( ! CsvConfig < T > . OmitHeaders && headers . Count > 0 )
281333 {
282334 var ranOnce = false ;
283- foreach ( var header in Headers )
335+ foreach ( var header in headers )
284336 {
285337 CsvWriter . WriteItemSeperatorIfRanOnce ( writer , ref ranOnce ) ;
286338
@@ -289,25 +341,23 @@ public static void Write(TextWriter writer, IEnumerable<T> records)
289341 writer . Write ( CsvConfig . RowSeparatorString ) ;
290342 }
291343
292- if ( records == null ) return ;
293-
294- if ( typeof ( T ) . IsValueType || typeof ( T ) == typeof ( string ) )
344+ if ( treatAsSingleRow )
295345 {
296- var singleRow = GetSingleRow ( records , typeof ( T ) ) ;
346+ var singleRow = GetSingleRow ( recordsList , typeof ( T ) ) ;
297347 WriteRow ( writer , singleRow ) ;
298348 return ;
299349 }
300350
301- var row = new string [ Headers . Count ] ;
302- foreach ( var record in records )
351+ var row = new string [ headers . Count ] ;
352+ foreach ( var record in recordsList )
303353 {
304- for ( var i = 0 ; i < PropertyGetters . Count ; i ++ )
354+ for ( var i = 0 ; i < propGetters . Count ; i ++ )
305355 {
306- var propertyGetter = PropertyGetters [ i ] ;
307- var value = propertyGetter ( record ) ?? "" ;
356+ var propGetter = propGetters [ i ] ;
357+ var value = propGetter ( record ) ?? "" ;
308358
309- var strValue = value is string
310- ? ( string ) value
359+ var strValue = value is string s
360+ ? s
311361 : TypeSerializer . SerializeToString ( value ) . StripQuotes ( ) ;
312362
313363 row [ i ] = strValue ;
0 commit comments