I created a class, very simple, and I'm attempting to read from a text file into a list using the class List.
I use StreamReader inputFile to open the file, but when I try to use ReadLine I get an error that I cannot convert from string to... .Bowler (which is the designation in my list.
I created the class so that I can access the list from multiple forms.
I'm obviously new to C#, and programming in general.
//the ReadBowlers method reads the names of bowlers
//into the listBowlers.
private void ReadBowlers(List<Bowler> listBowlers)
{
try
{
//Open the Bowlers.txt file.
StreamReader inputFile = File.OpenText("Bowlers.txt");
//read the names into the list
while (!inputFile.EndOfStream)
{
listBowlers.Add(inputFile.ReadLine());
}
//close the file.
inputFile.Close();
}
catch (Exception ex)
{
//display error message
MessageBox.Show(ex.Message);
}
The line that's giving me the error is:
listBowlers.Add(inputFile.ReadLine());
You can create a method that takes a string read from your file and returns a Bowler.
For example, suppose your line of data looks like this:
Bob Smith,5,XYZ
public Bowler Parse(string inputLine)
{
// split the line of text into its individual pieces
var lineSegments = inputLine.Split(',');
// create a new Bowler using those values
var result = new Bowler
{
Name = lineSegments[0],
Id = lineSegments[1],
SomeOtherBowlerProperty = lineSegments[2]
}
return result;
}
Now you can do this:
var line = inputFile.ReadLine();
var bowler = Parse(line);
listBowlers.Add(bowler);
But it gets even better! What if Bowler has lots of properties? What if you don't want to keep track of which position each column is in?
CsvHelper is a great Nuget package, and I'm sure there are others like it. They let us use someone else's tested code instead of writing it ourself. (I didn't lead with this because writing it first is a great way to learn, but learning to use what's available is good too.)
If your data has column headers, CsvHelper will figure out which columns contain which properties for you.
So suppose you have this data in a file:
FirstName,LastName,Id,StartDate
Bob,Smith,5,1/1/2019
John,Galt,6,2/1/2019
And this class:
public class Bowler
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? StartDate { get; set; }
}
You could write this code:
public List<Bowler> GetBowlersFromFile(string filePath)
{
using(var fileReader = File.OpenText(filePath))
using (var reader = new CsvReader(fileReader))
{
return reader.GetRecords<Bowler>().ToList();
}
}
It looks at the header row and figures out which column is which.
ReadLine() gives you the string so you can't directly assign it to a custom class unless an explicit conversion is specified.
Better make a new Bowler instance, assign its values by parsing the string separately and then add that instance to the list.
//the ReadBowlers method reads the names of bowlers
//into the listBowlers.
private void ReadBowlers(List<Bowler> listBowlers)
{
try
{
//Open the Bowlers.txt file.
string path = #"Bowlers.txt";
//read the names into the list
using (FileStream fs = File.OpenRead(path))
{
byte[] b = new byte[1024];
UTF8Encoding temp = new UTF8Encoding(true);
while (fs.Read(b,0,b.Length) > 0)
{
listBowlers.Add(temp.GetString(b));
}
}
}
catch (Exception ex)
{
//display error message
MessageBox.Show(ex.Message);
}
}
Related
I have a model to which the response from the API is written.
Here is what it looks like
public class SparkPostTemplateModel
{
public List<SparkPostTemplateDetailModel> Results { get; set; }
}
And SparkPostTemplateDetailModel:
public class SparkPostTemplateDetailModel
{
public string Id { get; set; }
}
How can I save to JSON file each Id I got from an API?
This Is what I have got:
private SparkPostTemplateModel EmailTemplates { get; set; } = new();
public async Task SaveTemplate()
{
try
{
EmailTemplates = await SparkPostManager.GetTemplates();
var test = EmailTemplates.Results.ToList();
string fileName = "response.json";
using FileStream createStream = File.Create(fileName);
await JsonSerializer.SerializeAsync(createStream, EmailTemplates);
await createStream.DisposeAsync();
File.WriteAllText(#"C:\temp\Response.json", test.ToString());
Console.WriteLine(File.ReadAllText(fileName));
}
catch (Exception ex)
{
throw;
}
And what my program save to file:
System.Collections.Generic.List`1[Test.API.Client.Infrastructure.Models.SparkPostTemplateDetailModel]
I want it to look like this:
Id1
Id2
Id3
Thanks for any tips!
Trying to transform a list to string wont tell you anything about it's contents. What if the list contains complex objects? what if the list contains more lists? That could get really confusing, so generic list only show you the most basic information about them when transforming them to strings.
Not sure why you are calling JsonSerializer.SerializeAsync, your wanted result isn't really a valid json file, more like a plain text file.
Your code is almost there, you just need to take each element in your list and append it to your file:
private SparkPostTemplateModel EmailTemplates { get; set; } = new();
public async Task SaveTemplate()
{
try
{
EmailTemplates = await SparkPostManager.GetTemplates();
var test = EmailTemplates.Results.ToList();
await using (StreamWriter w = File.AppendText(fileName))
//loop through the list elements and append lines
foreach (var template in test)
{
w.WriteLine(template.Id);
}
Console.WriteLine(File.ReadAllText(fileName));
}
catch (Exception ex)
{
throw;
}
This should work
Edit - File per loop
private SparkPostTemplateModel EmailTemplates { get; set; } = new();
public async Task SaveTemplate()
{
try
{
EmailTemplates = await SparkPostManager.GetTemplates();
var test = EmailTemplates.Results.ToList();
//AppendText will create a new file, if the file doesn't exist already
//insead of a foreach loop, do a regular for loop, so we can track the index of our elements
for (int i = 0; i < test.Count; i++)
{
//use the index to name the file (add plus one, so the numeration starts with the number one)
await using (StreamWriter w = File.AppendText($"file{i + 1}.txt"))
//retrieve the object from the list using our index
w.WriteLine(test[i].Id);
}
}
catch (Exception ex)
{
throw;
}
I have several text files with the following data structure:
{
huge
json
block that spans across multiple lines
}
--#newjson#--
{
huge
json
block that spans across multiple lines
}
--#newjson#--
{
huge
json
block that spans across multiple lines
} etc....
So it is actually json blocks that are row delimited by "--##newjson##--" string .
I am trying to write a customer extractor to parse this. The problem is that I can't use string data type to feed json deserializer because it has a maximum size of 128 KB and the json blocks do not fit in this. What is the best approach to parse this file using a custom extractor?
I have tried using the code below, but it doesn't work. Even the row delimiter "--#newjson#--" doesn't seem to work right.
public SampleExtractor(Encoding encoding, string row_delim = "--#newjson#--", char col_delim = ';')
{
this._encoding = ((encoding == null) ? Encoding.UTF8 : encoding);
this._row_delim = this._encoding.GetBytes(row_delim);
this._col_delim = col_delim;
}
public override IEnumerable<IRow> Extract(IUnstructuredReader input, IUpdatableRow output)
{
//Read the input by json
foreach (Stream current in input.Split(_encoding.GetBytes("--#newjson#--")))
{
var serializer = new JsonSerializer();
using (var sr = new StreamReader(current))
using (var jsonTextReader = new JsonTextReader(sr))
{
var jsonrow = serializer.Deserialize<JsonRow>(jsonTextReader);
output.Set(0, jsonrow.status.timestamp);
}
yield return output.AsReadOnly();
}
}
You dont need a custom extractor to do that.
The best solution is add one json by line. Then you can use a text extractor and extract line by line. You can also pick your own delimiter.
REFERENCE ASSEMBLY [Newtonsoft.Json];
REFERENCE ASSEMBLY [Microsoft.Analytics.Samples.Formats];
#JsonLines=
EXTRACT
[JsonLine] string
FROM
#Full_Path
USING
Extractors.Text(delimiter:'\b', quoting : false);
#ParsedJSONLines =
SELECT
Microsoft.Analytics.Samples.Formats.Json.JsonFunctions.JsonTuple([JsonLine]) AS JSONLine
FROM
#JsonLines
#AccessToProperties=
SELECT
JSONLine["Property"] AS Property
FROM
#ParsedJSONLines;
Here is how you can achieve the solution:
1) Create a c# equivalent of your JSON object
Note:- Assuming all your json object are same in your text file.
E.g:
Json Code
{
"id": 1,
"value": "hello",
"another_value": "world",
"value_obj": {
"name": "obj1"
},
"value_list": [
1,
2,
3
]
}
C# Equivalent
public class ValueObj
{
public string name { get; set; }
}
public class RootObject
{
public int id { get; set; }
public string value { get; set; }
public string another_value { get; set; }
public ValueObj value_obj { get; set; }
public List<int> value_list { get; set; }
}
2) Change your de-serializing code like below after you have done the split based on the delimiter
using (JsonReader reader = new JsonTextReader(sr))
{
while (!sr.EndOfStream)
{
o = serializer.Deserialize<List<MyObject>>(reader);
}
}
This would deserialize the json data in c# class object which would solve your purpose.
Later which you can serialize again or print it in text or ...any file.
Hope it helps.
I have a configuration class with all the parameters of my application, to acquire images from a scanner.
I have parameters like color/BW, resolution...
The parameters are changed often, so I'm searching a solution to write automatically when I save the parameters the changed parameters in the app.config file. And to do the reverted thing, write my class from the app.config at the init of the software.
Here are my two classes :
private void GetParameters() {
try
{
var appSettings = ConfigurationManager.AppSettings;
Console.WriteLine( ConfigurationManager.AppSettings["MyKey"]);
if (appSettings.Count == 0)
{
Console.WriteLine("AppSettings is empty.");
}
else
{
foreach (var key in appSettings.AllKeys)
{
Console.WriteLine("Key: {0} Value: {1}", key, appSettings[key]);
}
}
}
catch (ConfigurationErrorsException)
{
MessageBox.Show("Error reading app settings");
}
}
private void SetParameters(string key, string value)
{
try
{
Configuration configManager = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
KeyValueConfigurationCollection confCollection = configManager.AppSettings.Settings;
if (confCollection[key] == null)
{
confCollection.Add(key, value);
}
else
{
confCollection[key].Value = value;
}
configManager.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection(configManager.AppSettings.SectionInformation.Name);
}
catch (ConfigurationErrorsException)
{
MessageBox.Show("Error writing app settings");
}
}
I don't want to call the method for every parameter...
And there is my parameters class :
class ScannerParameters
{
public bool Color { get; set; }
public int Resolution{ get; set; }
public string FilePath { get; set; }
public TypeScan TypeScan { get; set; }
public string TextTest{ get; set; }
}
The question can be translated into how do I save an object into some kind of persistence?
Either use a database (seems like an overkill) or serialize it using a serializer or simply write it all down into a text file yourself. Using json serialization, serializing your ScannerParameters and then writing that into a file would seem most appropriate.
Using newtonsoft json, which is defacto standard for .net there's nice examples # http://www.newtonsoft.com/json/help/html/SerializingJSON.htm
In your case you would do:
// our dummy scannerParameters objects
var parameters = new ScannerParameters();
// let's serialize it all into one string
string output = JsonConvert.SerializeObject(paramaters);
// let's write all that into a settings text file
System.IO.File.WriteAllText("parameters.txt", output);
// let's read the file next time we need it
string parametersJson = System.IO.File.ReadAllText("parameters.txt);
// let's deserialize the parametersJson
ScannerParameters scannerParameters = JsonConvert.DeserializeObject<ScannerParameters>(parametersJson);
In C#, how can I save the checkstate value of checkboxes and radio buttons or text value in textboxes and richtextboxes in a single file? If it is even possible at all? How can I then make it possible for the user to load that file back into the software so all the values go back to the same checkboxes, and textboxes they were saved from?
You can do it in this (basic) way:
Just use a xml to store the data to and then read it back from the xml to access the objects data.
1.) Build a class for the data
[Serializable()]
[XmlRoot("MyData", Namespace = "")]
public class MyData
{
public MyData() { }
public bool checkBox1State { get; set; }
public bool checkBox2State { get; set; }
public string radioGroupSelected { get; set; }
public string button1Text { get; set; }
...
}
Then build 2 methods for reading and writing the xml file:
public static class MyDataWorker
{
public static MyData ReadMyData()
{
XmlSerializer reader = new XmlSerializer(typeof(MyData));
System.IO.StreamReader file = new System.IO.StreamReader("MyData.xml");
MyData data = new MyData();
data = (MyData)reader.Deserialize(file);
file.Close();
return data;
}
public static void WriteMyData(MyData data)
{
XmlSerializer writer = new XmlSerializer(typeof(MyData));
System.IO.StreamWriter file = new System.IO.StreamWriter("MyData.xml");
writer.Serialize(file, data);
file.Close();
}
}
Usage:
// Create data object instance
MyData data = new MyData();
// Fill Data from controls
data.checkBox1State = checkBox1.Checked;
...
// Write File to XML
MyDataWorker.WriteMyData(data);
// Read File to MyData object
MyData data = MyDataWorker.ReadMyData();
Further steps could be, extending the functions with parameters to read/write specific XML files to different paths.
So far for a small wrap up.
So I am reading a xml file with unknown length and reading each element into a list structure. Right now once I get to the end of the file I continue reading, this causes an exception. Right now I just catch this exception and continue with my life but is there a cleaner way to do this?
try
{
while(!textReader.EOF)
{
// Used to store info from each command as they are read from the xml file
ATAPassThroughCommands command = new ATAPassThroughCommands ();
// the following is just commands being read and their contents being saved
XmlNodeType node = textReader.NodeType;
textReader.ReadStartElement( "Command" );
node = textReader.NodeType;
name = textReader.ReadElementString( "Name" );
node = textReader.NodeType;
CommandListContext.Add(name);
command.m_Name = name;
command.m_CMD = Convert .ToByte(textReader.ReadElementString("CMD" ),16);
command.m_Feature = Convert .ToByte(textReader.ReadElementString("Feature" ),16);
textReader.ReadEndElement(); //</command>
m_ATACommands.Add(command);
}
}
catch ( Exception ex)
{
//</ATAPassThrough> TODO: this is an ugly fix come up with something better later
textReader.ReadEndElement();
//cUtils.DisplayError(ex.Message);
}
xml file:
<ATAPassThrough>
<Command>
<Name>Smart</Name>
<CMD>B0</CMD>
<Feature>D0</Feature>
</Command>
<Command>
<Name>Identify</Name>
<CMD>B1</CMD>
<Feature>D0</Feature>
</Command>
.
.
.
.
</ATAPassThrough>
I would recomend using XDocument for reading XML data... for instance in your case since you already have a TextReader for your XML you can just pass that into the XDocument.Load method... your entire function above looks like this..
var doc = XDocument.Load(textReader);
foreach (var commandXml in doc.Descendants("Command"))
{
var command = new ATAPassThroughCommands();
var name = commandXml.Descendants("Name").Single().Value;
// I'm not sure what this does but it looks important...
CommandListContext.Add(name);
command.m_Name = name;
command.m_CMD =
Convert.ToByte(commandXml.Descendants("CMD").Single().Value, 16);
command.m_Feature =
Convert.ToByte(commandXml.Descendants("Feature").Single().Value, 16);
m_ATACommands.Add(command);
}
Significantly easier. Let the framework do the heavy lifting for you.
Probably the easiest way if you have normal and consistant XML is to use the XML Serializer.
First Create Objects that match your XML
[Serializable()]
public class Command
{
[System.Xml.Serialization.XmlElement("Name")]
public string Name { get; set; }
[System.Xml.Serialization.XmlElement("CMD")]
public string Cmd { get; set; }
[System.Xml.Serialization.XmlElement("Feature")]
public string Feature { get; set; }
}
[Serializable()]
[System.Xml.Serialization.XmlRoot("ATAPassthrough")]
public class CommandCollection
{
[XmlArrayItem("Command", typeof(Command))]
public Command[] Command { get; set; }
}
The a method to return the CommandCollection
public class CommandSerializer
{
public commands Deserialize(string path)
{
CommandCollection commands = null;
XmlSerializer serializer = new XmlSerializer(typeof(CommandCollection ));
StreamReader reader = new StreamReader(path);
reader.ReadToEnd();
commands = (CommandCollection)serializer.Deserialize(reader);
reader.Close();
return commands ;
}
}
Not sure if this is exactly correct, I don't have the means to test it, but is should be really close.