Skip to content

Commit 0905126

Browse files
authored
Add files via upload
1 parent 2b34897 commit 0905126

File tree

10 files changed

+512
-6
lines changed

10 files changed

+512
-6
lines changed

src/Parsley/ColumnAttribute.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace parsley
2+
{
3+
public class ColumnAttribute : Attribute
4+
{
5+
public ColumnAttribute(int index, object defaultvalue = null)
6+
{
7+
Index = index;
8+
DefaultValue = defaultvalue;
9+
}
10+
11+
public int Index { get; }
12+
public object DefaultValue { get; }
13+
}
14+
}

src/Parsley/Extensions.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace parsley
2+
{
3+
internal static class Extensions
4+
{
5+
internal static void SetError(this IFileLine obj, string error)
6+
{
7+
if (obj.Errors == null)
8+
obj.Errors = new List<string>();
9+
10+
obj.Errors.Add(error);
11+
}
12+
}
13+
}

src/Parsley/ICustomType.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ namespace parsley
22
{
33
public interface ICustomType
44
{
5-
ICustomType Parse(string column);
5+
public ICustomType Parse(string column);
66
}
77
}

src/Parsley/IFileLine.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace parsley
2+
{
3+
public interface IFileLine
4+
{
5+
public int Index { get; set; }
6+
public IList<string> Errors { get; set; }
7+
}
8+
}

src/Parsley/IParser.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace parsley
2+
{
3+
public interface IParser
4+
{
5+
public T[] Parse<T>(string filepath) where T : IFileLine, new();
6+
7+
public T[] Parse<T>(string[] lines) where T : IFileLine, new();
8+
}
9+
}

