diff --git a/PocketCsvReader.Testing/CsvDataReaderTest.cs b/PocketCsvReader.Testing/CsvDataReaderTest.cs index 76b6de9..fb0efa6 100644 --- a/PocketCsvReader.Testing/CsvDataReaderTest.cs +++ b/PocketCsvReader.Testing/CsvDataReaderTest.cs @@ -330,7 +330,16 @@ public void GetValue_WithSchemaAndFormatDateTimeAndShort_CorrectParsing(string r Assert.That(dataReader.GetFieldType(0), Is.EqualTo(typeof(DateTime))); Assert.That(dataReader.GetFieldType(1), Is.EqualTo(typeof(short))); Assert.That(dataReader.GetValue(0), Is.EqualTo(new DateTime(2025, 1, 4, 14, 35, 8))); + Assert.That(dataReader[0], Is.EqualTo(new DateTime(2025, 1, 4, 14, 35, 8))); + Assert.That(dataReader.GetDate(0), Is.EqualTo(new DateOnly(2025, 1, 4))); + Assert.That(dataReader.GetTime(0), Is.EqualTo(new TimeOnly(14, 35, 8))); Assert.That(dataReader.GetValue(1), Is.EqualTo((short)108)); + Assert.That(dataReader[1], Is.EqualTo((short)108)); + Assert.That(dataReader.GetInt32(1), Is.EqualTo(108)); + Assert.That(dataReader.GetInt64(1), Is.EqualTo((short)108)); + Assert.That(dataReader.GetFloat(1), Is.EqualTo((float)108)); + Assert.That(dataReader.GetDouble(1), Is.EqualTo((double)108)); + Assert.That(dataReader.GetDecimal(1), Is.EqualTo((decimal)108)); } [Test] @@ -367,6 +376,35 @@ public void GetFieldValue_WithSchemaAndFormatDateTimeAndShort_CorrectParsing(str Assert.That(dataReader.GetFieldValue(1), Is.EqualTo(108m)); } + [Test] + [TestCase("foo;bar\r\n2025-01-04T14:35:08;108")] + public void GetFieldValue_WithoutSchema_CorrectParsing(string record) + { + var buffer = new MemoryStream(Encoding.UTF8.GetBytes(record)); + + var builder = new CsvReaderBuilder() + .WithDialect( + (dialect) => dialect + .WithDelimiter(';') + .WithHeader(true) + ); + using var dataReader = builder.Build().ToDataReader(buffer); + dataReader.Read(); + Assert.That(dataReader.FieldCount, Is.EqualTo(2)); + Assert.That(dataReader.GetName(0), Is.EqualTo("foo")); + Assert.That(dataReader.GetName(1), Is.EqualTo("bar")); + Assert.That(dataReader.GetFieldType(0), Is.EqualTo(typeof(object))); + Assert.That(dataReader.GetFieldType(1), Is.EqualTo(typeof(object))); + Assert.That(dataReader.GetFieldValue(0), Is.TypeOf()); + Assert.That(dataReader.GetFieldValue(1), Is.TypeOf()); + Assert.That(dataReader.GetFieldValue(1), Is.TypeOf()); + Assert.That(dataReader.GetFieldValue(1), Is.TypeOf()); + Assert.That(dataReader.GetFieldValue(0), Is.EqualTo(new DateTime(2025, 1, 4, 14, 35, 8))); + Assert.That(dataReader.GetFieldValue(1), Is.EqualTo((short)108)); + Assert.That(dataReader.GetFieldValue(1), Is.EqualTo(108)); + Assert.That(dataReader.GetFieldValue(1), Is.EqualTo(108m)); + } + [Test] [TestCase("Ansi")] [TestCase("Utf16-BE")] diff --git a/PocketCsvReader/CsvDataReader.cs b/PocketCsvReader/CsvDataReader.cs index ec34928..8f0d18d 100644 --- a/PocketCsvReader/CsvDataReader.cs +++ b/PocketCsvReader/CsvDataReader.cs @@ -8,6 +8,8 @@ using System.Threading.Tasks; using System.Globalization; using PocketCsvReader.Configuration; +using System.Reflection; +using System.Xml.Linq; namespace PocketCsvReader; public class CsvDataReader : IDataReader @@ -141,16 +143,7 @@ private void HandleUnexpectedFields(int expectedLength) public object this[int i] { - get - { - if (i < Record!.FieldSpans.Length && Fields!.Length > 0) - return Record.Slice(i).ToString(); - if (i < Fields!.Length) - return Profile.ParserOptimizations.HandleSpecialValues ? Profile.MissingCell : string.Empty; - if (Fields!.Length == 0) - throw new InvalidOperationException("Values are not defined yet."); - throw new IndexOutOfRangeException("Index out of range."); - } + get => GetValue(i); } public object this[string name] @@ -160,11 +153,7 @@ public object this[string name] if (Fields is null) throw new InvalidOperationException("Fields are not defined yet."); var index = Array.IndexOf(Fields, name); - if (index < Record!.FieldSpans.Length) - return Record.Slice(index).ToString(); - if (index < Fields!.Length) - return Profile.ParserOptimizations.HandleSpecialValues ? Profile.MissingCell : string.Empty; - throw new InvalidOperationException($"Field '{name}' not found."); + return GetValue(index); } } @@ -190,7 +179,7 @@ public DateTime GetDateTime(int i) var netFormat = new DateTimeFormatConverter().Convert(field.Format); return DateTime.ParseExact(GetValueOrThrow(i), netFormat, CultureInfo.InvariantCulture); } - + return DateTime.Parse(GetValueOrThrow(i), CultureInfo.InvariantCulture); } @@ -231,7 +220,11 @@ public DateTimeOffset GetDateTimeOffset(int i) public double GetDouble(int i) => double.Parse(GetValueOrThrow(i), CultureInfo.InvariantCulture); [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] public Type GetFieldType(int i) - => GetFieldDescriptor(i).RuntimeType; + { + if (TryGetFieldDescriptor(i, out var field)) + return field.RuntimeType; + return typeof(object); + } protected FieldDescriptor GetFieldDescriptor(int i) { @@ -309,6 +302,8 @@ public object GetValue(int i) { if (i >= FieldCount) throw new IndexOutOfRangeException($"Field index '{i}' is out of range."); + if (i < Fields!.Length && i >= Record!.FieldSpans.Length) + return Profile.ParserOptimizations.HandleSpecialValues ? Profile.MissingCell : string.Empty; if (!TryGetFieldDescriptor(i, out var field) || !TypeFunctions.TryGetFunction(field.RuntimeType, out var func)) @@ -322,8 +317,7 @@ public T GetFieldValue(int i) if (i >= FieldCount) throw new IndexOutOfRangeException($"Field index '{i}' is out of range."); - if (!TryGetFieldDescriptor(i, out var field) - || !TypeFunctions.TryGetFunction(out var func)) + if (!TypeFunctions.TryGetFunction(out var func)) throw new NotImplementedException($"No function registered for type {typeof(T).Name}"); return func.Invoke(i); @@ -382,7 +376,7 @@ public bool TryGetFunction([NotNullWhen(true)] out Func? func) { if (_typeToFunctionMap.TryGetValue(typeof(T), out var value)) { - func = (Func)value; + func = (Func)value; return true; } func = null;