diff --git a/docs/_data/navigation_docs.yml b/docs/_data/navigation_docs.yml index 7999f56..2efb3ee 100644 --- a/docs/_data/navigation_docs.yml +++ b/docs/_data/navigation_docs.yml @@ -14,5 +14,6 @@ - installation - basic-usage - fluent-api-profile-configuration + - fluent-api-schema - mapper-object-builder diff --git a/docs/_docs/csv-dialect-descriptor.md b/docs/_docs/csv-dialect-descriptor.md index f08d9cf..716559d 100644 --- a/docs/_docs/csv-dialect-descriptor.md +++ b/docs/_docs/csv-dialect-descriptor.md @@ -2,7 +2,7 @@ title: CSV dialect descriptor tags: [configuration] --- -The `CsvDialectDescriptor` class provides extensive configuration options for tuning the behavior of CSV parsing operations. Below is an explanation of each property and its potential impact on CSV processing. +The `CsvDialectDescriptor` class provides extensive configuration options for tuning the behavior of CSV parsing operations. Below is an explanation of each property and its potential impact on CSV processing. The description of PocketCsvReader is aligned with the [CSV Dialect Specification](https://specs.frictionlessdata.io/csv-dialect/#specification) provided by Frictionless Data. diff --git a/docs/_docs/fluent-api-schema.md b/docs/_docs/fluent-api-schema.md new file mode 100644 index 0000000..aea7109 --- /dev/null +++ b/docs/_docs/fluent-api-schema.md @@ -0,0 +1,113 @@ +--- +title: Fluent API for Schema +tags: [configuration] +--- + +## Overview + +The Fluent API for schema definition in PocketCsvReader provides an intuitive and expressive way to define the structure of CSV data. This is particularly useful when working with `IDataReader`, where the `GetValue` method returns a boxed `object`. This powerful feature enables dynamic retrieval of any column's value without prior type knowledge, making it highly flexible for handling various data types. It seamlessly integrates with schema definitions to ensure proper casting and minimize conversion overhead.. + +Defining a schema ensures that values are correctly interpreted and cast to their expected types, avoiding unnecessary type conversions at runtime. + +## Defining a Schema + +PocketCsvReader provides two ways to define schemas: + +- Indexed Schema: Fields are defined by their position (index) in the dataset. +- Named Schema: Fields are defined by their column names. + +### Creating an Indexed Schema + +Indexed schemas are useful when working with CSV files that do not contain headers or when column order is fixed. + +Example: + +```csharp +var schema = new SchemaDescriptorBuilder() + .Indexed() + .WithField() + .WithField(x => x.WithName("Description")) + .Build(); +``` + +In this example: + +- The first column is an int. +- The second column is a string with the name "Description". + +### Creating a Named Schema + +Named schemas provide more flexibility when working with CSV files that contain headers. + +Example: + +```csharp +var schema = new SchemaDescriptorBuilder() + .Named() + .WithField("ID") + .WithField("Description") + .Build(); +``` + +Here, the schema explicitly assigns types to fields based on column names. + +## Using Field Formatting and Format Descriptors + +The `WithFormat()` method allows specifying a format for fields that require special parsing, such as `DateTime` values, and relies on format descriptor builders like `IntegerFormatDescriptorBuilder`, `NumberFormatDescriptorBuilder`, and `TemporalFormatDescriptorBuilder` to handle culture-specific formatting details. This format is passed to the parser of the respective type, ensuring correct conversion from text to the expected type. + +**Example:** + +```csharp +var schema = new SchemaDescriptorBuilder() + .Named() + .WithField("Date", x => x.WithFormat("dd/MM/yyyy")) + .Build(); +``` + +In this example, the "Date" field is expected to be in the format `dd/MM/yyyy` (e.g., `25/12/2024`). The parser will use this format to correctly interpret and convert the string into a DateTime object. + +Using `WithFormat()` ensures that structured data such as dates are properly parsed and prevents errors due to mismatched formats. The `TemporalFormatDescriptorBuilder` provides control over date and time separators, ensuring compatibility with different cultural representations. + +### Numeric Formatting + +The `NumericFieldDescriptorBuilder` allows further customization of numeric fields: + +- `.WithDecimalChar(char decimalChar)`: Defines the character used for the decimal separator. +- `.WithGroupChar(char? groupChar)`: Defines the character used for digit grouping. Passing null removes grouping. +- `.WithoutGroupChar()`: Explicitly disables grouping. + +Example: + +```csharp +var schema = new SchemaDescriptorBuilder() + .Named() + .WithNumericField("Amount", x => x.WithDecimalChar(',') + .WithoutGroupChar()) + .Build(); +``` + +This defines an "Amount" field as a double, using `,` as the decimal separator and disabling digit grouping. + +### Custom Field Formatting + +```csharp +var schema = new SchemaDescriptorBuilder() + .Named() + .WithCustomField("Location", x => x.WithFormat("x;y")) + .Build(); +``` + +This ensures that the "Location" field is interpreted as a `Point` and formatted accordingly. + +When assigning a custom field, the parser is automatically searched for a method named `Parse` that accepts a string (the span to read) and an `IFormatProvider` as the last argument. Optionally, a second argument of type string can be provided to accept a format. + +## Benefits of Using a Schema + +- Ensures Type Safety: The schema guarantees that values are returned in their expected type. +- Simplifies Parsing: Eliminates the need for manual type conversion when using IDataReader.GetValue. +- Improves Readability: Fluent API provides a clean and declarative way to define schemas. +- Customizable Numeric Fields: Allows control over decimal and grouping characters for numeric fields. + +## Conclusion + +Using the Fluent API for schema definition in PocketCsvReader significantly enhances the usability and reliability of working with CSV data, especially when processing untyped data from an IDataReader. By leveraging indexed or named schemas, developers can streamline their data processing workflows while ensuring type safety and maintainability. diff --git a/docs/_docs/mapper-object-builder.md b/docs/_docs/mapper-object-builder.md index 9008b3b..f2a61c4 100644 --- a/docs/_docs/mapper-object-builder.md +++ b/docs/_docs/mapper-object-builder.md @@ -4,10 +4,9 @@ tags: [configuration] --- This documentation explains how to use the `SpanMapper` and `SpanObjectBuilder` classes for mapping and parsing flat-file data. Their primary purpose is to configure the mapping of fields from delimited data to a strongly-typed class. +These features are designed to work with the `To` method, which facilitates conversion of CSV rows into instances of T. Each row's fields are mapped according to the schema defined in `SpanObjectBuilder` or the `SpanMapper` delegate, ensuring accurate transformation into structured objects. -## Delegates - -### SpanMapper<T> +## SpanMapper<T> ```csharp public delegate T SpanMapper(ReadOnlySpan span, IEnumerable fieldSpans); @@ -19,32 +18,44 @@ The `SpanMapper` delegate maps data from a `ReadOnlySpan` representing - **`span`**: The source `ReadOnlySpan` containing the delimited row data. - **`fieldSpans`**: A collection of `FieldSpan` objects defining the start position and length of each field in the row. -### Parse +## Class SpanObjectBuilder<T> + +The `SpanObjectBuilder` class is designed to instantiate strongly-typed objects (`T`) from delimited flat-file data. It supports default parsers for common data types and allows customization via the `SetParser` method and the `Parse` delegate. + +### `Instantiate` a SpanObjectBuilder ```csharp -public delegate object? Parse(ReadOnlySpan span); +public T Instantiate(ReadOnlySpan span, IEnumerable fieldSpans) ``` **Purpose:** -The `Parse` delegate defines a method for parsing a `ReadOnlySpan` into an object of a specific type. It is used to handle custom parsing for various data types in the `SpanObjectBuilder` class. +Creates an instance of type `T` using constructor injection. The fields in the constructor are populated based on `fieldSpans` and the mapped parsers in `ParserMapping`. -- **`span`**: The `ReadOnlySpan` containing the value to parse. +- **`span`**: The `ReadOnlySpan` containing the delimited row data. +- **`fieldSpans`**: A collection of `FieldSpan` objects specifying the position and length of each field. -## Class SpanObjectBuilder<T> +**Behavior:** -The `SpanObjectBuilder` class is designed to instantiate strongly-typed objects (`T`) from delimited flat-file data using `SpanMapper` and the `Parse` delegates. It supports default parsers for common data types and allows customization via the `SetParser` method. +1. Identifies the appropriate constructor of `T` by matching the number of fields in `fieldSpans`. +2. Iterates through each `FieldSpan`, using the associated parser to convert the field to the required type. +3. If a type lacks a parser, throws an exception. +4. If parsing fails, throws a `FormatException` with detailed error information. -### Default Parsers +**Example:** -By default, the `SpanObjectBuilder` supports the following types: +```csharp +var builder = new SpanObjectBuilder(); +var spans = new List +{ + new FieldSpan { Start = 0, Length = 5 }, // Field 1 + new FieldSpan { Start = 6, Length = 10 } // Field 2 +}; +var obj = builder.Instantiate("12345 true", spans); +``` -- Strings -- Numbers (`int`, `long`, `short`, `byte`, `float`, `double`, `decimal`) -- Booleans -- Dates (`DateTime`, `DateOnly`, `TimeOnly`, `DateTimeOffset`) -- Characters (`char`) +### Specifying the field parsers -### SetParser<T>TField<T> +#### SetParser<TField> If you need to parse additional types or override the default behavior, use the `SetParser` method. @@ -61,36 +72,26 @@ var builder = new SpanObjectBuilder(); builder.SetParser(s => Guid.Parse(s)); ``` -### `Instantiate` +#### Parse delegate ```csharp -public T Instantiate(ReadOnlySpan span, IEnumerable fieldSpans) +public delegate object? Parse(ReadOnlySpan span); ``` **Purpose:** -Creates an instance of type `T` using constructor injection. The fields in the constructor are populated based on `fieldSpans` and the mapped parsers in `ParserMapping`. - -- **`span`**: The `ReadOnlySpan` containing the delimited row data. -- **`fieldSpans`**: A collection of `FieldSpan` objects specifying the position and length of each field. +The `Parse` delegate defines a method for parsing a `ReadOnlySpan` into an object of a specific type. It is used to handle custom parsing for various data types in the `SpanObjectBuilder` class. -**Behavior:** +- **`span`**: The `ReadOnlySpan` containing the value to parse. -1. Identifies the appropriate constructor of `T` by matching the number of fields in `fieldSpans`. -2. Iterates through each `FieldSpan`, using the associated parser to convert the field to the required type. -3. If a type lacks a parser, throws an exception. -4. If parsing fails, throws a `FormatException` with detailed error information. +#### Default Parsers -**Example:** +By default, the `SpanObjectBuilder` supports the following types: -```csharp -var builder = new SpanObjectBuilder(); -var spans = new List -{ - new FieldSpan { Start = 0, Length = 5 }, // Field 1 - new FieldSpan { Start = 6, Length = 10 } // Field 2 -}; -var obj = builder.Instantiate("12345 true", spans); -``` +- Strings +- Numbers (`int`, `long`, `short`, `byte`, `float`, `double`, `decimal`) +- Booleans +- Dates (`DateTime`, `DateOnly`, `TimeOnly`, `DateTimeOffset`) +- Characters (`char`) ### To<T> Method