I am using the following code and getting an exception ,
Exception thrown: 'CsvHelper.WriterException' in CsvHelper.dll on this line executed:
csv.WriteRecord(item);
This is the more of the code:
using (var memoryStream = new MemoryStream())
{
using (var writer = new StreamWriter(memoryStream))
{
using (var csv = new CsvHelper.CsvWriter(writer, System.Globalization.CultureInfo.CurrentCulture))
{
foreach (var item in csvData)
{
csv.WriteRecord(item); // Exception thrown here
}
}
}
var arr = memoryStream.ToArray();
//js.SaveAs("people.csv", arr); what type object is js? copied from the stackoverflow answer linked here
}
This is the csvData code:
IEnumerable<DateLowHigh> csvData = stocks.candles
.Select(c => new DateLowHigh
{
Time = c.datetime,
Close = c.close,
Low = c.low,
High = c.high
})
.ToList();
I do get the csvData.
This stackoverflow answer helped me get started.
I don't know why you need to use CsvHelper when you can easily do the same with the IO library.
sing System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.csv";
static void Main(string[] args)
{
Stocks stocks = new Stocks();
IEnumerable<DateLowHigh> csvData = stocks.candles.Select(c => new DateLowHigh
{
Time = c.datetime,
Close = c.close,
Low = c.low,
High = c.high
}).ToList();
MemoryStream memoryStream = new MemoryStream();
using (var writer = new StreamWriter(memoryStream))
{
string[] headers = {"Time", "Close", "Low", "High"};
writer.WriteLine(string.Join(",", headers));
foreach (var item in csvData)
{
writer.WriteLine(string.Join(",", new string[] { item.Time.ToString(), item.Close, item.Low.ToString(), item.High.ToString() }));
}
}
memoryStream.Position = 0;
}
}
public class Stocks
{
public List<Candle> candles { get; set; }
}
public class Candle
{
public DateTime datetime { get; set; }
public string close { get; set; }
public int low { get; set; }
public int high { get; set; }
}
public class DateLowHigh
{
public DateTime Time { get; set; }
public string Close { get; set; }
public int Low { get; set; }
public int High { get; set; }
}
}
To answer your question as to what type of object js is in js.SaveAs("people.csv", arr); it is likely Microsoft.JSInterop.IJSRuntime as seen in this Blazer HowTo. The StackOverflow question you referenced was likely using it to get access to JavaScript they could use in C# code. If you just want to save a CSV file using CsvHelper, it could be as simple as:
using (var writer = new StreamWriter("path\\to\\file.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(csvData);
}
If you are still getting errors, you would need to post the full exception with StackTrace in order to help further.
Related
Essentially I have to read and update the CSVfile (only one column) with the current date after the test finishes executing(ie,there are some values written in at the start of the test execution and then I need to update the same file to input another value). I also have a DateTime error which isn't getting resolved no matter what I try.
Sample of CSV start of test
RunId ProductArea Product Component PageObject Control TimeTakenByLocatorJson
Run_645987 R201 BN2018.5 N778 BC1 C143
CSV one column Needs to get updated after test
( TimeTakenByLocatorJson)
RunId ProductArea Product Component PageObject Control TimeTakenByLocatorJson
Run_645987 R201 BN2018.5 N778 BC1 C143 2021-07-19
I've been trying to update a CSV file using CSVhelper. The code I have is in Java and when I tried translating the same code in C# it doesn't work.
This is the code in Java
public synchronized void writeEndCSV(String runId)
{
CSVWriter csvWriter = null;
try
{
String setupCSVLocation = Reporting.getSetupCSVLocation();
CSVReader csvReader = new CSVReader(new FileReader(setupCSVLocation));
List<String[]> records = csvReader.readAll();
for(int i=0;i<records.size();i++)
{
if(records.get(i)[SETUP_RUNID].equalsIgnoreCase(runId));
{
records.get(i)[SETUP_TimeTakenByLocatorJSON] = Reporting.getExecutionEndDate();
}
}
csvReader.close();
csvWriter = new CSVWriter(new FileWriter(setupCSVLocation));
csvWriter.writeAll(records);
csvWriter.flush();
csvWriter.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
This is my code in C# (I'm new to .Net so I'm not sure about many parts)
public void writeEnd(string runId)
{
var records = Enumerable.Empty<LocatorTime>();
try
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
// Don't write the header again.
HasHeaderRecord = false,
};
using (var reader = new StreamReader(#"D:\Reports\" + runId + #"\LocatorTime.csv"))
using (var csv = new CsvReader(reader, config))
{
//csv.Context.RegisterClassMap<LocatorTime>();
records = csv.GetRecords<LocatorTime>().ToList();
foreach (var record in records)
{
if (record.RunID == runId)
{
record.TimeTakenByLocatorJSON = DateTime.Now;
}
// Console.WriteLine("inside loop");
}
}//Endof Stream Reader
using (var stream = File.Open(#"D:\Reports\" + runId + #"\LocatorTime.csv", FileMode.Append)) //not sure what the file mode should be
using (var writer = new StreamWriter(stream))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(records);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}//end func writeEnd
This is the class used for the csv file & are also the column names in the csvfile
public class LocatorTime
{
public string RunID { get; set; }
public string ProductArea { get; set; }
public string Product { get; set; }
public string Component { get; set; }
public string PageObject { get; set; }
public string Control { get; set; }
public DateTime TimeTakenByLocatorJSON //only this value needs to be written for update at end of exec
{
get;
set;
}/*error because of DateTime datatype how to resolve?*/ }//LocatorTimeClass
/*public void SetExeDate() //tried this for removing DateTime error, didn't work
{
DateTime today = DateTime.Today; // As DateTime
string s_today = today.ToString("yyyy-MM-dd"); // As String
//TimeTakenByLocatorJSON = s_today.Trim();
TimeTakenByLocatorJSON = Convert.ToDateTime(s_today);}
*/
public sealed class LocatorTimeMap : ClassMap<LocatorTime> //is mapping helpful for updating? currently commented out
{
public LocatorTimeMap()
{
Map(m => m.RunID).Index(0);
Map(m => m.ProductArea).Index(1);
Map(m => m.Product).Index(2);
Map(m => m.Component).Index(3);
Map(m => m.PageObject).Index(4);
Map(m => m.Control).Index(5);
Map(m => m.TimeTakenByLocatorJSON).Index(6); //error
}
}
I had used the below link as reference for trying to update the CSV file hence the use of "HasHeaderRecord = false"
https://joshclose.github.io/CsvHelper/examples/writing/appending-to-an-existing-file/
I noticed 3 things. 1st - In your sample data, you have 7 column headers, but only 6 columns of data. 2nd - Your class names for "RunId" and "TimeTakenByLocatorJson" don't exactly match the columns in your sample data. 3rd - Your config says "Don't write the header again.", but you are using it for reading.
For the 1st issue, I'm going to assume this was a misprint and I'll add another column of data.
For the 2nd issue, there are at least 3 ways to handle it. You already handled it one way by mapping to indexes in LocatorTimeMap. I'll give you a 2nd way by casting the header to lower case. A 3rd way is to use the name attribute
For the 3rd issue, the header is there for reading and I assume you want the header when you write it, so you can leave HasHeaderRecord = false out.
void Main()
{
writeEnd("Run_645987");
}
// You can define other methods, fields, classes and namespaces here
public void writeEnd(string runId)
{
var records = Enumerable.Empty<LocatorTime>();
try
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
// This will convert both the header coming in and your class property to lower case.
PrepareHeaderForMatch = args => args.Header.ToLower()
};
var input = new StringBuilder();
input.AppendLine("RunId,ProductArea,Product,Component,PageObject,Control,TimeTakenByLocatorJson");
input.AppendLine("Run_645987,R201,BN2018.5,N778,BC1,control1,2021-07-19");
using (var reader = new StringReader(input.ToString()))
//using (var reader = new StreamReader(#"D:\Reports\" + runId + #"\LocatorTime.csv"))
using (var csv = new CsvReader(reader, config))
{
records = csv.GetRecords<LocatorTime>().ToList();
foreach (var record in records)
{
if (record.RunID == runId)
{
record.TimeTakenByLocatorJSON = DateTime.Now;
}
}
}//Endof Stream Reader
//using (var stream = File.Open(#"D:\Reports\" + runId + #"\LocatorTime.csv", FileMode.Append)) //not sure what the file mode should be
//using (var writer = new StreamWriter(stream))
using (var csv = new CsvWriter(Console.Out, CultureInfo.InvariantCulture))
{
csv.WriteRecords(records);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}//end func writeEnd
public class LocatorTime
{
public string RunID { get; set; }
public string ProductArea { get; set; }
public string Product { get; set; }
public string Component { get; set; }
public string PageObject { get; set; }
public string Control { get; set; }
public DateTime TimeTakenByLocatorJSON //only this value needs to be written for update at end of exec
{
get;
set;
}
}//LocatorTimeClass
Hi I have the following Code whereby in my class I have defined all the classes that I will need to build up my JSON string for an eventual put request. I am trying to put my AttributeColor and AttributeSize Fields into that attributes string[] so that the JSON returns the following :
{"group":null,"productid":"42","sku":"211","money":"20.00","categoryid":"42","attributes":["42","green"]}
myObject.attributes = reader["Sizeattribute"].ToString() + reader["ColorAttribute"].ToString();
where am I going wrong? how can I add two fields to this one array for perfect JSON? Right now I am getting the error Cannot implicitly convert type 'string' to 'string[]'
Code Snippet:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
namespace JSON_Example
{
class Program
{
static void Main(string[] args)
{
// Read From SQL
using (SqlConnection connection = new SqlConnection("Server = localhost;Database=xxxx;UID=admin;PASSWORD=xxxx"))
{
String query = "SELECT * FROM [test].[dbo].[DimProductVariantXXX] "; // Please Change the View that you are pointing towards
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
List<Product> myObjectList = new List<Product>();
var reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
Product myObject = new Product();
myObject.productid = reader["productid"].ToString();
myObject.sku = reader["sku"].ToString();
myObject.money = reader["money"].ToString();
// myObject.categoryid = Convert.ToString(reader["categoryid"]);
myObject.attributes = reader["Sizeattribute"].ToString() + reader["ColorAttribute"].ToString();
// myObject.variantparentid = reader["variantparentid"].ToString();
myObjectList.Add(myObject);
}
}
Console.WriteLine(myObjectList);
Product product = new Product();
String JSONresult = JsonConvert.SerializeObject(product);
string path = #"D:\json\product.json";
if (File.Exists(path))
{
File.Delete(path);
using (var tw = new StreamWriter(path, true))
{
tw.WriteLine(JSONresult.ToString());
tw.Close();
}
}
else if (!File.Exists(path))
{
using (var tw = new StreamWriter(path, true))
{
tw.WriteLine(JSONresult.ToString());
tw.Close();
}
}
}
}
}
}
class Product
{
public Group group;
public string productid { get; set; }
public string sku { get; set; }
public string money { get; set; }
public string categoryid { get; set; }
public string sizeattribute { get; set; }
public string colorattribute { get; set; }
public List<string>[] attributes { get; set; }
}
}
In the Product class, the attributes property should be defined as a single list or array. It's currently defined as an array of List<string> (not sure if this is a typo).
Update the definition:
public List<string> attributes { get; set; }
Now, myObject.attributes is a List<string> so you need to initialise the list and add the values to it.
You could do this using the Collection Initializer:
myObject.attributes = new List<string>
{
reader["Sizeattribute"].ToString(),
reader["ColorAttribute"].ToString()
};
This should produce the expected JSON.
i am parsing class with empty string in some element in XML file like below.
objectCxml.Request.InvoiceDetailRequest.InvoiceDetailRequestHeader.InvoiceDetailHeaderIndicator = "";
XmlSerializer s = new XmlSerializer(typeof(cXML));
XmlTextWriter tw = new XmlTextWriter(path, Encoding.UTF8);
s.Serialize(tw, objectCxml);
It generate xml like below
<InvoiceDetailHeaderIndicator xsi:type="xsd:string"/>
But i want it as below
<InvoiceDetailHeaderIndicator/>
Any Suggestion?
InvoiceDetailHeaderIndicator property is object
So... don't do that? Make it string and you should be set.
Ultimately, the point here is that XmlSerializer wants to be able to round-trip data reliably; that is its job. There are two ways of doing that:
know the type statically (i.e. string instead of object in the type model)
embed additional metadata in the payload (xsi:type="xsd:string")
If you don't want 2, you'll need 1, otherwise it can't work. Frankly 1 is a much better idea anyway.
I tested with a local minimal setup, and this worked fine:
public class InvoiceHeaderThing
{
public string InvoiceDetailHeaderIndicator { get; set; }
}
Full code below:
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
static class P
{
static void Main()
{
const string path = "my.xml";
var objectCxml = new cXML();
objectCxml.Request.InvoiceDetailRequest.InvoiceDetailRequestHeader.InvoiceDetailHeaderIndicator = "";
XmlSerializer s = new XmlSerializer(typeof(cXML));
using (XmlTextWriter tw = new XmlTextWriter(path, Encoding.UTF8))
{
s.Serialize(tw, objectCxml);
}
Console.WriteLine(File.ReadAllText(path));
}
}
public class cXML
{
public RequestThing Request { get; set; } = new RequestThing();
}
public class RequestThing
{
public InvoiceDetailThing InvoiceDetailRequest { get; set; } = new InvoiceDetailThing();
}
public class InvoiceDetailThing
{
public InvoiceHeaderThing InvoiceDetailRequestHeader { get; set; } = new InvoiceHeaderThing();
}
public class InvoiceHeaderThing
{
public string InvoiceDetailHeaderIndicator { get; set; }
}
I have been trying to create a simple csv file using the csv helper. However, the result, I am getting is not what I expected.
For some reason which I cannot find it, the first value is shifting towards the right and appear as the header.
Could someone point me what I am doing wrong here?
public class Record
{
public string Vrm { get; set; }
public string Version { get; set; }
public DateTime Started { get; set; }
public DateTime? Completed { get; set; }
public string Status { get; set; }
public string Comments { get; set; }
}
static void Main(string[] args)
{
var source = new List<Record> {
new Record {
Status = "Success",
Version = "enhance",
Started = DateTime.Parse("2017-11-15 13:27:56.9933333"),
Completed = DateTime.Parse("2017-11-15 13:27:57.7300000"),
Vrm = "16aux",
Comments = "Completed Successfully"
}
};
var month = DateTime.UtcNow.Month;
var year = DateTime.UtcNow.Year;
var fileName = $"TestFile_{month}{year}.csv";
using (var sw = new StreamWriter(fileName))
{
var writer = new CsvWriter(sw);
try
{
writer.WriteHeader<Record>();
foreach (var record in source)
{
writer.WriteField(record.Vrm);
writer.WriteField(record.Version);
writer.WriteField(record.Started);
writer.WriteField(record.Completed);
writer.WriteField(record.Status);
writer.WriteField(record.Comments);
writer.NextRecord();
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
var i = sw;
}
}
The result is something like this:
Read up on http://joshclose.github.io/CsvHelper/writing#writing-all-records
You need to advance the writer one line by calling writer.NextRecord(); after writer.WriteHeader<Record>();.
You could also simply write all data at once, using csv.WriteRecords( records ); instead of foreaching over them
I have a ArrayList with some user objects in. I am trying to serialize them into an XML format that I can send to a webserver. The format needs to be UTF 8.
I keep running into this error:
The type of the argument object 'User' is not primitive.
This is effectively two issues, however the main one being that this primitive error will not let me try and other web examples for utf8. I simply to not understand why it does this. I have tried using:
[Serializable]
Currently I have a function which will work but it will not do the xml to a utf8 format. And when I try any other examples on the web I then get this primitive error. Below is my current code:
My User Class:
using System;
using System.Xml;
using System.Xml.Serialization;
[Serializable]
public class User
{
public int UserID { get; set; }
public string Name { get; set; }
public string Password { get; set; }
public DateTime DateCreated { get; set; }
public string DeviceMacAddr { get; set; }
public DateTime LastLoggedIn { get; set; }
public float LastLoggedLat { get; set; }
public float LastLoggedLong { get; set; }
public bool Active { get; set; }
public string SyncStatus { get; set; }
public DateTime LastSyncDate { get; set; }
}
My XML writing Script:
using UnityEngine;
using System;
using System.Collections;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Collections.Generic;
public class XmlWriting : MonoBehaviour {
private string formatString = "'yyyy'##'MM'##'dd' 'HH'*'mm'*'ss'";
[XmlAttribute("Users")]
ArrayList Users = new ArrayList();
//List<User> Users = new List<User>();
// Use this for initialization
void Start () {
Users.Add(new User { UserID = 1,
Name = "Test Woman",
Password = "aa",
DateCreated = DateTime.Now,
DeviceMacAddr = "24:70:8c:83:86:BD",
LastLoggedIn = DateTime.Now,
LastLoggedLat = 36.083101f,
LastLoggedLong = -11.263433f,
Active = true,
SyncStatus = "Awaiting Response",
LastSyncDate = DateTime.Now,
}
);
Users.Add(new User { UserID = 2,
Name = "Test Man",
Password = "aa",
DateCreated = DateTime.Now,
DeviceMacAddr = "74:21:0c:93:46:XD",
LastLoggedIn = DateTime.Now,
LastLoggedLat = 83.083101f,
LastLoggedLong = -3.261823f,
Active = true,
SyncStatus = "Complete",
LastSyncDate = DateTime.Now,
}
);
var serializer = new XmlSerializer(typeof(ArrayList));
var memoryStream = new MemoryStream();
var streamWriter = new StreamWriter(memoryStream, System.Text.Encoding.UTF8);
serializer.Serialize(streamWriter, Users);
byte[] utf8EncodedXml = memoryStream.ToArray();
Debug.Log ("SerializeArrayList: " );//+ utf8EncodedXml);
}
// Update is called once per frame
void Update () {
}
private string SerializeArrayList(ArrayList obj)
{
XmlDocument doc = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(typeof(ArrayList), new Type[]{typeof(User)});
using (MemoryStream stream = new System.IO.MemoryStream())
{
try
{
serializer.Serialize(stream, obj);
stream.Position = 0;
doc.Load(stream);
Debug.Log ("stream: " + doc.InnerXml);
return doc.InnerXml;
}
catch (Exception ex)
{
}
}
return string.Empty;
}
public class Utf8StringWriter : StringWriter
{
public override Encoding Encoding
{
get { return Encoding.UTF8; }
}
}
}
Any help is much appreciated.
Thanks
You need to put the [Serializable] attribute above the class definitions for User and every other class you intend to serialize. As for UTF-8, it shouldn't matter, at least not logically - C#'s deserializer on the other side should handle any encoding transparently for you.