FileHelpers - Column mapping - c#

Quick question regarding filehelper library:
I have used file helper engine to read stream, do my validation and if the CSV file has not got a header we need to match/map it to my model: i.e
id, name, age, phone, sex,
but the CSV might not come in this format/order all the time and we need to match them using a drop down list for each column.
Is there any way I can do this?
Thannks,

The short answer, no. BUT you can create a dependent class dynamically:
Since you have the list of possible fields in your JSON file, I would recommend doing a basic System.IO ReadLine for the first data row, and then parse by your delimiter for the individual headers. i.e.:
string headerString;
var headers = new List<String>();
var file = new System.IO.StreamReader("C:\\myFile.txt");
headerString = file.ReadLine();
file.Close();
headers = headerString.Split(',').ToList();
now you have the list of strings for the first row to match against your JSON file. Then you can create your dependent class using System.Reflection.Emit (referenced link below)
typeBuilder.SetParent(typeof(MyFileHelperBaseClass));
// can place the property definitions in a for loop against your headers
foreach(string h in headers){
typeBuilder.DefineProperty("<header/col#>", ..., typeof(System.Int32), null);
}
stackoverflow article 14724822: How Can I add properties to a class on runtime in C#?
File Helpers gets a little finicky at times, so it will take some tweaking.
Hope this helps

