CsvMappingBuilderAddPropertyT(String, MultiColumnTypeConverterT) Method

Adds a new DynamicProperty instance that accesses several columns of a CSV file.

Definition

Namespace: FolkerKinzel.CsvTools.Mappings
Assembly: FolkerKinzel.CsvTools.Mappings (in FolkerKinzel.CsvTools.Mappings.dll) Version: 1.1.0+1263e8243dc2cd78095f678f813d7d9c52ea4315
C#
public CsvMappingBuilder AddProperty<T>(
	string propertyName,
	MultiColumnTypeConverter<T> converter
)

Parameters

propertyName  String
The identifier of the dynamic .NET property. It must follow the rules for C# identifiers. Only ASCII characters are accepted.
converter  MultiColumnTypeConverterT
An instance derived from the abstract MultiColumnTypeConverterT class. You have to write this class yourself because it depends on the CSV file.

Type Parameters

T
The .NET data type of the dynamic property.

Return Value

CsvMappingBuilder
The CsvMappingBuilder to chain calls.

Remarks

Use this method if the dynamic property is based on several columns of the CSV file.

Example

Using MultiColumnTypeConverterT to create and parse a CSV file.

(For the sake of easier readability exception handling has been omitted.)

C#
using FolkerKinzel.CsvTools;
using FolkerKinzel.CsvTools.Mappings;
using System.Diagnostics;
using System.Drawing;
// A namespace alias helps to avoid name conflicts
// with the converters from System.ComponentModel
using Conv = FolkerKinzel.CsvTools.Mappings.TypeConverters;

namespace Examples;

/// <summary>
/// Parse <see cref="Color"/> instances whose data is distributed across 
/// different columns of the CSV file.
/// </summary>
internal static class MultiColumnConverterExample
{
    /// <summary>
    /// Custom implementation of <see cref="Conv::MultiColumnTypeConverter{T}"/>
    /// for the <see cref="Color"/> struct. (A ready-to-use implementation can't 
    /// be provided as it depends on the CSV file.)
    /// </summary>
    private sealed class ColorConverter : Conv::MultiColumnTypeConverter<Color>
    {
        public override bool AcceptsNull => false;

        public ColorConverter(CsvMappingBuilder mapping)
            : base(mapping, true, Color.Transparent) { }

        // Copy ctor
        private ColorConverter(ColorConverter other) : base(other) { }

        // Using the copy ctor for cloning is required
        public override object Clone() => new ColorConverter(this);

        protected override void DoConvertToCsv(Color value)
        {
            dynamic mapping = Mapping;

            mapping.A = value.A;
            mapping.R = value.R;
            mapping.G = value.G;
            mapping.B = value.B;
        }

        protected override bool TryParse(out Color result)
        {
            dynamic mapping = Mapping;

            try
            {
                result = Color.FromArgb(mapping.A, mapping.R, mapping.G, mapping.B);
                return true;
            }
            catch (FormatException)
            {
                result = DefaultValue;
                return false;
            }
        }
    }

    internal static void ParseDataFromSeveralCsvColumns()
    {
        Conv::TypeConverter<byte> byteConverter = new Conv::ByteConverter().ToHexConverter();
        var mappingBuilder = CsvMappingBuilder.Create();

        var colorConverter = new ColorConverter(mappingBuilder.AddProperty("A", byteConverter)
                                                              .AddProperty("R", byteConverter)
                                                              .AddProperty("G", byteConverter)
                                                              .AddProperty("B", byteConverter));

        CsvMapping mapping = mappingBuilder
            .AddProperty("ColorName", ["Name"], Conv::StringConverter.CreateNullable())
            .AddProperty("Color", colorConverter)
            .Build();

        DirectoryInfo tmpDirectory = Directory.CreateTempSubdirectory();
        string csvPath = Path.Combine(tmpDirectory.FullName, "Colors.csv");

        CreateCsvFile(csvPath, mapping);
        Console.WriteLine(File.ReadAllText(csvPath));

        ShowCsvContentInBrowser(mapping, csvPath);

        Thread.Sleep(5000);
        tmpDirectory.Delete(true);
    }

    private static void CreateCsvFile(string csvPath, dynamic mapping)
    {
        using CsvWriter writer = Csv.OpenWrite(csvPath, ["Name", "A", "R", "G", "B"]);
        mapping.Record = writer.Record;

        mapping.ColorName = nameof(Color.CornflowerBlue);
        mapping.Color = Color.CornflowerBlue;
        writer.WriteRecord();

        mapping.ColorName = nameof(Color.LawnGreen);
        mapping.Color = Color.LawnGreen;
        writer.WriteRecord();

        mapping.ColorName = nameof(Color.Salmon);
        mapping.Color = Color.Salmon;
        writer.WriteRecord();
    }

    private static void ShowCsvContentInBrowser(CsvMapping mapping, string csvPath)
    {
        string htmlPath = Path.Combine(Path.GetDirectoryName(csvPath) ?? "", "colors.htm");
        CreateHtmlFile(htmlPath, csvPath, mapping);
        _ = Process.Start(new ProcessStartInfo { FileName = htmlPath, UseShellExecute = true });
    }

    private static void CreateHtmlFile(string htmlPath, string csvPath, dynamic mapping)
    {
        var htmlFile = new FileInfo(htmlPath);
        using StreamWriter writer = htmlFile.AppendText();

        writer.WriteLine("""
            <html>
            <head>
            <title>Colors From CSV</title>
            <style>
            table { font-size: 200%; }
            th, td { padding: 30px; }
            </style>
            </head>
            <body>
            <table>
            <thead>
            <tr><th>Name</th><th>Color</th></tr>
            </thead>
            <tbody>
            """);

        using CsvReader csvReader = Csv.OpenRead(csvPath);

        foreach (CsvRecord record in csvReader)
        {
            mapping.Record = record;
            writer.Write("<tr><td>");
            writer.Write(mapping.ColorName);
            writer.Write("</td>");
            writer.Write("<td style=\"background-color: #");
            writer.Write((mapping.Color.ToArgb() & 0xFFFFFF).ToString("x"));
            writer.Write(";\" />");
            writer.WriteLine("</td></tr>");
        }

        writer.WriteLine("""
            </tbody>
            </table>
            </body>
            </html>
            """);
    }
}

/*
Console Output:

Name,A,R,G,B
CornflowerBlue,FF,64,95,ED
LawnGreen,FF,7C,FC,0
Salmon,FF,FA,80,72
*/

Exceptions

ArgumentNullExceptionpropertyName or converter is null.
ArgumentException

propertyName does not conform to the rules for C# identifiers (only ASCII characters)

- or -

a DynamicProperty with the same PropertyName has already been added.

RegexMatchTimeoutException Validating of propertyName takes too long.

See Also