src/Parsley/IocExtensions.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
using Microsoft.Extensions.DependencyInjection;
2-
3-
namespace parsley
4-
{
5-
public static class IocExtensions
1+
using Microsoft.Extensions.DependencyInjection;
2+
3+
namespace parsley
4+
{
5+
public static class IocExtensions
66
{
77
public static IServiceCollection UseParsley(this IServiceCollection services, char delimeter = ',')
88
{

src/Parsley/Parser.cs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
using System.ComponentModel;
2+
using System.Reflection;
3+
4+
namespace parsley
5+
{
6+
public class Parser : IParser
7+
{
8+
protected char Delimiter { get; set; }
9+
10+
public Parser() : this(',')
11+
{
12+
}
13+
14+
public Parser(char delimiter)
15+
{
16+
Delimiter = delimiter;
17+
}
18+
19+
public T[] Parse<T>(string filepath) where T : IFileLine, new()
20+
{
21+
if (string.IsNullOrEmpty(filepath) || !File.Exists(filepath))
22+
return [];
23+
24+
var lines = ReadToLines(filepath);
25+
26+
return Parse<T>(lines);
27+
}
28+
29+
public T[] Parse<T>(string[] lines) where T : IFileLine, new()
30+
{
31+
if (lines == null || lines.Length == 0)
32+
return [];
33+
34+
var list = new T[lines.Length];
35+
36+
var objLock = new object();
37+
38+
var index = 0;
39+
var inputs = lines.Select(line => new { Line = line, Index = index++ });
40+
41+
Parallel.ForEach(inputs, () => new List<T>(),
42+
(obj, loopstate, localStorage) =>
43+
{
44+
var parsed = ParseLine<T>(obj.Line);
45+
46+
parsed.Index = obj.Index;
47+
48+
localStorage.Add(parsed);
49+
return localStorage;
50+
},
51+
finalStorage =>
52+
{
53+
if (finalStorage == null)
54+
return;
55+
56+
lock (objLock)
57+
finalStorage.ForEach(f => list[f.Index] = f);
58+
});
59+
60+
return list;
61+
}
62+
63+
private string[] ReadToLines(string path)
64+
{
65+
var lines = new List<string>();
66+
67+
foreach (var line in File.ReadLines(path))
68+
{
69+
if (line != null)
70+
lines.Add(line);
71+
}
72+
73+
return lines.ToArray();
74+
}
75+
76+
private T ParseLine<T>(string line) where T : IFileLine, new()
77+
{
78+
var obj = new T();
79+
80+
var values = GetDelimiterSeparatedValues(line);
81+
82+
if (values.Length == 0 || values.Length == 1)
83+
{
84+
obj.SetError(Resources.InvalidLineFormat);
85+
return obj;
86+
}
87+
88+
var propInfos = GetLineClassPropertyInfos<T>();
89+
90+
if (propInfos.Length == 0)
91+
{
92+
obj.SetError(string.Format(Resources.NoColumnAttributesFoundFormat, typeof(T).Name));
93+
return obj;
94+
}
95+
96+
if (propInfos.Length != values.Length)
97+
{
98+
obj.SetError(Resources.InvalidLengthErrorFormat);
99+
return obj;
100+
}
101+
102+
foreach (var propInfo in propInfos)
103+
try
104+
{
105+
var attribute = (ColumnAttribute)propInfo.GetCustomAttributes(typeof(ColumnAttribute), true).First();
106+
107+
var pvalue = values[attribute.Index];
108+
109+
if (string.IsNullOrWhiteSpace(pvalue) && attribute.DefaultValue != null)
110+
pvalue = attribute.DefaultValue.ToString();
111+
112+
if (propInfo.PropertyType.IsEnum)
113+
{
114+
if (string.IsNullOrWhiteSpace(pvalue))
115+
{
116+
obj.SetError(string.Format(Resources.InvalidEnumValueErrorFormat, propInfo.Name));
117+
continue;
118+
}
119+
120+
if (long.TryParse(pvalue, out var enumLong))
121+
{
122+
var numeric = Enum.ToObject(propInfo.PropertyType, enumLong);
123+
propInfo.SetValue(obj, numeric, null);
124+
continue;
125+
}
126+
127+
var val = Enum.Parse(propInfo.PropertyType, pvalue, true);
128+
propInfo.SetValue(obj, val, null);
129+
continue;
130+
}
131+
132+
var converter = TypeDescriptor.GetConverter(propInfo.PropertyType);
133+
var value = converter.ConvertFrom(pvalue);
134+
135+
propInfo.SetValue(obj, value, null);
136+
}
137+
catch (Exception e)
138+
{
139+
obj.SetError(string.Format(Resources.LineExceptionFormat, propInfo.Name, e.Message));
140+
}
141+
142+
return obj;
143+
}
144+
145+
private static PropertyInfo[] GetLineClassPropertyInfos<T>() where T : IFileLine, new()
146+
{
147+
var propInfos = typeof(T).GetProperties()
148+
.Where(p => p.GetCustomAttributes(typeof(ColumnAttribute), true).Any() && p.CanWrite)
149+
.ToArray();
150+
return propInfos;
151+
}
152+
153+
private string[] GetDelimiterSeparatedValues(string line)
154+
{
155+
var values = line.Split(Delimiter)
156+
.Select(x => !string.IsNullOrWhiteSpace(x) ? x.Trim() : x)
157+
.ToArray();
158+
return values;
159+
}
160+
}
161+
}

src/Parsley/Parsley.csproj

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<Title>Parsley.Net</Title>
8+
<Authors>CodeShayk</Authors>
9+
<Company>CodeShayk</Company>
10+
<Description>Parsley is a .Net utility to parse Fixed width or Delimiter separated file (eg.CSV). </Description>
11+
<PackageIcon>ninja-icon-16.png</PackageIcon>
12+
<PackageReadmeFile>README.md</PackageReadmeFile>
13+
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
14+
<Copyright>Copyright (c) 2025 Code Shayk</Copyright>
15+
<RepositoryType>git</RepositoryType>
16+
<PackageTags>tsv, csv, delimiter, delimited-files, delimited-data, fixed-width, comma-separated-values, comma-separated-fields, comma-separated-text, comma-separated-file, comma-separated, fixed-width-text, delimiter-string, delimiter-separated-values, fixed-width-parser, delimiter-separated-fields, delimiter-separated-text, delimiter-separated-file, fixed-width-file, fixed-width-format, delimiter-file, delimited-file</PackageTags>
17+
<IncludeSymbols>True</IncludeSymbols>
18+
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
19+
<PackageLicenseFile>LICENSE</PackageLicenseFile>
20+
<GenerateDocumentationFile>True</GenerateDocumentationFile>
21+
<PackageProjectUrl>https://github.com/CodeShayk/Parsley.Net/wiki</PackageProjectUrl>
22+
<RepositoryUrl>https://github.com/CodeShayk/Parsley.Net</RepositoryUrl>
23+
<PackageReleaseNotes>v1.0 - Targets .Net9.0
24+
* Includes core functionality for parsing delimiter separated files.</PackageReleaseNotes>
25+
<Version>1.0.0</Version>
26+
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
27+
</PropertyGroup>
28+
29+
<ItemGroup>
30+
<None Include="..\..\Images\ninja-icon-16.png">
31+
<Pack>True</Pack>
32+
<PackagePath>\</PackagePath>
33+
</None>
34+
<None Include="..\..\LICENSE">
35+
<Pack>True</Pack>
36+
<PackagePath>\</PackagePath>
37+
</None>
38+
<None Include="..\..\README.md">
39+
<Pack>True</Pack>
40+
<PackagePath>\</PackagePath>
41+
</None>
42+
</ItemGroup>
43+
44+
<ItemGroup>
45+
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.4" />
46+
</ItemGroup>
47+
48+
</Project>

0 commit comments

Comments
 (0)