You can use File.ReadLines(#"C:\myfile.txt").First() to read the first line and get the headers.
Then you can just use a FileHelpers CodeBuilder to build your runtime class. From the example for a delimited csv file:
DelimitedClassBuilder cb = new DelimitedClassBuilder("Customers", ",");
cb.IgnoreFirstLines = 1;
cb.IgnoreEmptyLines = true;
cb.AddField("BirthDate", typeof(DateTime));
cb.LastField.TrimMode = TrimMode.Both;
cb.LastField.FieldNullValue = DateTime.Today;
cb.AddField("Name", typeof(string));
cb.LastField.FieldQuoted = true;
cb.LastField.QuoteChar = '"';
cb.AddField("Age", typeof(int));
engine = new FileHelperEngine(cb.CreateRecordClass());
DataTable dt = engine.ReadFileAsDT("testCustomers.txt");
Then you can traverse the resulting data table.

Related

how to resolve error while converting CSV to jason file

I have written the code with reference from one of the post on this forum but getting below error
my csv data as
how to proceed?
Before you can add data to a DataTable, you must add the columns you will need. You can also check if the column exists before adding it using dt.Columns.Contains.... If your CSV file has a header row you can use that to give your columns some header text.
Something like (this compiles but not actually run):
var dt = new DataTable();
var rows = csvfile.Split('\n');
// Add the columns
var colHeaders = rows[0].Split(',');
foreach(var header in colHeaders)
{
dt.Columns.Add(header, typeof(string));
}
// now add the data rows
foreach(var row in rows.Skip(1))
{
if(!string.IsNullOrEmpty(row))
{
var data = row.Split(',');
foreach(var d in data)
{
dt.Rows.Add(d);
}
}
}
There are other examples available on the internet specifically for reading CSV files into DataTables (here's one).

Reading multiple classes from single csv file using CsvHelper

I've been using Josh Close' CsvHelper a bit recently to parse CSV files, I quite like the fluent api for class mapping.
I'm trying to map a csv file which contains multiple record types, the file structure is
C,Comment,Timestamp
I,Class1,Header1,Header2
D,Class1,Data1,Data2
D,Class1,Data1,Data2
...
I,Class2,Header1,Header2,Header3
D,Class2,Data1,Data2,Data3
D,Class2,Data1,Data2,Data3
...
C,Checksum
Is this something which can be handled by CsvHelper? I've writen a custom parser which basically works but all it really does is filter out the Header and Data fields for a specific class - I'd really like to be able to do something like
csv.Configuration.RegisterClassMap<Class1>();
csv.Configuration.RegisterClassMap<Class2>();
var data1 = csv.GetRecords<Class1>().ToList();
var data2 = csv.GetRecords<Class2>().ToList();
And read the file in one pass? Is this possible or am I using the wrong parser?
Regards
Dave
There is a way to do this; you just have to do it manually.
You manually read the csv file row by row
Inspect the first column for the discriminator that will indicate that you need to map to a Class object.
Inspect the second column for the class to map to.
Map the entire row to that given class.
public static void ReadMultiClassCsv()
{
var class1Data = new List<Class1>();
var class2Data = new List<Class2>();
using (StreamReader reader = File.OpenText(#"C:\filename.csv"))
using (var csvReader = new CsvReader(reader))
{
//1. You manually read the csv file row by row
while (csvReader.Read())
{
var discriminator = csvReader.GetField<string>(0);
//2. Inspect the first column for the discriminator that will indicate that you need to map to a Class object.
if (discriminator == "D")
{
var classType = csvReader.GetField<string>(1);
//3. Inspect the second column for the class to map to.
switch (classType)
{
//4. Map the entire row to that given class.
case "Class1":
class1Data.Add(csvReader.GetRecord<Class1>());
break;
case "Class2":
class2Data.Add(csvReader.GetRecord<Class2>());
break;
default:
break;
}
}
}
}
}

Proof Reading .CSV per line

CSVHelper and FileHelper is not an option
I have a .csv export that I need to check for consistency structured like the below
Reference,Date,EntryID
ABC123,08/09/2015,123
ABD234,08/09/2015,124
XYZ987,07/09/2015,125
QWE456,08/09/2016,126
I can use ReadLine or RealAllLines and .Split which give me entire rows/columns BUT I have need to select each row and then go through each attribute (separated by ',') for format checking
I am running into problems here. I can not single out each value in a row for this check.
It is probably either something simple onto
class Program
{
static void Main(string[] args)
{
string csvFile = #"proof.csv";
string[] lines = File.ReadAllLines(csvFile);
var values = lines.Skip(1).Select(l => new { FirstRow = l.Split('\n').First(), Values = l.Split('\n').Select(v => int.Parse(v)) });
foreach (var value in values)
{
Console.WriteLine(string.Format("{0}", value.FirstRow));
}
}
}
Or I am going down the wrong path, my searches relate to pulling specific rows or columns (as opposed to checking the individual values associated)
The sample of the data above has a highlighted example: The date is next year and I would like to be able to proof that value (just an example as it could be in either column where errors appear)
I can not single out each value in a row
That's because you split on \n twice. The values within a row are separated by comma (,).
I'm not sure what all that LINQ is supposed to do, but it's as simple as this:
string[] lines = File.ReadAllLines(csvFile);
foreach (var line in lines.Skip(1))
{
var values = line.Split(',');
// access values[0], values[1] ...
}
Instead of reading it as text read it by OLEDB object, so data of CSV file will come in datatable and you do not need to spit it.
To Read the csv file you can use these objects of OLEDB
System.Data.OleDb.OleDbCommand
System.Data.OleDb.OleDbDataAdapter
System.Data.OleDb.OleDbConnection
and
System.Data.DataTable

Using OpenXML, how can I associate a list for data validation

I am processing an .xlsm file and need to know how to use a list on another sheet for data validation using openXML and C#.
To start, I have a .xlsm file with two empty sheets and macros in it. In my program I open the file, Create the column header on Sheet1 then create the validation list on sheet2. So, after I run my program Sheet1 "A1" contains the text "Color" and Sheet2 "A1:A4" contains "Blue","Green","Red","Yellow". I get this far just fine.
I would like to make it so there is a dropdown list in all cells of column "A" on sheet1 that contains each of the 4 colors and enforces them as the only input. In Microsoft Excel this is done by going to the "Data" tab, selecting "Data Validation" selecting "List" and highlighting the cells you want to use. I need to make this association programmatically.
The (Desired) XML that Microsoft Excel creates if I do it manually is this:
<extLst>
<ext uri="{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main">
<x14:dataValidations count="1" xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
<x14:dataValidation type="list" allowBlank="1" showInputMessage="1" showErrorMessage="1">
<x14:formula1>
<xm:f>'Validation Data'!$A$1:$A$4</xm:f>
</x14:formula1>
<xm:sqref>A1:A1048576</xm:sqref>
</x14:dataValidation>
</x14:dataValidations>
</ext>
</extLst>
The following method and results is something I tried. It may give a better Idea of what I'm trying to do.
Here, I pass in "'Sheet2'!$A$1:$A$4" as the "validationListCells" parameter. This represents the cells in "Sheet2" that, in this example, would contain the color names "Red", "Green"...etc.
I pass in "A2:A1048576" as the "cellsToValidate" parameter. This represents all cells of Sheet1 column "A", on which I want to enforce validation.
I pass "Sheet1" as the worksheetName parameter.
private void InsertValidation(String worksheetName, String validationListCells, String cellsToValidate)
{
DataValidations dataValidations1 = new DataValidations() { Count = (UInt32Value)1U };
DataValidation dataValidation1 = new DataValidation()
{
Formula1 = new Formula1(validationListCells),
Type = DataValidationValues.List,
ShowInputMessage = true,
ShowErrorMessage = true,
SequenceOfReferences = new ListValue<StringValue>() { InnerText = cellsToValidate }
};
dataValidations1.Append(dataValidation1);
using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Open(_documentPath, true))
{
WorksheetPart worksheetPart = GetWorksheetPartByName(spreadSheet, worksheetName);
worksheetPart.Worksheet.Append(dataValidations1);
worksheetPart.Worksheet.Save();
}
}
It results in this XML in Sheet1.xml. Which causes an error in Excel.
<x:dataValidations count="1">
<x:dataValidation type="list" showInputMessage="1" showErrorMessage="1" sqref="A2: A1048576">
<x:formula1>'Sheet2'!$A$1:$A$5</x:formula1>
</x:dataValidation>
</x:dataValidations>
It looks like I may be on the right track since it is beginning to resemble the xml created by Excel, but I'm completely new to openXML and I'm finding little about this topic on the net.
Thanks in advance!
For anyone else in need of this..the code below worked for me.
I put in there user3251089's variable names.
In general, when I try to programmatically create an excel "feature" I manually make a really basic excel that has in it that feature (delete extra sheets too). Then I reflect the code and try to make it prettier.
hope it serves to someone!
using Excel = DocumentFormat.OpenXml.Office.Excel;
using X14 = DocumentFormat.OpenXml.Office2010.Excel;
.....
Worksheet worksheet = worksheetPart.Worksheet;
WorksheetExtensionList worksheetExtensionList = new WorksheetExtensionList();
WorksheetExtension worksheetExtension = new WorksheetExtension() { Uri = "{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}" };
worksheetExtension.AddNamespaceDeclaration("x14", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main");
X14.DataValidations dataValidations = new X14.DataValidations() { Count = (UInt32Value)3U };
dataValidations.AddNamespaceDeclaration("xm", "http://schemas.microsoft.com/office/excel/2006/main");
//sites validation
dataValidations.Append(new X14.DataValidation()
{
Type = DataValidationValues.List,
AllowBlank = true,
ShowInputMessage = true,
ShowErrorMessage = true,
DataValidationForumla1 = new X14.DataValidationForumla1() { Formula = new Excel.Formula(validationListCells) },
ReferenceSequence = new Excel.ReferenceSequence(cellsToValidate)
});
worksheetExtension.Append(dataValidations);
worksheetExtensionList.Append(worksheetExtension);
worksheet.Append(worksheetExtensionList);
worksheet.Save();

How can I create a header in a table for each new page with OpenXml Wordprocessing

I am trying to create a table with a header. I want this header to be repeated for each new page that the table takes. How can I do this in C# and OpenXml Wordprocessing?
DocumentFormat.OpenXml.Packaging.WordprocessingDocument internalDoc =
DocumentFormat.OpenXml.Packaging.WordprocessingDocument.Open(stream, true);
var tables = wordDoc.MainDocumentPart.Document.Descendants<SdtBlock>().Where
( r => r.SdtProperties.GetFirstChild<Tag>().Val.Value.StartsWith(DATA_TABLE_TAG));
Table table = tables.Descendants<Table>().Single();
//Here can I set some property to repeat the header of the table?
As Chris said, an instance of the TableHeader class is what you need. It needs to be appended to the header row's TableRowProperties:
var row = table.GetFirstChild<TableRow>();
if (row.TableRowProperties == null)
row.TableRowProperties = new TableRowProperties();
row.TableRowProperties.AppendChild(new TableHeader());
For anyone who is looking for the same issue:
The code below must be applied to the header Row, as TablePropertiesRow
TableRowProperties tblHeaderRowProps = new TableRowProperties(
new CantSplit() { Val = OnOffOnlyValues.On },
new TableHeader() { Val = OnOffOnlyValues.On }
);
tblHeaderRow.AppendChild<TableRowProperties>(tblHeaderRowProps);
Deww!!
I think this is what you're looking for. If you apply that element to a particular row, it will behave the way you're describing.
To create header for every table in a page.
You need to create multiple body's and append to document.
If you want to create new header to every table, you need to append every table to new body then apply page break.
Finally, append all bodies to document.
Then you finally have your result in created document.
If any doubts reply to me.
Regards,
Balaji

Categories

Resources