Adding List Data to a Text File - c#

I am trying to save the list of data from C# code to a text file. It throws an error at foreach stating that list cannot be converted to string. Any ideas where I am going wrong?
I have to Run Application only on .Net Framework 3.5
IList<FolderDetails> ListToCheck;
using (System.IO.StreamWriter file = new System.IO.StreamWriter(#"C:\LogFile.txt"))
{
foreach (string line in ListToCheck)
{
file.WriteLine(line);
}
}
public class FolderDetails
{
public string PropertyGroupName { get; set; }
public string ProjFiles { get; set; }
}

Examine this code:
IList<FolderDetails> ListToCheck;
using (System.IO.StreamWriter file = new System.IO.StreamWriter(#"C:\LogFile.txt"))
{
foreach (string line in ListToCheck)
You say ListToCheck is a list of objects of type FolderDetail, but in
foreach (string line in ListToCheck)
you try to assert that the type is string instead.
Try something like
foreach (FolderDetail line in ListToCheck)
{
file.WriteLine(line.SomeAppropriatePropertyOfFolderDetail);

Rework it this way...
Add a ToString() override to your class:
private class FolderDetails
{
public string PropertyGroupName { get; set; }
public string ProjFiles { get; set; }
public override string ToString()
{
return string.Format("{0}:{1}", this.PropertyGroupName, this.ProjFiles);
}
}
Then output like this:
List<FolderDetails> foldersListToCheck = GetFolderDetails();
File.WriteAllLines(#"C:\LogFile.txt", foldersListToCheck.Select(f => f.ToString()));

IList<FolderDetails> ListToCheck;
using (System.IO.StreamWriter file = new System.IO.StreamWriter(#"C:\LogFile.txt"))
{
foreach (string line in ListToCheck)
{
file.WriteLine(line);
}
}
In the above code ListToCheck is not a list of strings and it is not initialized

Related

Passing Data From Text File to Constructor

I'm looking for a way to pass information from a text file into a constructor so that I can create an array of that constructor object with each object in the array holding information from the rows of the text file.
The constructor is formatted as follows:
public Member(string name, int number, decimal rate, double hours)
While the text file is formatted as such:
Eric Wallace, 352456, 15.88, 32.20
Clara Kell, 233424, 35.88, 18.76
Darren Price, 656795, 27.82, 20.25
etc...
and each Member will go into an array.
In the end, what I need is for each row to be split up and passed to the constructor in a way where each row becomes its own member in an array so that they can be output one after another in a loop or called individually as rows.
My approach would begin with making an interface that all my "buildable" data types will implement. I want my data models deciding how they are built from a string:
public interface IBuildableFromString
{
public IBuildableFromString Build(string str, string seperator = ",");
}
Then make Member implement it like so:
public class Member : IBuildableFromString
{
public string Name { get; set; }
public int Number { get; set; }
public decimal Rate { get; set; }
public double Hours { get; set; }
public Member() { }
public Member(string name, int number, decimal rate, double hours)
{
Name = name;
Number = number;
Rate = rate;
Hours = hours;
}
public IBuildableFromString Build(string str, string seperator = ",")
{
try
{
string[] parts = str.Split(seperator);
return new Member(parts[0], int.Parse(parts[1]),
decimal.Parse(parts[2]), double.Parse(parts[3]));
}
catch
{
return null;
}
}
}
Then the method to read the file and build the object data:
public static T[] BuildData<T>(string filePath) where T :
IBuildableFromString, new()
{
List<T> dataObjects = new List<T>();
string[] lines = File.ReadAllLines(filePath);
foreach (string line in lines)
{
if (!String.IsNullOrEmpty(line))
{
var newMember = new T().Build(line);
if (newMember != null)
dataObjects.Add((T)newMember);
}
}
return dataObjects.ToArray();
}
Lastly, call the function above like so:
static void Main(string[] args)
{
var data = BuildData<Member>(#"path_to_your_file.txt");
}
It probably needs more error checking, but this was the most extensible way I could think of doing it. Cheers!
As long as your file is well-formed, then this would work:
Member[] members =
File
.ReadLines(#"mytextfile.txt")
.Select(x => x.Split(',').Select(y => y.Trim()).ToArray())
.Select(x => new Member(x[0], int.Parse(x[1]), decimal.Parse(x[2]), double.Parse(x[3])))
.ToArray();
I will use StreamReader to read the txt file, then use replace to eliminate spaces, and then use split to split the data.
Use StreamReader to read text from a file:
StreamReader sr = new StreamReader(#"C:\demo\de.txt")
Make Member implement it like so:
public class Member {
public string Name { get; set; }
public int Number { get; set; }
public decimal Rate { get; set; }
public double Hours { get; set; }
public Member(string name, int number, decimal rate, double hours) {
Name = name;
Number = number;
Rate = rate;
Hours = hours;
}
}
Call the data like this:
foreach (var item in members) {
Console.WriteLine($"{ item.Name} { item.Number} { item.Rate} { item.Hours}");
}
Total code:
using System;
using System.Collections.Generic;
using System.IO;
namespace ConsoleApp2 {
class Program {
static void Main(string[] args) {
List<Member> members = new List<Member>();
try {
// Create an instance of StreamReader to read from a file.
// The using statement also closes the StreamReader.
using (StreamReader sr = new StreamReader(#"C:\demo\de.txt")) {
string line;
// Read and display lines from the file until the end of
// the file is reached.
while ((line = sr.ReadLine()) != null) {
line = line.Replace(" ", "");
string[] tmp = line.Split(',');
string name = tmp[0];
int number = Convert.ToInt32(tmp[1]);
decimal rate = Convert.ToDecimal(tmp[2]);
double hours = Convert.ToDouble(tmp[3]);
members.Add(new Member(name, number, rate, hours));
}
}
} catch (Exception e) {
// Let the user know what went wrong.
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}
foreach (var item in members) {
Console.WriteLine($"{ item.Name} { item.Number} { item.Rate} { item.Hours}");
}
Console.ReadLine();
}
public class Member {
public string Name { get; set; }
public int Number { get; set; }
public decimal Rate { get; set; }
public double Hours { get; set; }
public Member(string name, int number, decimal rate, double hours) {
Name = name;
Number = number;
Rate = rate;
Hours = hours;
}
}
}
}
If you have questions, please add a comment.

Iteration cannot operate on variables of type public definition for 'getenumerator'

I am doing a search in which I am making an API call and get the XML response and SerializeXmlNode and DeserializeObject to my root object. Now the problem is when I tried to loop with foreach.
I get this error below:
foreach statement cannot operate on variables of type (Model.AccountLite) because does not contain public instance definition for 'getenumerator'
I have inspected this data = JsonConvert.DeserializeObject(json); and i can see the data.
I have tried to look at this previously asked question
Search API call
public static List<AccountLite> searchAccounts(string searchString)
{
List<AccountLite> result = new List<AccountLite>();
Root data = new Root();
string[] contains = searchString.Split(' ');
RestClient client = new RestClient(baseUrl);
foreach (string contain in contains)
{
if (contain.Length < 3) continue;
RestRequest request = new RestRequest($"/xx/xx/xx/xxx/xxx/account?xx=Lite&searchString={searchString}");
String encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
request.AddHeader("Authorization", "Basic " + encoded);
IRestResponse response = client.Execute(request);
string requestResponse = response.Content;
//Converting data from XML into Json and deserializet json object
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(requestResponse);
string json = JsonConvert.SerializeXmlNode(doc);
data = JsonConvert.DeserializeObject<Root>(json);
}
catch (Exception)
{
continue;
}
if (data?.SiebelMessageEnvelope?.ListOfAccountLite?.AccountLite == null)
continue;
//this line is the one showing error.
foreach (AccountLite item in data.SiebelMessageEnvelope.ListOfAccountLite.AccountLite)
{
bool containsBoth = true;
foreach (string contain2 in contains)
{
if (!item.Name.ToLower().Contains(contain2.ToLower()) && !item.Id.ToLower().Contains(contain2.ToLower()))
containsBoth = false;
}
if (containsBoth)
{
if (result.FirstOrDefault(i => i.Id == item.Id) == null)
{
result.Add(item);
}
}
}
}
return result;
}
Model
public class AccountLite
{
public string Id { get; set; }
public string AccountStatus { get; set; }
public string AccountTypeCode { get; set; }
public string Location { get; set; }
public string Name { get; set; }
public string SRIntegrationFlag { get; set; }
}
public class ListOfAccountLite
{
public AccountLite AccountLite { get; set; }
}
public class SiebelMessageEnvelope
{
[JsonProperty("#xmlns")]
public string Xmlns { get; set; }
public ListOfAccountLite ListOfAccountLite { get; set; }
}
public class Root
{
public SiebelMessageEnvelope SiebelMessageEnvelope { get; set; }
}
Json Object
{
"SiebelMessageEnvelope":{
"#xmlns":"",
"ListOfAccountLite":{
"AccountLite":{
"Id":"",
"AccountStatus":"",
"AccountTypeCode":"",
"Location":"",
"Name":"",
"SRIntegrationFlag":""
}
}
}
}
Your ListOfAccountLite just contains a single AccountLite. It doesn't make sense to foreach over a single object, where that object is not enumerable (meaning: implemented IEnumerable[<T>] or contains an explicit GetEnumerator() method).
There's only one object, so... just take it. Instead of
if (data?.SiebelMessageEnvelope?.ListOfAccountLite?.AccountLite == null)
continue;
foreach (AccountLite item in data.SiebelMessageEnvelope.ListOfAccountLite.AccountLite)
{
// ... do the thing
}
simply
var item = data?.SiebelMessageEnvelope?.ListOfAccountLite?.AccountLite;
if (item is null)
continue;
// ... do the thing
That said: you should probably investigate whether ListOfAccountLite in the JSON etc is meant to be an array rather than a single object.

Cannot create an Abstract class when trying to create List<T> from CSV using csv.GetRecords()

I'm attempting to create a generic method to enable me to parse a CSV document into an object of my choice.
Everything seems to work ok but the results after executing the csv.GetRecords() method are empty and the inner exception of the response is "Instances of abstract classes cannot be created."
I've also tried using the csv.EnumerateRecords(record); and get the same result.
public class ImportManager
{
[Ignore]
public string FileSeperator { get; set; }
[Ignore]
public string Filename { get; set; }
public IEnumerable<T> ParseFile<T>() where T : class
{
using (var reader = new StreamReader(this.Filename))
using (var csv = new CsvReader(reader))
{
csv.Configuration.Delimiter = this.FileSeperator;
csv.Configuration.HasHeaderRecord = true;
var results = csv.GetRecords<T>();
return results;
}
}
}
public class MyObject : ImportManager
{
public string Field1 { get; set; }
public DateTime Field2 { get; set; }
public int Field3 { get; set; }
public List<MyObject> LoadFile()
{
var response = ParseFile<MyObject>();
return response.ToList<MyObject>();
}
}
MyObject moObjList= new MyObject() { Filename = "MyFileName.txt", FileSeperator = "|" };
var results = moObjList.LoadFile();
Help!
I believe adding ToList() to csv.GetRecords<T>() may solve your issue. GetRecords<T>() does a lazy load. It doesn't attempt to enumerate the records until you call return response.ToList<MyObject>(); at which time the StreamReader is already disposed.
public class ImportManager
{
[Ignore]
public string FileSeperator { get; set; }
[Ignore]
public string Filename { get; set; }
public IEnumerable<T> ParseFile<T>() where T : class
{
using (var reader = new StreamReader(this.Filename))
using (var csv = new CsvReader(reader))
{
csv.Configuration.Delimiter = this.FileSeperator;
csv.Configuration.HasHeaderRecord = true;
var results = csv.GetRecords<T>().ToList();
return results;
}
}
}

Get All Directories of a Folder Path Recursively into my Poco

I wrote a poco like this :
public class MyDirectory
{
public string Path { get; set; }
public List<MyDirectory> MyDirectories { get; set; }
}
then I wrote 2 methods like this
private static List<MyDirectory> lst = new List<MyDirectory>();
private static void DirSearch(string dir)
{
var dirs = Directory.GetDirectories(dir);
foreach (string d in dirs)
{
var ddd = new MyDirectory();
ddd.Path = d;
ddd.MyDirectories = GetList(Directory.GetDirectories(d));
lst.Add(ddd);
DirSearch(d);
}
}
private static List<MyDirectory> GetList(string[] list)
{
var lst = new List<MyDirectory>();
foreach (var l in list)
{
lst.Add(new MyDirectory() { Path = l });
}
return lst;
}
but I does not work Can anybody change it to working method ?
I dont know How fill my Poco correctly. I need recursively method.
If you want to recursively load directories then you don't GetList method. The method DirSearch is sufficient but need some refactoring like below:
private static List<MyDirectory> DirSearch(string directory)
{
var directories = new List<MyDirectory>();
foreach (var l in Directory.EnumerateDirectories(directory))
{
directories.Add(new MyDirectory
{
Path = l,
MyDirectories = DirSearch(l)
});
}
return directories;
}
Notice in the snippet I'm using Directory.EnumerateDirectories instead of Directory.GetDirectories. I do this because the latter isn't performant.
To call this method just do this:
lst = DirSearch(pathToYourDirectory);
You can make it better by using IEnumerable instead of List as type of MyDirectories property.
Your code will finally look like this :
public class MyDirectory
{
public string Path { get; set; }
public IEnumerable<MyDirectory> MyDirectories { get; set; }
}
private static IEnumerable<MyDirectory> DirSearch(string directory)
{
return Directory.EnumerateDirectories(directory).Select(l => new MyDirectory
{
Path = l,
MyDirectories = DirSearch(l)
});
}
Indeed your static property lst need to be of type IEnumerable<MyDirectory> also.
By doing those changes you will be able to start using each instance of MyDirectory instead of waiting all subdirectories to be returned when using List.
Instead of do recursive, you can use this code
Directory.GetDirectories(path, "*", SearchOption.AllDirectories)
Create cunstructor for your POCO.
public class MyDirectory
{
public MyDirectory()
{
MyDirectories = new List<MyDirectory>();
}
public string Path { get; set; }
public List<MyDirectory> MyDirectories { get; set; }
}
Recursive Method:
private MyDirectory FindRecursiveDirectories(string dir)
{
var rootDirectory = new MyDirectory();
rootDirectory.Path = dir;
foreach (var subDirectory in Directory.EnumerateDirectories(dir))
{
var myDir = FindRecursiveDirectories(subDirectory);
rootDirectory.MyDirectories.Add(myDir);
}
return rootDirectory;
}
Finally call FindRecursiveDirectories Method.
var result = FindRecursiveDirectories(yourDirectory);

Problem with serializing a dictionary wrapper

I defined two classes. First one...
[Serializable]
public class LocalizationEntry
{
public LocalizationEntry()
{
this.CatalogName = string.Empty;
this.Identifier = string.Empty;
this.Translation = new Dictionary<string, string>();
this.TranslationsList = new List<Translation>();
}
public string CatalogName
{
get;
set;
}
public string Identifier
{
get;
set;
}
[XmlIgnore]
public Dictionary<string, string> Translation
{
get;
set;
}
[XmlArray(ElementName = "Translations")]
public List<Translation> TranslationsList
{
get
{
var list = new List<Translation>();
foreach (var item in this.Translation)
{
list.Add(new Translation(item.Key, item.Value));
}
return list;
}
set
{
foreach (var item in value)
{
this.Translation.Add(item.Language, item.Text);
}
}
}
}
...where public List<Translation> TranslationsList is a wrapper for non-serializable public Dictionary<string, string> Translation.
Pair of key and value is defined as follows:
[Serializable]
public class Translation
{
[XmlAttribute(AttributeName = "lang")]
public string Language
{
get;
set;
}
[XmlText]
public string Text
{
get;
set;
}
public Translation()
{
}
public Translation(string language, string translation)
{
this.Language = language;
this.Text = translation;
}
}
At last code used to serialize:
static void Main(string[] args)
{
LocalizationEntry entry = new LocalizationEntry()
{
CatalogName = "Catalog",
Identifier = "Id",
};
entry.Translation.Add("PL", "jabłko");
entry.Translation.Add("EN", "apple");
entry.Translation.Add("DE", "apfel");
using (FileStream stream = File.Open(#"C:\entry.xml", FileMode.Create))
{
XmlSerializer serializer = new XmlSerializer(typeof(LocalizationEntry));
serializer.Serialize(stream, entry);
}
LocalizationEntry deserializedEntry;
using (FileStream stream = File.Open(#"C:\entry.xml", FileMode.Open))
{
XmlSerializer serializer = new XmlSerializer(typeof(LocalizationEntry));
deserializedEntry = (LocalizationEntry)serializer.Deserialize(stream);
}
}
The problem is that after deserialization deserializedEntry.TranslationsList is empty. I set a breakpoint at setter of LocalizationEntry.TransalionsList and it comes from deserializer empty as well. Product of serialization is of course valid. Is there any gap in my code?
EDIT:
Here is generated XML:
<?xml version="1.0"?>
<LocalizationEntry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CatalogName>Catalog</CatalogName>
<Identifier>Id</Identifier>
<Translations>
<Translation lang="PL">jabłko</Translation>
<Translation lang="EN">apple</Translation>
<Translation lang="DE">apfel</Translation>
</Translations>
</LocalizationEntry>
The problem is that your TranslationList property is not being set by the Xml Deserializer. The set method will be hit but only by the call to this.TranslationsList = new List(); in the LocalisationEntry constructor. I'm not yet sure why but I suspect it's because it doesn't know how to convert an array of Translation objects back into a List.
I added the following code and it worked fine:
[XmlArray(ElementName = "Translations")]
public Translation[] TranslationArray
{
get
{
return TranslationsList.ToArray();
}
set
{
TranslationsList = new List<Translation>(value);
}
}
[XmlIgnore]
public List<Translation> TranslationsList
....
I am guessing the problem has to do with this:
public List<Translation> TranslationsList
The get/set operators are designed only for something to get or assign a fully-formed list. If you tried to use this in your own code, for example, every time you would do something like
TranslationsList.Add(item)
It would just create a new list from the existing dictionary and not actually deal with your item. I bet the deserializer works much the same way: uses set to create the new object once, then uses get as it adds each item from the XML. Since all that happens in get is it copies from the dictionary (which is empty when you begin your deserialization) you end up with nothing.
Try replacing this with just a field:
public List<Translation> TranslationsList;
and then explicitly call the code to copy the dictionary to this list before you serialize, and copy it from this list to the dictionary after you deserialize. Assuming that works, you can probably figure out a more seamless way to implement what you're trying to do.
I've created a sample, which will allow you to avoid the unnecessary hidden property when using the XmlSerializer:
class Program
{
static void Main(string[] args)
{
LocalizationEntry entry = new LocalizationEntry()
{
CatalogName = "Catalog",
Identifier = "Id",
Translations =
{
{ "PL", "jabłko" },
{ "EN", "apple" },
{ "DE", "apfel" }
}
};
using (MemoryStream stream = new MemoryStream())
{
XmlSerializer serializer = new XmlSerializer(typeof(LocalizationEntry));
serializer.Serialize(stream, entry);
stream.Seek(0, SeekOrigin.Begin);
LocalizationEntry deserializedEntry = (LocalizationEntry)serializer.Deserialize(stream);
serializer.Serialize(Console.Out, deserializedEntry);
}
}
}
public class LocalizationEntry
{
public LocalizationEntry() { this.Translations = new TranslationCollection(); }
public string CatalogName { get; set; }
public string Identifier { get; set; }
[XmlArrayItem]
public TranslationCollection Translations { get; private set; }
}
public class TranslationCollection
: Collection<Translation>
{
public TranslationCollection(params Translation[] items)
{
if (null != items)
{
foreach (Translation item in items)
{
this.Add(item);
}
}
}
public void Add(string language, string text)
{
this.Add(new Translation
{
Language = language,
Text = text
});
}
}
public class Translation
{
[XmlAttribute(AttributeName = "lang")]
public string Language { get; set; }
[XmlText]
public string Text { get; set; }
}
There are some drawbacks when working with the XmlSerializer class itself. The .NET guidelines encourage you the not provide public-setters for collection-properties (like your translation list). But when you look at the code generated by the XmlSerializer, you'll see that it will use the Setter regardless of it is accessible. This results in a compile-error when the interim class is dynamically loaded by the XmlSerializer. The only way to avoid this, is to make the XmlSerializer think, that it can't actually create an instance of the list and thus won't try to call set for it. If the XmlSerializer detects that it can't create an instance it will throw an exception instead of using the Setter and the interim class is compiled successfully. I've used the param-keyword to trick the serializer into thinking that there is no default-constructor.
The only drawback from this solution is that you have to use a non-generic, non-interface type for the property (TranslationCollection) in my example.

Categories

Resources