Wants to populate dictionary with data from website, but I fail on adding them [duplicate] - c#

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed yesterday.
I am trying to make method where I could scrap data from webpage that's behind password locked form. I used Selenium Web Driver to successfully connect to website and login there, but I would like to store the data do Dicitonary that I made.
// Custom params
public class Params_Rezervation
{
public string Time { get; set; }
public string Guests { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Status { get; set; }
public string Text { get; set; }
public string Table { get; set; }
}
This is the custom params I would like to have in my dicitonary so I could access them. I tried to make this dictionary.
public static Dictionary<int, Params_Rezervace> rezervace_data = new Dictionary<int, Params_Rezervace>();
Then I tried it populate it with data from website, but I fail on adding data to dictionary.
public static void LoadReservation()
{
// Setting up a
var chromeOptions = new ChromeOptions();
chromeOptions.AddArguments("--headless");
// Console hiding
var chromeDriverService = ChromeDriverService.CreateDefaultService();
chromeDriverService.HideCommandPromptWindow = true;
try
{
using (var browser = new ChromeDriver(chromeDriverService, chromeOptions))
{
// Setting up a website
browser.Navigate().GoToUrl("specific website");
// Entering password + submiting it
browser.FindElement(By.Id("pwbox-580")).SendKeys("password");
browser.FindElement(By.Name("Submit")).Click();
// Finding the specific table
IWebElement tableElement = browser.FindElement(By.XPath("//*[#id=\"post-580\"]/div/div[2]/table"));
// Finding the row
IList<IWebElement> rows = tableElement.FindElements(By.XPath(".//tr"));
// Data
int number = 0;
foreach (IWebElement row in rows)
{
// Finding specific columns
IList<IWebElement> columns = row.FindElements(By.XPath(".//th"));
//The error line where I dont know how to continue
rezervace_data.Add(number, Params_Rezervace);
// Data populating
rezervace_data[number].Time = columns[0].Text;
rezervace_data[number].Guests = columns[1].Text;
rezervace_data[number].Name = columns[2].Text;
rezervace_data[number].Email = columns[3].Text;
rezervace_data[number].Text = columns[4].Text;
rezervace_data[number].Table = columns[5].Text;
number++;
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error while loading data! \n" + ex, "Error", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
Error I get:
System.NullReferenceException: 'The object reference is not set to an instance of the object.'
System.Collections.Generic.Dictionary<TKey, TValue>.this[TKey].get returned null.
I trying to learn programming so If someone will answer me what to change it will be very helpfull, because I am learning it by myself and websites :)

Your “error line” should be something like this:
rezervace_data.Add(number, new Params_Rezervace());
You need to add an object in your dictionary of type Params_Rezervace, and the way you do that is with the new keyword.

Related

How to create a Class List with different numbers of inputs in C#

I'm working on my first real c# project and I have faced a problem with my way of creating List based on a Class, which I have no idea how to solve.
I’m trying to write some code, which takes an input file (txt/csv) of multiple constructions with multiple layers, put it into my program, and later write the constructions into a new txt/csv file.
When having the same numbers of layers, it works fine. But when the constructions have different numbers of layers it causes trouble and I get a “System.IndexOutOfRangeException”.
My question is: Can I make the Class which I’m basing my List on, dynamic (I don’t know if it is the technical term), so it work with different numbers of inputs? Both when Adding the construction to the program and when I write it to a new file?
My code is:
class Program
{
static void Main(string[] args)
{
// Filepath for the input and output file
string filePathIn_constructions = #"C:\Library\Constructions.txt";
string filePathOut = #"C:\Library\EPlus_Inputfile.txt";
// Creating a list of constructions based on the class. The list is made from the file "filePathIn_constructions"
List<Construction> allConstructions = new List<Construction>();
List<string> lines_constructions = File.ReadAllLines(filePathIn_constructions).ToList(); // add it to a list
// Adding all the data from the fil to the variable "allConstructions"
foreach (var line in lines_constructions)
{
string[] entries = line.Split(',');
Construction newConstruction = new Construction();
newConstruction.EIndex = entries[0];
newConstruction.Name = entries[1];
newConstruction.Layer1 = entries[2];
newConstruction.Layer2 = entries[3];
newConstruction.Layer3 = entries[4];
newConstruction.Layer4 = entries[5];
newConstruction.Layer5 = entries[6];
allConstructions.Add(newConstruction); // Add it to our list of constructions
}
List<string> output = new List<string>();
foreach (var x in allConstructions) // Printing the new
{
output.Add($"{x.EIndex}, {x.Name}, {x.Layer1}, {x.Layer2}, {x.Layer3}, {x.Layer4}, {x.Layer5}");
}
File.WriteAllLines(txtFilePathOut, output);
}
}
My Class for the Constructions is
public class Construction
{
public string EIndex { get; set; }
public string Name { get; set; }
public string Layer1 { get; set; }
public string Layer2 { get; set; }
public string Layer3 { get; set; }
public string Layer4 { get; set; }
public string Layer5 { get; set; }
}
An example of a input/output file could be
Construction,ConcreteWall,Concrete;
Construction,Brickwall1,Birck,Isulation,Brick;
Construction,Brickwall2,Birck,AirGap,Isulation,Brick;
Construction,Wood/Concrete Wall,Wood,Isulation,Concrete,Gypson;
Construction,Wood Wall,Wood,AirGap,Gypson,Isulaiton,Gypson;
I hope someone can help. Thanks.
Edit: I have to be able to excess the construction Name seperatly, because i'm using it to do some sorting of the.
public class Construction
{
public string EIndex { get; set; }
public string Name { get; set; }
public List<string> Layers { get; set; } = new List<string>();
}
foreach (var line in lines_constructions)
{
string[] entries = line.Split(',');
Construction newConstruction = new Construction();
newConstruction.EIndex = entries[0];
newConstruction.Name = entries[1];
for (int i=2; i < entries.Length; i++) {
newConstruction.Layers.Add(entries[i]);
}
allConstructions.Add(newConstruction);
}
foreach(var x in allConstuctions) {
File.AppendAllText(output, $"{x.EIndex}, {x.Name}, {string.Join(", ", x.Layers)}");
}
It is because you are trying to reach a cell of an array that doesn't exist (documentation)
In your input/output file you have lines that have between 3 and 7 values, and you are building an array entries out of those values. This means that you will have arrays with between 3 and 7 cells
The problem is that right after creating those arrays you try to access on every array the cells 0, 1, 2... up to the 7th, even for arrays that have only 3 cells!
What you could do to fix this in a simple way is to add columns to have the same number of separator on each lines (you defined the separator of your lines as column with line.Split(',')). This way, every arrays that you will create will always have 7 cells, even if the value inside is null

Efficiently fetching information from .xml files and make them available to other methods

first I'm sorry if I mix up some terminology or overlooked some very obvious method, but I'm quite new in C# and OOP in general and this is my project I "self learn" c# with at the moment. That's why I will share larger parts of my code to hopefully iron out some unclarities.
I wan't to get information from an .xml file and use those information in various different methods. For this I found a solution which I implemented and works fine, but is kind of clunky.
I use this Method to read out everything I need from the XML:
class ReadAndLoad
{
public List<CharacterAttributes> FetchAttributes(string userPath)
{
// Declare a new XML Document
XmlDocument XmlDoc = new XmlDocument();
// Try to open the XML file
try
{
Console.WriteLine("\nNow Loading: {0}\n", userPath);
XmlDoc.Load(userPath);
}
// Catch "File Not Found" errors
catch (System.IO.FileNotFoundException)
{
Console.WriteLine("No file found!");
Environment.Exit(1);
}
// Catch Argument Exceptions
catch (System.ArgumentException)
{
Console.WriteLine("Invalid path detected!");
Environment.Exit(1);
}
// Catach all other errors, and print them to console.
catch (Exception err)
{
Console.WriteLine("An Exception has been caught:");
Console.WriteLine(err);
Environment.Exit(1);
}
// Declare the xpath for finding objects inside the XML file
XmlNodeList XmlDocNodes = XmlDoc.SelectNodes("/character/attributes/attribute");
// Define a new List, to store the objects we pull out of the XML
List<CharacterAttributes> attributeList = new List<CharacterAttributes>();
// Loop through the nodes, extracting Person information.
// We can then define a person object and add it to the list.
foreach (XmlNode node in XmlDocNodes)
{
int tempValue = int.Parse(node["totalvalue"].InnerText);
CharacterAttributes obj = new CharacterAttributes(node["name"].InnerText, tempValue);
attributeList.Add(obj);
}
for (int i = 0; i < attributeList.Count; i++)
{
Console.WriteLine(attributeList[i].AttributeName);
Console.WriteLine(attributeList[i].TotalValue);
}
return attributeList;
}
Created a "Character class" that has all attributes in an constructor
class Character
{
//Attribute Fields
public int Body { get; set; }
public int Agility { get; set; }
public int Reaction { get; set; }
public int Strength { get; set; }
public int Willpower { get; set; }
public int Logic { get; set; }
public int Intuition { get; set; }
public int Charisma { get; set; }
public int Edge { get; set; }
public int Essence { get; set; }
public int Resonance { get; set; }
public int Magic { get; set; }
//Attribute Constructor
public Character(int xmlBody, int xmlAgility, int xmlReaction, int xmlStrength, int xmlIntuition, int xmlCharisma, int xmlLogic, int xmlWillpower, int xmlEdge, int xmlMagic, int xmlResonance, int xmlEssence)
{
this.Body = xmlBody;
this.Agility = xmlAgility;
this.Reaction = xmlReaction;
this.Strength = xmlStrength;
this.Intuition = xmlIntuition;
this.Charisma = xmlCharisma;
this.Logic = xmlLogic;
this.Willpower = xmlWillpower;
this.Edge = xmlEdge;
this.Essence = xmlEssence;
this.Resonance = xmlResonance;
this.Magic = xmlMagic;
}
And to create a character I created this Method which takes the list provided by ReadAndLoad.FetchAttributes and feeds them in the constructor
class CreateCharacters
{
public Character CreateCharacterFromXML(string userPath)
{
ReadAndLoad readAndLoad = new ReadAndLoad();
List<CharacterAttributes> attributeList = new List<CharacterAttributes>();
attributeList = readAndLoad.FetchAttributes(userPath);
int bod = attributeList[0].TotalValue;
int agi = attributeList[1].TotalValue;
int rea = attributeList[2].TotalValue;
int str = attributeList[3].TotalValue;
int cha = attributeList[4].TotalValue;
int intuition = attributeList[5].TotalValue;
int log = attributeList[6].TotalValue;
int wil = attributeList[7].TotalValue;
int edg = attributeList[8].TotalValue;
int mag = attributeList[9].TotalValue;
int res = attributeList[11].TotalValue;
int ess = attributeList[12].TotalValue;
Character myCharacter = new Character(bod, agi, rea, str, cha, intuition, log, wil, edg, mag, res, ess);
return myCharacter;
}
}
I feel like there is a more elegant and efficient way to do this, which is easier to expand upon with more data from the same XML. Because at the moment if I would like to introduce some other data I would have to create a new ReadAndLoad Method, the new class for that information, expand the character class and constructor and add all that in the CreateCharacter Method.
Someone pointed me towards xml deserialization, but I wasn't able to make the examples given here work (I tried deserialization from TextReader).
The xml I tried to deserialize / get information from doesn't provide a schema as url, because of that I don't really know how to deserialize it. Given below is the start of the xml.
After some searching I found the schema .xsd that probably gives me the correct schema.
Deserialisazion is sadly not possible at the moment because of an error in the .xsd I can't find.
The .xsd I try to use for deserialization references another .xsd which seems to contain errors, which leads to some elements not being declared.
InteliSense sadly doesn't provide any information regarding that error.
The super-simple way to do this is to copy an XML document to your clipboard and in a C# code file Paste XML as Classes:

Deserialize JSON to list with unknown object name in C#

I want to deserialize following JSON.
The problem is that the objects "ANDE" & "DAR" can change.
Means the objects are unknown and change depending on the JSON i wanna deserialize.
About 8000 different objects (ANDE, DAR, ...) need to be deserialized.
{"ANDE":
{"chart":[
{"date":"20180914","minute":"09:30"},{"date":"20180914","minute":"13:30"}]},
"DAR":
{"chart":[
{"date":"20180914","minute":"09:30"},{"date":"20180914","minute":"13:30"}]}}
I get the data by HTTP response and want to put into a List:
HttpResponseMessage response = client.GetAsync(API_PATH).GetAwaiter().GetResult();
List historicalDataList = response.Content.ReadAsAsync<List<HistoricalDataResponse>>().GetAwaiter().GetResult();
The HistoricalDataResponse class looks like:
public class HistoricalDataResponse
{
public string date { get; set; }
public string minute { get; set; }
}
How can i deserialize this kind of JSON with unknown objects in C#?
Any help is highly appreciated.
Then you should use a dynamic variable:
dynamic ReturnValue = JsonConvert.DeserializeObject(jsonstring);
note that as in dynamic objects, properties are determined after being assigned in runtime, so you will not get a drop down menu in design time, and also as its properties are unknown in design time, and property you test in design time even if its not correct, you wont get an error, and you will get the error in runtime when it is assigned.
dynamic ReturnValue = JsonConvert.DeserializeObject(jsonstring);
try
{
var a = ReturnValue.ANDE; // will work if it has ANDE property.
// do what you would do with ANDE
}
catch{}
try
{
var a = ReturnValue.DAR; // will work if it has DAR property.
// do what you would do with DAR
}
catch{}
Use a dictionary with string as key type :
void Main()
{
var client = new HttpClient();
HttpResponseMessage response = client.GetAsync("url").GetAwaiter().GetResult();
var json = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
var result = JsonConvert.DeserializeObject<Dictionary<string, DateResponse>>(json);
foreach (var element in result)
{
var key = element.Key; // ANDE
foreach (var item in element.Value.Chart)
{
var date = item.date;
var minute = item.minute;
}
}
}
public class DateResponse{
public List<HistoricalDataResponse> Chart { get; set; }
}
public class HistoricalDataResponse
{
public string date { get; set; }
public string minute { get; set; }
}

Looping through a string array to create class variables of those strings

So I have this code in a .cs file called SchoolData that has a class and a list.
public static List<YearGroupsData> yearGroupsDataList = new List<YearGroupsData>();
public class YearGroupsData
{
public int id { get; set; }
public int year { get; set; }
public string groupName { get; set; }
public int subject { get; set; }
}
However, I'm trying to use a loop in another .cs script that does a web connection and gets the data from the website, I haven't included the connection info or some of the script for that as this isn't the part going wrong...
private IEnumerator ViewYearGroups()
{
//Some code for connection here
yield return viewYearGroups;
string yearGroupsDataString = viewYearGroups.text;
yearGroups = yearGroupsDataString.Split(';');
foreach (string yearGroup in yearGroups)
{
YearGroupsData yearGroupsData = new YearGroupsData()
{
id = Int32.Parse(GetDataValue(yearGroup, "Id:")),
year = Int32.Parse(GetDataValue(yearGroup, "Year:")),
groupName = GetDataValue(yearGroup, "GroupName:"),
subject = Int32.Parse(GetDataValue(yearGroup, "Subject:")),
};
SchoolData.yearGroupsDataList.Add(yearGroupsData);
}
}
The GetDataValue is the part that is messing up. It gives me ArgumentOutOfRangeException and I'm not sure why. It works if I'm not using it in a loop, I've tried a for loop as well and still the same, anyone know what's happening?
public string GetDataValue(string data, string index)
{
string value = data.Substring(data.IndexOf(index) + index.Length);
if (value.Contains("|"))
{
value = value.Remove(value.IndexOf("|"));
}
return value;
}
Add a try catch in your GetDataValue() method to help with debugging. If it works without the foreach loop, then my guess is one of the string objects you are iterating over is different than what you may be expecting.
https://msdn.microsoft.com/en-us/library/system.argumentoutofrangeexception(v=vs.110).aspx
try
{
string value = data.Substring(data.IndexOf(index) + index.Length);
}
catch (ArgumentOutOfRangeException e)
{
Console.WriteLine(e.Message);
}
Where I used the .Split to divide the string at each semicolon was the issue. The string I was splitting had a space after the last semicolon which was creating an empty index from that. I used
yearGroups = yearGroups.Take(yearGroups.Count() - 1).ToArray();
to remove the last index that was empty. The trycatch method helped me find this, thanks.
the ArgumentOutOfRangeException happen when the value of an argument is outside the allowable range of values as defined by the invoked method
try this:
string value = data.Substring(data.IndexOf(index) + index.Length - 1 );

Storing DataGrid in List

With my program I'm trying to automatize another program of which there can be multiple instances. I've already written functionality that will watch the processlist and detect all processes of the program that I want to automatize.
It will store some basic informations about found instances into this ConcurrentDictionary which has its ProcessId as key and the class ProgramToWatch as value:
public static ConcurrentDictionary<int, ProgramToWatch> ProgramToWatchDictionary = new ConcurrentDictionary<int, ProgramToWatch>();
public class ProgramToWatch
{
public string ListItemName { get; set; }
public Process BEProcess { get; set; }
public string BEMainWindowTitle { get; set; }
public Application BEApplication { get; set; }
public Window BEWindow { get; set; }
public bool isLoggedIn { get; set; }
public List<ProgramToWatchDataGrid> BEDataGrid = new List<ProgramToWatchDataGrid>();
}
Now the part I am having problems with. The program I want to watch has a DataGridView which I want to copy into my dictionary. For this I have the BEDataGrid List. The list is using this class as its type:
public class ProgramToWatchDataGrid
{
public int ListID { get; set; }
public string Name { get; set; }
public string Mail { get; set; }
}
Now to store it, I create another instance of the ProgramToWatchDataGrid (called updateGrid), write all the data I read into it, and place this into my Dictionary (I simplified). To do this, I iterate through the DataGrid (first while loop), row for row and copy the updateGrid to my Dictionary - in the second while loop I display the values to verify:
public void ReadDataGridView(int ProcessId)
{
ProgramToWatchDataGrid updateGrid = new ProgramToWatchDataGrid();
//read and store every row
int i=0;
while(i<=totalRowsInGrid)
{
updateGrid.ListId = DataGrid.Rows[i].Cells[0].Value;
updateGrid.Name = DataGrid.Rows[i].Cells[1].Value;
updateGrid.Mail = DataGrid.Rows[i].Cells[2].Value;
ProgramToWatchDictionary[ProcessID].BEDataGrid.Insert(i, updateGrid);
Display(ProgramToWatchDictionary[ProcessID].BEDataGrid[i].Mail);
}
Display("Elements: " + ProgramToWatchDictionary[ProcessID].BeDataGrid.Count);
//display every rows mail
i=0;
while(i<=totalRowsInGrid)
{
Display("HI: " + ProgramToWatchDictionary[ProcessID].BEDataGrid[i].Mail);
i++;
}
}
The way I understand it, the Information rows I read should now be located in ProgramToWatchDictionary[ProcessID].BEDataGrid[accordingRow] because I inserted the Information at that place.
The strange thing is, that this output will be produced:
[01:19] christoferlindstrm#yahoo.com
[01:19] eliseisaksson#yahoo.com
[01:19] peter#pan.com
[01:19] Elements: 3
[01:19] HI: peter#pan.com
[01:19] HI: peter#pan.com
[01:19] HI: peter#pan.com
So right after I've inserted it, the List contains the right value. But after it will always be the last value that was read? Why does this happen and how can I fix this?
Some help would be greatly appreciated!
Got it.
You should create the instance of the object to be added in the while, otherwise you are using only one instance which happens to get different values in your cycle. And of course it will end up with the last value you have inserted.
public void ReadDataGridView(int ProcessId)
{
ProgramToWatchDataGrid updateGrid = null;
//read and store every row
int i=0;
while(i<=totalRowsInGrid)
{
//create a new instance
updateGrid = new ProgramToWatchDataGrid();
updateGrid.ListId = DataGrid.Rows[i].Cells[0].Value;
updateGrid.Name = DataGrid.Rows[i].Cells[1].Value;
updateGrid.Mail = DataGrid.Rows[i].Cells[2].Value;
ProgramToWatchDictionary[ProcessID].BEDataGrid.Insert(i, updateGrid);
Display(ProgramToWatchDictionary[ProcessID].BEDataGrid[i].Mail);
}
Display("Elements: " + ProgramToWatchDictionary[ProcessID].BeDataGrid.Count);
//display every rows mail
i=0;
while(i<=totalRowsInGrid)
{
Display("HI: " + ProgramToWatchDictionary[ProcessID].BEDataGrid[i].Mail);
i++;
}
}

Categories