1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
4+ using ServiceStack . Reflection ;
5+ using ServiceStack . Text . Common ;
6+ using ServiceStack . Text . Jsv ;
7+
8+ namespace ServiceStack . Text
9+ {
10+ public class CsvReader
11+ {
12+ static readonly ITypeSerializer Serializer = JsvTypeSerializer . Instance ;
13+
14+ public static List < string > ParseLines ( string csv )
15+ {
16+ var rows = new List < string > ( ) ;
17+ if ( string . IsNullOrEmpty ( csv ) )
18+ return rows ;
19+
20+ var withinQuotes = false ;
21+ var lastPos = 0 ;
22+
23+ var i = - 1 ;
24+ var len = csv . Length ;
25+ while ( ++ i < len )
26+ {
27+ var c = csv [ i ] ;
28+ if ( c == JsWriter . QuoteChar )
29+ {
30+ var isLiteralQuote = i + 1 < len && csv [ i + 1 ] == JsWriter . QuoteChar ;
31+ if ( isLiteralQuote )
32+ {
33+ i ++ ;
34+ continue ;
35+ }
36+
37+ withinQuotes = ! withinQuotes ;
38+ }
39+
40+ if ( withinQuotes )
41+ continue ;
42+
43+ if ( c == JsWriter . LineFeedChar )
44+ {
45+ var str = i > 0 && csv [ i - 1 ] == JsWriter . ReturnChar
46+ ? csv . Substring ( lastPos , i - lastPos - 1 )
47+ : csv . Substring ( lastPos , i - lastPos ) ;
48+
49+ if ( str . Length > 0 )
50+ rows . Add ( str ) ;
51+ lastPos = i + 1 ;
52+ }
53+ }
54+
55+ if ( i > lastPos )
56+ {
57+ var str = csv . Substring ( lastPos , i - lastPos ) ;
58+ if ( str . Length > 0 )
59+ rows . Add ( str ) ;
60+ }
61+
62+ return rows ;
63+ }
64+
65+ public static List < string > ParseFields ( string line )
66+ {
67+ var to = new List < string > ( ) ;
68+ if ( string . IsNullOrEmpty ( line ) )
69+ return to ;
70+
71+ var i = - 1 ;
72+ var len = line . Length ;
73+ while ( ++ i <= len )
74+ {
75+ var value = Serializer . EatValue ( line , ref i ) ;
76+ to . Add ( value . FromCsvField ( ) ) ;
77+ }
78+
79+ return to ;
80+ }
81+ }
82+
83+ public class CsvReader < T >
84+ {
85+ public const char DelimiterChar = ',' ;
86+
87+ public static List < string > Headers { get ; set ; }
88+
89+ internal static List < Action < T , object > > PropertySetters ;
90+ internal static Dictionary < string , Action < T , object > > PropertySettersMap ;
91+
92+ internal static List < ParseStringDelegate > PropertyConverters ;
93+ internal static Dictionary < string , ParseStringDelegate > PropertyConvertersMap ;
94+
95+ private static readonly ParseStringDelegate OptimizedReader ;
96+
97+ static CsvReader ( )
98+ {
99+ //if (typeof(T) == typeof(string))
100+ //{
101+ // OptimizedReader = ReadRow;
102+ // return;
103+ //}
104+
105+ Reset ( ) ;
106+ }
107+
108+ internal static void Reset ( )
109+ {
110+ Headers = new List < string > ( ) ;
111+
112+ PropertySetters = new List < Action < T , object > > ( ) ;
113+ PropertySettersMap = new Dictionary < string , Action < T , object > > ( PclExport . Instance . InvariantComparerIgnoreCase ) ;
114+
115+ PropertyConverters = new List < ParseStringDelegate > ( ) ;
116+ PropertyConvertersMap = new Dictionary < string , ParseStringDelegate > ( PclExport . Instance . InvariantComparerIgnoreCase ) ;
117+
118+ var isDataContract = typeof ( T ) . IsDto ( ) ;
119+ foreach ( var propertyInfo in TypeConfig < T > . Properties )
120+ {
121+ if ( ! propertyInfo . CanWrite || propertyInfo . GetSetMethod ( ) == null ) continue ;
122+ if ( ! TypeSerializer . CanCreateFromString ( propertyInfo . PropertyType ) ) continue ;
123+
124+ var propertyName = propertyInfo . Name ;
125+ var setter = propertyInfo . GetValueSetter < T > ( ) ;
126+ PropertySetters . Add ( setter ) ;
127+
128+ var converter = JsvReader . GetParseFn ( propertyInfo . PropertyType ) ;
129+ PropertyConverters . Add ( converter ) ;
130+
131+ if ( isDataContract )
132+ {
133+ var dcsDataMemberName = propertyInfo . GetDataMemberName ( ) ;
134+ if ( dcsDataMemberName != null )
135+ propertyName = dcsDataMemberName ;
136+ }
137+
138+ Headers . Add ( propertyName ) ;
139+ PropertySettersMap [ propertyName ] = setter ;
140+ PropertyConvertersMap [ propertyName ] = converter ;
141+ }
142+ }
143+
144+ internal static void ConfigureCustomHeaders ( Dictionary < string , string > customHeadersMap )
145+ {
146+ Reset ( ) ;
147+
148+ for ( var i = Headers . Count - 1 ; i >= 0 ; i -- )
149+ {
150+ var oldHeader = Headers [ i ] ;
151+ string newHeaderValue ;
152+ if ( ! customHeadersMap . TryGetValue ( oldHeader , out newHeaderValue ) )
153+ {
154+ Headers . RemoveAt ( i ) ;
155+ PropertySetters . RemoveAt ( i ) ;
156+ }
157+ else
158+ {
159+ Headers [ i ] = newHeaderValue . EncodeJsv ( ) ;
160+ }
161+ }
162+ }
163+
164+ private static List < T > GetSingleRow ( IEnumerable < string > rows , Type recordType )
165+ {
166+ var row = new List < T > ( ) ;
167+ foreach ( var value in rows )
168+ {
169+ var to = recordType == typeof ( string )
170+ ? ( T ) ( object ) value
171+ : TypeSerializer . DeserializeFromString < T > ( value ) ;
172+
173+ row . Add ( to ) ;
174+ }
175+ return row ;
176+ }
177+
178+ public static List < T > GetRows ( IEnumerable < string > records )
179+ {
180+ var rows = new List < T > ( ) ;
181+
182+ if ( records == null ) return rows ;
183+
184+ if ( typeof ( T ) . IsValueType ( ) || typeof ( T ) == typeof ( string ) )
185+ {
186+ return GetSingleRow ( records , typeof ( T ) ) ;
187+ }
188+
189+ foreach ( var record in records )
190+ {
191+ var to = typeof ( T ) . CreateInstance < T > ( ) ;
192+ foreach ( var propertySetter in PropertySetters )
193+ {
194+ propertySetter ( to , record ) ;
195+ }
196+ rows . Add ( to ) ;
197+ }
198+
199+ return rows ;
200+ }
201+
202+ public static object ReadObject ( string csv )
203+ {
204+ if ( csv == null ) return null ; //AOT
205+
206+ return Read ( CsvReader . ParseLines ( csv ) ) ;
207+ }
208+
209+ public static object ReadObjectRow ( string csv )
210+ {
211+ if ( csv == null ) return null ; //AOT
212+
213+ return ReadRow ( csv ) ;
214+ }
215+
216+ public static List < T > Read ( List < string > rows )
217+ {
218+ var to = new List < T > ( ) ;
219+ if ( rows == null || rows . Count == 0 ) return to ; //AOT
220+
221+ if ( typeof ( T ) . IsAssignableFromType ( typeof ( Dictionary < string , object > ) ) )
222+ {
223+ return CsvDictionaryWriter . ReadObjectDictionary ( rows )
224+ . ConvertAll ( x => ( T ) x . FromObjectDictionary ( typeof ( T ) ) ) ;
225+ }
226+
227+
228+ if ( OptimizedReader != null )
229+ {
230+ foreach ( var row in rows )
231+ {
232+ to . Add ( ( T ) OptimizedReader ( row ) ) ;
233+ }
234+ return to ;
235+ }
236+
237+ List < string > headers = null ;
238+ if ( ! CsvConfig < T > . OmitHeaders || Headers . Count == 0 )
239+ headers = CsvReader . ParseFields ( rows [ 0 ] ) ;
240+
241+ if ( typeof ( T ) . IsValueType ( ) || typeof ( T ) == typeof ( string ) )
242+ {
243+ return GetSingleRow ( rows , typeof ( T ) ) ;
244+ }
245+
246+ for ( var rowIndex = headers == null ? 0 : 1 ; rowIndex < rows . Count ; rowIndex ++ )
247+ {
248+ var row = rows [ rowIndex ] ;
249+ var o = typeof ( T ) . CreateInstance < T > ( ) ;
250+
251+ var fields = CsvReader . ParseFields ( row ) ;
252+ for ( int i = 0 ; i < fields . Count ; i ++ )
253+ {
254+ var setter = i < PropertySetters . Count ? PropertySetters [ i ] : null ;
255+ if ( headers != null )
256+ PropertySettersMap . TryGetValue ( headers [ i ] , out setter ) ;
257+
258+ if ( setter == null )
259+ continue ;
260+
261+ var converter = i < PropertyConverters . Count ? PropertyConverters [ i ] : null ;
262+ if ( headers != null )
263+ PropertyConvertersMap . TryGetValue ( headers [ i ] , out converter ) ;
264+
265+ if ( converter == null )
266+ continue ;
267+
268+ var field = fields [ i ] ;
269+ var convertedValue = converter ( field ) ;
270+ setter ( o , convertedValue ) ;
271+ }
272+
273+ to . Add ( o ) ;
274+ }
275+
276+ return to ;
277+ }
278+
279+ public static T ReadRow ( string value )
280+ {
281+ if ( value == null ) return default ( T ) ; //AOT
282+
283+ return Read ( CsvReader . ParseLines ( value ) ) . FirstOrDefault ( ) ;
284+ }
285+
286+ }
287+ }
0 commit comments