FixedWidthParserWriter 1.2.0

dotnet add package FixedWidthParserWriter --version 1.2.0                
NuGet\Install-Package FixedWidthParserWriter -Version 1.2.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FixedWidthParserWriter" Version="1.2.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add FixedWidthParserWriter --version 1.2.0                
#r "nuget: FixedWidthParserWriter, 1.2.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install FixedWidthParserWriter as a Cake Addin
#addin nuget:?package=FixedWidthParserWriter&version=1.2.0

// Install FixedWidthParserWriter as a Cake Tool
#tool nuget:?package=FixedWidthParserWriter&version=1.2.0                

FixedWidthParserWriter

Library (C# .NET) for Parsing(Reading) & Writing fixed-width/flat data files (.txt, others).
Uses FastMember instead of slower Reflection.
It is targeting NetStandard 2.0 so it can be used on project targeting NetCore(2.0+) or NetFramework(4.6.1+).

Available on NuGet latest version.
Package manager console command for installation: Install-Package FixedWidthParserWriter

There are 2 main types of usage that are explained in the following segments:
1. LineFields
2. FileFields and
3. CustomFileFields
Both are simple to use and easily configured with Attributes.

Contributing

If you find this project useful you can mark it by leaving a Github *Star.

Please read CONTRIBUTING for details on code of conduct, and the process for submitting pull requests.
NuGet
Want to Contact us for Development & Consulting: www.codis.tech

Also take a look into others packages:
-Open source (MIT or cFOSS) authored .Net libraries (@Infopedia.io personal blog post) | № | .Net library | Description | | - | ------------------------ | -------------------------------------------------------- | | 1 | EFCore.BulkExtensions | EF Core Bulk CRUD Ops (Flagship Lib) | | 2 | EFCore.UtilExtensions | EF Core Custom Annotations and AuditInfo | | 3 | EFCore.FluentApiToAnnotation | Converting FluentApi configuration to Annotations | | 4 | FixedWidthParserWriter | Reading & Writing fixed-width/flat data files | | 5 | CsCodeGenerator | C# code generation based on Classes and elements | | 6 | CsCodeExample | Examples of C# code in form of a simple tutorial |

1. Data in LineFields

First is regular flat data file;
Record per Line (Fixed-Width), for example:

No |         Description         | Qty |   Price    |   Amount   |
  1.Laptop Dell xps13                  1       821.00       821.00
  2.Monitor Asus 32''                  2       478.00       956.00

For parsing/writing we make a model which Properties have [FixedWidthLineField] Attribute:

public class InvoiceItem
{
    [FixedWidthLineField(Start = 1, Length = 3)]
    public int Number { get; set; }

    [FixedWidthLineField(Start = 4, Length = 1)]
    public string NumberedBullet { get; set; } = ".";

    [FixedWidthLineField(Start = 5, Length = 30)]
    public string Description { get; set; }

    [FixedWidthLineField(Start = 35, Length = 6)]
    public int Quantity { get; set; }

    [FixedWidthLineField(Start = 41, Length = 13)]
    public decimal Price { get; set; }

    [FixedWidthLineField(Start = 54, Length = 13)]
    public decimal Amount => Quantity * Price;
}

Then we can call it like this:

// dataLines stripped of header
public List<Invoiceitem> ParseFieldsFromLines(new List<string> dataLines)
{
    List<InvoiceItem> invoiceItems = new FixedWidthLinesProvider<InvoiceItem>().Parse(dataLines);
    return invoiceItems;
}

public List<string> WriteFieldsToLines(List<InvoiceItem> invoiceItems)
{
    List<string> dataLines = new FixedWidthLinesProvider<InvoiceItem>().Write(invoiceItems);
    return dataLines;
}

[FixedWidthLineField] has following parameters that can be configured for each Property:

  • Start - required for LineType so that order of lineFields does not depends on order of modelPropertis
  • Length - when writing if Property value longer then defined in Length it is cut from the right to fit - valueTrim;
                  zero 0 value means entire line and negative values means start from Right side
  • Format - Defaults per data type or group
  • Pad - Defaults per data category: { PadNumeric = ' ', PadNonNumeric = ' ' }
  • PadSide - Defaults per data category: { PadSideNumeric = PadSide.Left, PadSideNonNumeric = PadSide.Right }
  • DoTrim - Default is 'True' when text will be trimmed before casting
  • StructureTypeId - Default = 0, used when having multiple files with different structure or format for same data
  • NullPattern - Default = string.Empty, Pattern used to represent a null value

*_Format types:
-FormatNumberInteger Default = "0", *groupFormat:Int32,Int64
-FormatNumberDecimal Default = "0.00", *groupFormat:Decimal,Single,Double
                                      ("0;00" - Special custom Format that removes decimal separator: 123.45 → 12345)</pre>
-FormatBoolean . . . . . . Default = "1; ;0" ("ValueForTrue; ValueForNull; ValueForFalse")
-FormatDateTime. . . . . .Default = "yyyyMMdd"
Custom format strings for Numeric and DateTime.

*_Special feature is 'DYNAMIC Settings' with which Attributes values can be defined at runtime, for all usage types.
Data is forwarded using Dict with PropertyName and new independent Attribute with parameter values: Dictionary<string, FixedWidthAttribute> dynamicSettings.
It can be sett for all needed Properties when having no Attributes, or just add/override some specific. And if need to exclude ones that has regular Atribute then set it with Null.
Sample in test LineParserTest.

When need more then 1 file structure/format we can put multiple Attributes per Property with different StructureTypeId.
Next example shows 2 structures, second has one less Property and different PadNumeric: '0' instead of ' '(space).
To change DefaultConfig per StructureType, model should implement IFixedWidth interface with SetDefaultConfig() func.

public enum ConfigType { Alpha, Beta }

public class InvoiceItem : IFixedWidth
{
    public DefaultConfig GetDefaultConfig(int StructureTypeId)
    {
        var defaultConfig = new DefaultConfig();
        switch ((ConfigType)StructureTypeId)
        {
            case ConfigType.Alpha:
                // config remains initial default
                break;
            case ConfigType.Beta:
                defaultConfig.PadNumeric = '0';
                break;
        }
        return defaultConfig;
    }

    [FixedWidthLineField(StructureTypeId = (int)ConfigType.Alpha, Start = 1, Length = 3)]
    [FixedWidthLineField(StructureTypeId = (int)ConfigType.Beta,  Start = 1, Length = 4)]
    public int Number { get; set; }

    [FixedWidthLineField(StructureTypeId = (int)ConfigType.Alpha, Start = 4, Length = 1)]
    public string NumberedBullet { get; set; } = ".";

    [FixedWidthLineField(StructureTypeId = (int)ConfigType.Alpha, Start = 5, Length = 30)]
    [FixedWidthLineField(StructureTypeId = (int)ConfigType.Beta,  Start = 5, Length = 30)]
    public string Description { get; set; }

    //... Others Properties
}

Beta Structure:

No |         Description         | Qty |   Price    |   Amount   |
0001Laptop Dell xps13             0000010000000821.000000000821.00
0002Monitor Asus 32''             0000020000000478.000000000956.00

Calling the methods:

var linesProvider = new FixedWidthLinesProvider<InvoiceItem>();
List<InvoiceItem> itemsA = linesProvider.Parse(dataLinesA, (int)ConfigType.Alpha);
List<InvoiceItem> itemsB = linesProvider.Parse(dataLinesB, (int)ConfigType.Alpha);

PARSE method for all use cases can also optionally have third parameter List<string> errorLog which when sent as Empty list(not null) will be loaded with list of Exceptions if any were to happen during parsing and casting operations. When method is called without this param, which remains null, in that case first error with throw Exception and procedure will be stopped.

Full Examples are in Tests of the project.

2. Data in FileFields

Second usage is when one data record is in different rows at defined positions;
Record per File (Fixed/Relative-Height), E.g.:

SoftysTech LCC
__________________________________________________________________

Invoice Date: 2018-10-30         Buyer:   SysCompanik

                        INVOICE no. 0169/18
						
No |         Description         | Qty |   Price    |   Amount   |
...
...
------------------------------------------------------------------
                                                          1,299.00 

Date: 2018-10-31                                 Financial Manager
                                                          John Doe

For parsing/writing [FixedWidthFileField] attributes are used, that have additional parameter:

  • Line - in which we define line number where the value is (Negative values represent row number from bottom)

For type FileField param. Length not required, if not set means value goes till end of row(trimmed), and Start has default = 1.

public class Invoice
{
    [FixedWidthFileField(Line = 1)]
    public string CompanyName { get; set; }

    [FixedWidthFileField(Line = 4, Start = 15, Length = 19, Format = "yyyy-MM-dd")]
    public DateTime Date { get; set; }

    [FixedWidthFileField(Line = 4, Start = 43)]
    public string BuyerName { get; set; }

    [FixedWidthFileField(Line = 6, Start = 37)]
    public string InvoiceNumber { get; set; }

    [FixedWidthFileField(Line = -4, Length = 66, Format = "0,000.00")]
    public decimal AmountTotal { get; set; }

    [FixedWidthFileField(Line = -2, Start = 7, Length = 10, Format = "yyyy-MM-dd")]
    public DateTime DateCreated { get; set; }

    [FixedWidthFileField(Line = -2, Start = 17, Length = 50, PadSide = PadSide.Left)]
    public string SignatoryTitle { get; set; }

    // Line Negative - counted from bottom 
    [FixedWidthFileField(Line = -1, Length = 66, PadSide = PadSide.Left)]
    public string SignatureName { get; set; }
}

Usage:

public Invoice ParseFieldsFromFile(new List<string> fileLines)
{
    invoice invoice = new FixedWidthFileProvider<Invoice>().Parse(fileLines);
    return invoice;
}

public List<string> WriteFieldsToFile(Invoice)
{
    List<string> templateLines = GetDataFormTemplate();
    var fileProvider = new FixedWidthFileProvider<Invoice>() { Content = templateLines };
    fileProvider.UpdateContent(invoice);
    invoice.UpdateContent();
    return invoice.Content;
}

DataFormTemplate looks like this:

{CompanyName}
__________________________________________________________________

Invoice Date: {InvoiceDate}      Buyer:   {BuyerName}

                        INVOICE no. NNNN/YY
						
No |         Description         | Qty |   Price    |   Amount   |
...
...
------------------------------------------------------------------
                                                              0.00 

Date: {DateCreated}                               {SignatoryTitle}
                                                   {SignatureName}

In situation where many same type properties have Format different from default one, instead of setting custom format individually for each Property, it is possible to override DefaultConfig for certain data types/groups in that class:

public class Invoice : IFixedWidth
{
    public DefaultConfig GetDefaultConfig(int StructureTypeId)
    {
        return new DefaultConfig
        {
            FormatDateTime = "yyyy-MM-dd"
        };
    }

    [FixedWidthFileField(Line = 1)]
    public string CompanyName { get; set; }

    // Format set on class with FormatDateTime - not required on each Attribute of DateTime Property
    [FixedWidthFileField(Line = 4, Start = 15, Length = 19/*, Format = "yyyy-MM-dd"*/)]
    public DateTime Date { get; set; }

    /* ... Other Properties */
}

If we need to changed DefaultConfig(Format) for multiple models then we could override entire Provider to keep it DRY.

Combining both previous usages we can make complex file structures like invoiceFull.

When there is some special situation where some Fields can not be configured with existing options, then we can make additional custom parsing prior or/and after calling the method - PRE & POST Processing.

3. Data in CustomFileFields

Third use case is when one data field is relative to some text position.

SoftysTech LCC company
__________________________________________________________________

Date generated: 30.06.2020.

                       Q REPORT no. 11/20**  
1569ccACTN.162-677-169-796
...
Revenue:
1,234.55
...
xx
public class Report
{
  [CustomFileField(EndsWith = " company")]
  public string CompanyName { get; set; }

  [CustomFileField(StartsWith = "Date generated: ", Format = "d.M.yyyy.")]
  public DateTime Date { get; set; }

  [CustomFileField(Contains = "Q REPORT no. ", RemoveText = "**")]
  public string Number { get; set; }

  [CustomFileField(Contains = "ACTN.", Length = -15)]
  public string Account { get; set; }

  [CustomFileField(StartsWith = "Revenue:", Offset = 1)]
  public decimal Revenue { get; set; }
}

For parsing [CustomFileField] attributes are used, with additional params:

  • StartsWith, EndsWith, Contains - finds lines with first occurance of search criteria
  • Offset - moves found line up(is positive) or down(negative value) for defined number of rows
  • RemoveText - to clear custom substring from text value before additional casting
  • RemoveStartsWith, RemoveEndsWith, RemoveContains - defaults are 'True' meaning that search string is also cleared
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.2.0 1,715 9/13/2024
1.1.6 122 9/13/2024
1.1.5 103 9/12/2024
1.1.4 62,469 9/20/2022
1.1.3 1,094 9/20/2022
1.1.2 1,206 9/19/2022
1.1.1 23,019 4/27/2022
1.1.0 68,345 5/21/2020
1.0.9 1,233 5/21/2020
1.0.8 59,961 9/30/2019
1.0.7 12,771 2/19/2019
1.0.6 1,881 1/16/2019
1.0.5 1,537 11/22/2018
1.0.4 1,435 11/20/2018
1.0.3 1,423 11/19/2018
1.0.2 1,439 11/19/2018
1.0.1 1,474 11/18/2018
1.0.0 1,544 11/14/2018

FixedWidthConfig added