Basic Gist: User browses to CSV, app reads CSV, parses does some calcs and then spits out a new csv. This is a windows Form app using c# flavoured .net.
CSV File being Tested:
Position,Title3,Title1,Title2,Title4,Title5,,
1,P-D3101A,NAME 1,175,282.9280381,1 x 30 x 120
2,P-D3103A,NAME 2,37.2,60.14241724,30 x 16
3,P-D3102A,NAME 3,29.8,48.17860306,30 x 10
4,P-D2301A,NAME 4,35,56.58560762,30 x 16
5,P-D1201A,NAME 5,38,61.43580256,30 x 16
6,P-D2301D,NAME 6,32,51.73541268,30 x 16
7,A-D0636,NAME 7,8.5,13.74221899,30 x 1.5
Have a class:
class Equipment
{
public string Title1 { get; set; }
public double Title2 { get; set; }
public string Title3 { get; set; }
}
and a parser (thanks very much to SO community) :
static IList<Equipment> Parse(string[] input)
{
var result = new List<Equipment>();
var header = input[0].Split(',').Select(t => t.Trim().ToLower()).ToList();
var title1Loc = GetIndexOf(header, "title1");
var title2Loc = GetIndexOf(header, "title2");
var title3Loc = GetIndexOf(header, "title3");
foreach (var s in input.Skip(1))
{
var line = s.Split(',');
result.Add(new Equipment
{
Title1 = line[title1Loc].Trim(),
Title2 = double.Parse(line[title2Loc]),
Title3 = line[title3Loc].Trim(),
});
}
return result;
}
static int GetIndexOf(IList<string> input, params string[] needles)
{
return Array.FindIndex(input.ToArray(), needles.Contains);
}
and a couple of buttons, one is to get the file and save its file path in the string referenceFile , the other is to parse the data:
private void button2_Click(object sender, EventArgs e)
{
string[] data = File.ReadAllLines(referenceFile);
MessageBox.Show(string.Join(Environment.NewLine, data), "Your CSV");
}
Ultimately I want to produce a new CSV File, which I can although its blank, so to check what its getting from the parsed data I used a message box which displays:
MyProject.Form1+Equipment
repeated 7 times on new lines. So it is correctly going over my 7 line file, however not actually generating what I want. Any glaring errors I cant see?
You will need to override the ToString method of to Equipment class to get the desired output which dictates that how the string representation of your type should look like.
Here is how it can look like as an example:
class Equipment
{
public string Title1 { get; set; }
public double Title2 { get; set; }
public string Title3 { get; set; }
public override string ToString()
{
return $"{Title1},{Title2},{Title3}";
}
}
Related
my situation is that I need to add up all prices of items in a Listbox and have the total price in the textbox. I am using c# WinForms. This what I have so far but it is not working correctly by showing total, it is showing the price of the last item I press and not the total of all items
public class SelectedPizza
{
public string Size { get; set; }
public string Name { get; set; }
public string Price { get; set; }
public string Format() => $"{Size} {Name} {Price}"; // Format inside Listbox
}
private void ButtonSizeClick(object sender, EventArgs e)
{
var button = (Button)sender;
_selectedPizza.Size = button.Tag.ToString(); // Adds pizza size to order listbox
if (_selectedPizza.Name != null)
_selectedPizza.Price = GetPrice(_selectedPizza.Size);
}
private void ButtonNameClick(object sender, EventArgs e)
{
var button = (Button)sender;
_selectedPizza.Name = button.Tag.ToString();
if (_selectedPizza.Size != null)
_selectedPizza.Price = GetPrice(_selectedPizza.Size);
listBox1.Items.Add(_selectedPizza.Format()); // Adds pizza name to order listbox
}
private string GetPrice(string sSize)
{
string sPrice = "0.00";
if (sSize == "Large")
sPrice = "11.90";
TxtbxTotal.Text = sPrice; // Displays price in order total textbox
return sPrice;
}
I won't go deep into your problem but I will address the main question.
Finally, you want to sum up the Listbox items.
So to do that, add the below piece to your code:
for (int i = 0; i < listboxname.Items.Count; i++)
{
sum += Convert.ToDecimal(listboxname.Items[i].Text);
}
textBoxtotal.Text = Convert.ToString(sum);
I roughly read the entire code and couldn't find any event relating to total of the listbox. Maybe you missed to write the click event of the total button.
you seem writing a decent program but you are killing it doing this listBox1.Items.Add(_selectedPizza.Format());
you need to use this technology
public interface IFoodItem // all list box items must have certain properties
{
public int Id { get; set; }
string Name { get; set; }
string Price { get; set; }
public string Display { get; }
}
public class Pizza : FoodItem
{
public int Id { get; set; }
public string Size { get; set; }
public string Name { get; set; }
public string Price { get; set; }
public string Display
{
get { return $"{Size} {Name} {Price}"; }
}
}
// another example
public class Sandwhich : FoodItem
{
// . . . . .
}
//. . . . . .
// Get the list
var listData = new List<IFoodItem>();
listData.Add( . . . );
// . . . . .
// this is how you should set the list - DataSource always last
lstList.ValueMember = "Id";
lstList.DisplayMember = "Display";
lstList.DataSource = listData;
// Get selected item info
IFoodItem item = (IFoodItem)lstList.SelectedItem;
txtItemPrice.Text = item.Price;
// Get Total using LINQ
txtTOTALPrice.Text = ((List<IFoodItem>)lstList.DataSource).Sum(item => item.Price);
UPDATE
From OP: "i have to stick to a specification for school, so all I need to know is how to do it with the code that I have got already"
your value is public string Format() => $"{Size} {Name} {Price}"; added here listBox1.Items.Add(_selectedPizza.Format());
In this case you do this (your sum is third part of the string item in the list)
var sum = 0m;
foreach (object item in listBox1.Items)
{
string[] itemParts =
((string)item).Split(default(char[]), StringSplitOptions.RemoveEmptyEntries);
// price is last index
sum += decimal.Parse(itemParts[itemParts.Length - 1]); // get last item
}
txtTotal.Text = sum;
Here is the working unit test
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var list = new List<object>();
list.Add("aaa xxx 5 10.99");
list.Add("bbb yyy 7 11.99");
list.Add("ccc zzz 8 12.99");
var sum = 0m;
foreach (object item in list)
{
string[] itemParts =
((string)item).Split(default(char[]), StringSplitOptions.RemoveEmptyEntries);
// price's index is 2
sum += decimal.Parse(itemParts[itemParts.Length - 1]);
}
Console.WriteLine(sum);
}
}
Is there any library out there that can serialize objects with array properties to .csv?
Let's say I have this model:
public class Product
{
public string ProductName { get; set; }
public int InStock { get; set; }
public double Price { get; set; }
...
public string[] AvailableVariants { get; set; }
}
Would something like that be possible to do?
Edit: I need to present some data in a csv/excel format. The thing is, I'm not sure if there is a simple way of achieving what I want with CSV serialization libraries or if I should rather focus on writing an Excel native file.
An example of result I'm looking for:
Product Name In Stock Price Variants
ABC 241 200 Normal
CAB 300 300 Normal
Red
Blue
CBA 125 100 Normal
White
Awesome
Red
ACB 606 75 Normal
Small
Large
X-Large
What would be the most efficient way to do this?
I'm not aware of any libraries that will do this, here's a console example of how I'd approach writing/reading from a CSV:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace TestingProduct
{
class TestingProduct
{
public class Product
{
public string ProductName { get; set; }
public int InStock { get; set; }
public double Price { get; set; }
public string[] AvailableVariants { get; set; }
public override string ToString() => $"{ProductName},{InStock},{Price}{(AvailableVariants?.Length > 0 ? "," + string.Join(",", AvailableVariants) : "")}";
public static Product Parse(string csvRow)
{
var fields = csvRow.Split(',');
return new Product
{
ProductName = fields[0],
InStock = Convert.ToInt32(fields[1]),
Price= Convert.ToDouble(fields[2]),
AvailableVariants = fields.Skip(3).ToArray()
};
}
}
static void Main()
{
var prod1 = new Product
{
ProductName = "test1",
InStock= 2,
Price = 3,
AvailableVariants = new string[]{ "variant1", "variant2" }
};
var filepath = #"C:\temp\test.csv";
File.WriteAllText(filepath, prod1.ToString());
var parsedRow = File.ReadAllText(filepath);
var parsedProduct = Product.Parse(parsedRow);
Console.WriteLine(parsedProduct);
var noVariants = new Product
{
ProductName = "noVariants",
InStock = 10,
Price = 10
};
var prod3 = new Product
{
ProductName = "test2",
InStock= 5,
Price = 5,
AvailableVariants = new string[] { "variant3", "variant4" }
};
var filepath2 = #"C:\temp\test2.csv";
var productList = new List<Product> { parsedProduct, prod3, noVariants };
File.WriteAllText(filepath2, string.Join("\r\n", productList.Select(x => x.ToString())));
var csvRows = File.ReadAllText(filepath2);
var newProductList = new List<Product>();
foreach (var csvRow in csvRows.Split(new string[] { "\r\n" }, StringSplitOptions.None))
{
newProductList.Add(Product.Parse(csvRow));
}
newProductList.ForEach(Console.WriteLine);
Console.ReadKey();
}
}
}
This code will work with a class that has a single object array property. Do you need something that can handle an object with multiple array properties?
I have written some kind of library to write csv files, have a look:
public static class CsvSerializer
{
public static bool Serialize<T>(string path, IList<T> data, string delimiter = ";")
{
var csvBuilder = new StringBuilder();
var dataType = typeof(T);
var properties = dataType.GetProperties()
.Where(prop => prop.GetCustomAttribute(typeof(CsvSerialize)) == null);
//write header
foreach (var property in properties)
{
csvBuilder.Append(property.Name);
if (property != properties.Last())
{
csvBuilder.Append(delimiter);
}
}
csvBuilder.Append("\n");
//data
foreach (var dataElement in data)
{
foreach (var property in properties)
{
csvBuilder.Append(property.GetValue(dataElement));
if (property != properties.Last())
{
csvBuilder.Append(delimiter);
}
}
csvBuilder.Append("\n");
}
File.WriteAllText(path, csvBuilder.ToString());
return true;
}
}
public class CsvSerialize : Attribute
{
}
Lets pretend you want to serialize following class:
public class MyDataClass
{
[CsvSerialize]
public string Item1 {get; set;}
[CsvSerialize]
public string Item2 {get; set;}
}
Then just do:
public void SerializeData(IList<MyDataClass> data)
{
CsvSerializer.Serialize("C:\\test.csv", data);
}
It takes a IList of your class and writes a csv.
It cant serialize arrays but that would be easy to implement.
How can I code in wpf -xaml (C#) to read a specific line in a .csv File.
Every button on the Periodic table I made goes to a new window which shows in a listview the specific things about it. But then my question how to do that?
public class Atomic
{
public string Group { get; set; }
public string Period { get; set; }
public string Block { get; set; }
public string Atomicnumber { get; set; }
public string Stateat { get; set; }
public string Electronconfiguration { get; set; }
public string ChemspiderID { get; set; }
public Atomic(string group, string period, string block, string atomicnumber, string stateat, string electronconfiguration, string chemspiderID)
{
Group = group;
Period = period;
Block= block;
Atomicnumber = atomicnumber;
Stateat = stateat;
Electronconfiguration = electronconfiguration;
ChemspiderID = chemspiderID;
}
}
public IEnumerable<Atomic> ReadCSV(string fileName)
{
// We change file extension here to make sure it's a .csv file.
// TODO: Error checking.
string[] lines = File.ReadAllLines(System.IO.Path.ChangeExtension(fileName, ".csv"));
// lines.Select allows me to project each line as a Person.
// This will give me an IEnumerable<Person> back.
return lines.Select(line =>
{
string[] data = line.Split(';');
// We return a person with the data in order.
return new Atomic(data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
});
}
If you want to read a specific line you could do the following.
public Atomic ReadCSV(string fileName, int lineIndex)
{
return File.ReadLines(System.IO.Path.ChangeExtension(fileName, ".csv"))
.Skip(lineIndex)
.Select(line => line.Split(';'))
.Select(data => new Atomic(data[0], data[1], data[2], data[3], data[4], data[5], data[6]))
.FirstOrDefault();
}
This will read the first lineNumber + 1 lines of the file take the last line read and create your Atomic object from that line. If there are not enough lines it will return a null value. If you prefer a one based index just change .Skip(lineIndex) to .Skip(lineIndex - 1).
While converting json string datatable facing an issue with , (comma) value in value field.
actualy my json string is [{"BNo":"345","GNo":"3453","FirstName":"fjai","LastName":"ljai","Address":"BARETI,CEVO, 13/2","Telephone":"051682247","BirthDate":"23-Jan-1981","Email":""}]
In that please look at the address scenario "Address":"BARETI,CEVO, 13/2"
It has the , in the values field. While converting the string to data base i got error. Here the code which i used convert json string to datatable
public DataTable JsonStringToDataTbl(string jsonString)
{
DataTable dt = new DataTable();
string[] jsonStringArray = Regex.Split(jsonString.Replace("[", "").Replace("]", ""), "},{");
List<string> ColumnsName = new List<string>();
foreach (string jSA in jsonStringArray)
{
string[] jsonStringData = Regex.Split(jSA.Replace("{", "").Replace("}", ""), ",");
foreach (string ColumnsNameData in jsonStringData)
{
try
{
int idx = ColumnsNameData.IndexOf(":");
string ColumnsNameString = ColumnsNameData.Substring(0, idx - 1).Replace("\"", "");
if (!ColumnsName.Contains(ColumnsNameString))
{
ColumnsName.Add(ColumnsNameString);
}
}
catch (Exception ex)
{
throw new Exception(string.Format("Error Parsing Column Name : {0}", ColumnsNameData));
}
}
break;
}
foreach (string AddColumnName in ColumnsName)
{
dt.Columns.Add(AddColumnName);
}
foreach (string jSA in jsonStringArray)
{
string[] RowData = Regex.Split(jSA.Replace("{", "").Replace("}", ""), ",");
DataRow nr = dt.NewRow();
foreach (string rowData in RowData)
{
try
{
int idx = rowData.IndexOf(":");
string RowColumns = rowData.Substring(0, idx - 1).Replace("\"", "");
string RowDataString = rowData.Substring(idx + 1).Replace("\"", "");
nr[RowColumns] = RowDataString;
}
catch (Exception ex)
{
continue;
}
}
dt.Rows.Add(nr);
}
return dt;
}
The code must omit the , in the value field.. what can i do
If your keys are unknown at the time of being read, then you can use the JObject and the JProperty classes from JSON.Net to retrieve the keys and their values like this:
private void printKeysAndValues(string json)
{
var jobject = (JObject)((JArray)JsonConvert.DeserializeObject(json))[0];
foreach (var jproperty in jobject.Properties())
{
Console.WriteLine("{0} - {1}", jproperty.Name, jproperty.Value);
}
}
Applied to two different JSON input string, retrieves the key/value pair:
var json1 = #"[{""BNo"":""345"",""GNo"":""3453"",""FirstName"":""fjai"",""LastName"":""ljai"",""Address"":""BARETI,CEVO, 13/2"",""Telephone"":""051682247"",""BirthDate"":""23-Jan-1981"",""Email"":""""}]";
var json2 = #"[{""Test"": ""A"", ""Text"":""some text"", ""Numbers"":""123""}]";
printKeysAndValues(json1);
Console.WriteLine("-------------------");
printKeysAndValues(json2);
And the output is:
BNo - 345
GNo - 3453
FirstName - fjai
LastName - ljai
Address - BARETI,CEVO, 13/2
Telephone - 051682247
BirthDate - 23-Jan-1981
Email -
-------------------
Test - A
Text - some text
Numbers - 123
One possibility would be to use the dynamic keyword. You can directly access the field like this:
var json = #"[{""BNo"":""345"",""GNo"":""3453"",""FirstName"":""fjai"",""LastName"":""ljai"",""Address"":""BARETI,CEVO, 13/2"",""Telephone"":""051682247"",""BirthDate"":""23-Jan-1981"",""Email"":""""}]";
dynamic data = JsonConvert.DeserializeObject(json);
// take the first element of the array
string address = data[0].Address;
Console.WriteLine(address.Replace(",", " "));
The output is:
BARETI CEVO 13/2
Note that String.Replace does not fail, if the symbol that should be replaced is not currently present, so "test".Replace(",", " "); will return test.
Another possibility is to use the in ASP.NET build in JSON converter (serializer/deserializer) - NewtonSoft JSON.Net. You can use it in order to regain the structured data. You need to create a class that represents the JSON structure:
public class Data
{
public string BNo { get; set; }
public string GNo { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string Telephones { get; set; }
public string BirthDates { get; set; }
public string Emails { get; set; }
}
Then the current JSON can be converted to an object of type Data using the JsonConvert.DeserializeObject method:
var json = #"[{""BNo"":""345"",""GNo"":""3453"",""FirstName"":""fjai"",""LastName"":""ljai"",""Address"":""BARETI,CEVO, 13/2"",""Telephone"":""051682247"",""BirthDate"":""23-Jan-1981"",""Email"":""""}]";
// remove square braces [ and ] at the start resp. end
var data = JsonConvert.DeserializeObject<Data>(json.Substring(1).Substring(0, json.Length - 2));
Now you can access the Address field and for example replace the , symbol:
Console.WriteLine(data.Address.Replace(",", " "));
The output is:
BARETI CEVO 13/2
I think your service returns also the wrong JSON format. JSON always starts with an object (when not in JavaScript), meaning that everything at the top level must be enclosed within curly braces { and }. If the service should return an array, then it should look like this {"results": [{"BNo":"...},{...}]}. If you can't change the service, then you can adapt / correct the returned string. Add a typed model for the array:
public class DataHolder
{
public Data[] data { get; set; }
}
and then create a correct JSON object holding an array:
var data = JsonConvert.DeserializeObject<DataHolder>("{\"data\":" + json + "}");
Console.WriteLine(data.data[0].Address.Replace(",", " "));
The output is again the same.
You can convert the JSON value to C# objects using Newtonsoft. This would be easy for you. Once you have converted to the below object, you can easily modify the Address property to remove the ',' value.
public class RootObject
{
public string BNo { get; set; }
public string GNo { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string Telephone { get; set; }
public string BirthDate { get; set; }
public string Email { get; set; }
}
Use the below line to convert to C# object
var jsonString = "The output of your webservice";
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(jsonString);
Now obj instance holds the C# object which is very easy to manipulate.
public class Earthquake
{
public double Magnitude { get; set; }
public string Location { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public double depth { get; set; }
public DateTime date { get; set; }
public string EventID { get; set; }
public string URL { get; set; }
public Earthquake()
: this(string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty)
{ }
public Earthquake(string magna, string locate, string lat, string longi, string dept, string dat, string Event, string website)
{
Magnitude = Convert.ToDouble(magna);
Location = locate;
Latitude = Convert.ToDouble(lat);
Longitude = Convert.ToDouble(longi);
depth = Convert.ToDouble(dept);
date = Convert.ToDateTime(dat);
EventID = Event;
URL = website;
}
}
public void GetData()
{
string[] text = File.ReadAllLines(#"Earthquakes.csv");
Earthquake[] data = new Earthquake[1];
foreach (string line in text)
{
string[] myColumns = line.Split(',');
Earthquake[] earth = new Earthquake[myColumns[0], myColumns[1], myColumns[2], myColumns[3], myColumns[4], myColumns[5], myColumns[6], myColumns[7]];
data[i] = earth[i];
i++;
}
}
Ignore commented parts I have those under control. The problem I am having is getting the data from the csv file into the Earthquake Array. I am getting syntax errors, and I know why, it's because the data type isn't correct, but I honestly cannot figure out how to fix it.
Also if you notice I am trying to use bubble sort and since there is no definition for "compare" for double, what do I use instead?
If your reading from CSV file you probably have to remove white space from the split values.
Try adding .Trim() to your column variables
myColumns[0].Trim()
if your looking to sort yor array consider using System.Linq
eg:
var byMag = earthQuakes.OrderBy(e => e.Magnitude);
Looking at your code you posted, GetData() will not work.
Try returning a list or Enumerable
public IEnumerable<Earthquake> GetData(string filename)
{
string[] text = File.ReadAllLines(filename);
foreach (string line in text)
{
string[] myColumns = line.Split(',');
yield return new Earthquake(myColumns[0].Trim(), myColumns[1].Trim(), myColumns[2].Trim(), myColumns[3].Trim(), myColumns[4].Trim(), myColumns[5].Trim(), myColumns[6].Trim(), myColumns[7].Trim());
}
}
Usage:
var earthquakes = GetData(#"Earthquakes.csv");