Skip to content

Commit

Permalink
Merge pull request #29 from LuigimonSoft/Feature/LC-21-converts-a-col…
Browse files Browse the repository at this point in the history
…lection-of-model-objects-into-csv-lines

#21 add Methods to write models to CSV format
  • Loading branch information
LuigimonSoft authored Feb 24, 2024
2 parents 21db6fa + c0a8ccd commit d310495
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 11 deletions.
18 changes: 18 additions & 0 deletions CsvDataMapper.Core/Attributes/ColumnOrderAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CsvDataMapper.Core.Attributes
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class ColumnOrderAttribute : Attribute
{
public int Order { get; }
public ColumnOrderAttribute(int order)
{
Order = order;
}
}
}
65 changes: 54 additions & 11 deletions CsvDataMapper.Core/Services/CsvService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public CsvService(ICsvRepository csvRepository, char delimiter, bool hasHeader)

Datacolumns = line.Split(_delimiter);

if(Datacolumns.Contains("[") && Datacolumns.Contains("]"))
if (Datacolumns.Contains("[") && Datacolumns.Contains("]"))
{
for (int i = 0; i < Datacolumns.Length; i++)
{
Expand Down Expand Up @@ -160,14 +160,14 @@ public CsvService(ICsvRepository csvRepository, char delimiter, bool hasHeader)
{
string? line = string.Empty;
string[] columnsHeaders = Array.Empty<string>();

var properties = typeof(TModel).GetProperties();
var columns = new List<CsvColumn>();

if (_hasHeader)
{
line = await _csvRepository.ReadCsvFileLineAsync();
if(line == null)
if (line == null)
throw new CsvDataMapperException(ErrorCode.InvalidCsvFormat);

columnsHeaders = line.Split(_delimiter);
Expand All @@ -193,7 +193,7 @@ public CsvService(ICsvRepository csvRepository, char delimiter, bool hasHeader)
while ((line = await _csvRepository.ReadCsvFileLineAsync()) != null)
{
var model = new TModel();
string[] Datacolumns =new string[] { };
string[] Datacolumns = new string[] { };
if (line.Contains("[") && line.Contains("]"))
{
int ini = line.IndexOf("[");
Expand All @@ -203,7 +203,7 @@ public CsvService(ICsvRepository csvRepository, char delimiter, bool hasHeader)
line = line.Replace(line.Substring(ini, end - ini + 1), substring);

}

Datacolumns = line.Split(_delimiter);

if (_hasHeader)
Expand All @@ -228,7 +228,7 @@ public CsvService(ICsvRepository csvRepository, char delimiter, bool hasHeader)
if (Datacolumns[i].Contains("[") && Datacolumns[i].Contains("]"))
{
Datacolumns[i] = Datacolumns[i].Replace("[", "");
Datacolumns[i] = Datacolumns[i].Replace("]", "").Replace("\"","");
Datacolumns[i] = Datacolumns[i].Replace("]", "").Replace("\"", "");
var dataArray = Datacolumns[i].Trim().Count() > 0 ? Datacolumns[i].Split("|").ToList() : new List<string>();
column.PropertyInfo.SetValue(model, dataArray);
}
Expand All @@ -245,9 +245,9 @@ public CsvService(ICsvRepository csvRepository, char delimiter, bool hasHeader)
column.PropertyInfo.SetValue(model, Convert.ChangeType(Datacolumns[i], column.PropertyInfo.PropertyType, CultureInfo.InvariantCulture));
}
}



models.Add(model);
}
}
Expand Down Expand Up @@ -308,15 +308,15 @@ public async Task<List<Dictionary<string, string>>> ReadCsvAsDynamicAsync()
if (_hasHeader)
{
line = await _csvRepository.ReadCsvFileLineAsync();
if(String.IsNullOrEmpty(line))
if (String.IsNullOrEmpty(line))
throw new CsvDataMapperException(ErrorCode.InvalidCsvFormat);
headers.AddRange(line.Split(_delimiter));
}

while ((line = await _csvRepository.ReadCsvFileLineAsync()) != null)
{
var columns = line.Split(_delimiter);

var row = new Dictionary<string, string>();
for (int i = 0; i < columns.Length; i++)
{
Expand All @@ -328,5 +328,48 @@ public async Task<List<Dictionary<string, string>>> ReadCsvAsDynamicAsync()

return result;
}

public bool WriteModelToCsv<TModel>(IEnumerable<TModel> models) where TModel : new()
{
return _csvRepository.WriteLines(WriteModel(models));
}

public async Task<bool> WriteModelToCsvAsync<TModel>(IEnumerable<TModel> models) where TModel : new()
{
return await _csvRepository.WriteLinesAsync(WriteModel(models));
}

private List<string> WriteModel<TModel>(IEnumerable<TModel> models) where TModel : new ()
{
var lines = new List<string>();

var properties = typeof(TModel).GetProperties()
.Select(p => new
{
Property = p,
Order = p.GetCustomAttribute<ColumnOrderAttribute>()?.Order ?? int.MaxValue,
ColumnName = p.GetCustomAttribute<ColumnNameAttribute>()?.ColumnName ?? p.Name
})
.OrderBy(p => p.Order)
.ToList();

var header = string.Join(",", properties.Select(p => "\"" + p.ColumnName.Replace("\"", "\"\"") + "\""));
lines.Add(header);


foreach (var model in models)
{
var lineValues = properties.Select(prop =>
{
var rawValue = prop.Property.GetValue(model)?.ToString() ?? "";
var escapedValue = rawValue.Replace("\"", "\"\"");
return (rawValue.Contains(",") || rawValue.Contains("\"")) ? $"\"{escapedValue}\"" : escapedValue;
});

lines.Add(string.Join(",", lineValues));
}

return lines;
}
}
}
3 changes: 3 additions & 0 deletions CsvDataMapper.Core/Services/Interfaces/ICsvService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@ internal interface ICsvService
IList<TModel> MapCsvToListModel<TModel>() where TModel : new();
Task<List<Dictionary<string, string>>> ReadCsvAsDynamicAsync();
List<Dictionary<string, string>> ReadCsvAsDynamic();

Task<bool> WriteModelToCsvAsync<TModel>(IEnumerable<TModel> models) where TModel : new();
bool WriteModelToCsv<TModel>(IEnumerable<TModel> models) where TModel : new();
}
}

0 comments on commit d310495

Please sign in to comment.