infinite loop stackoverflow list of objects exception - c#

i have this code:
public List<CsvUserData> CsvUserList = new List<CsvUserData>();
public CsvUserData()
{
readCSV(#"C:\userdata.csv");
}
public string CSVEmailEditText { get; set; }
public string CSVNameEditText { get; set; }
public string CSVAddressEditText { get; set; }
public string CSVPostnumEditText { get; set; }
public string CSVCityEditText { get; set; }
public string CSVPhoneEditText { get; set; }
public string CSVCommentEditText { get; set; }
public string SelectPage { get; set; }
private void readCSV(string location)
{
var reader = new StreamReader(File.OpenRead(location));
string line;
string[] values;
while (!reader.EndOfStream)
{
line = reader.ReadLine();
values = line.Split(',');
CsvUserList.Add
(
new CsvUserData
{
CSVEmailEditText = values[0],
CSVNameEditText = values[1],
CSVAddressEditText = values[2],
CSVPostnumEditText = values[3],
CSVCityEditText = values[4],
CSVPhoneEditText = values[5],
}
);
}
}
I am trying to read csv file into list that consists of objects named CsvUserData, the class definition is displayed above. Once the class is instantiated my program is getting into infinite loop eventually resulting in stackoverflow exception once the list memory is full, even though my csv file only has one row of data. Can someone help me and explain why is this happening?

Let’s see:
Create a new CsvUserData object, call the constructor.
readCSV(#"C:\userdata.csv");
Inside readCSV: Open file, and iterate over the lines.
For each line: new CsvUserData { … }
Go to 1.
So you end up creating new CsvUserData objects from within the constructor of the CsvUserData type. So this will repeat forever.
You probably meant to make the readCSV method static or something, and only call it once. There is really no reason why it should be called from the constructor. And the constructor shouldn’t really open a file and create stuff based on the file; that’s far too much work for a constructor.

Related

C# Type safe JSON-Lines Deserialization

Currently I am working with the Shopify GraphQL Bulk Query.
This Query returns a JSON Lines file. Such a file may look like this:
{"id":"gid:\/\/shopify\/Product\/5860091625632","title":"Levis Jeans","description":"Cool Jeans","vendor":"Levis","status":"ACTIVE"}
{"id":"gid:\/\/shopify\/ProductImage\/20289865679008","__parentId":"gid:\/\/shopify\/Product\/5860091625632"}
{"id":"gid:\/\/shopify\/ProductVariant\/37178118963360","title":"32","position":1,"image":null,"selectedOptions":[{"name":"Size","value":"32"}],"inventoryItem":{},"__parentId":"gid:\/\/shopify\/Product\/5860091625632"}
{"available":10,"location":{"id":"gid:\/\/shopify\/Location\/57510625440"},"__parentId":"gid:\/\/shopify\/ProductVariant\/37178118963360"}
{"id":"gid:\/\/shopify\/ProductVariant\/37178118996128","title":"31","position":2,"image":null,"selectedOptions":[{"name":"Size","value":"31"}],"inventoryItem":{},"__parentId":"gid:\/\/shopify\/Product\/5860091625632"}
{"available":5,"location":{"id":"gid:\/\/shopify\/Location\/57510625440"},"__parentId":"gid:\/\/shopify\/ProductVariant\/37178118996128"}
{"available":3,"location":{"id":"gid:\/\/shopify\/Location\/57951518880"},"__parentId":"gid:\/\/shopify\/ProductVariant\/37178118996128"}
{"id":"gid:\/\/shopify\/ProductVariant\/37178119028896","title":"34","position":3,"image":null,"selectedOptions":[{"name":"Size","value":"34"}],"inventoryItem":{},"__parentId":"gid:\/\/shopify\/Product\/5860091625632"}
{"available":5,"location":{"id":"gid:\/\/shopify\/Location\/57510625440"},"__parentId":"gid:\/\/shopify\/ProductVariant\/37178119028896"}
{"available":15,"location":{"id":"gid:\/\/shopify\/Location\/57951518880"},"__parentId":"gid:\/\/shopify\/ProductVariant\/37178119028896"}
Each line of this file is a valid JSON-object and the lines are connected via __parentId with each other.
My Goal is to Deserialize this into C# Classes like this:
class Product
{
public string Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public IEnumerable<ProductImage> Images { get; set; }
public IEnumerable<ProductVariant> Variants { get; set; }
}
class ProductImage
{
public string Id { get; set; }
}
class ProductVariant
{
public string Id { get; set; }
public IEnumerable<IDictionary<string, string>> SelectedOptions { get; set; }
public IEnumerable<InventoryLevel> Levels { get; set; }
}
class InventoryLevel
{
public int Available { get; set; }
}
And the output of a potential function performing the deserialization:
var file = new System.IO.StreamReader(#"c:\test.jsonl");
var products = DeserializeJsonL<IEnumerable<Product>>(file);
Shopify suggests to read the file in reverse. I get the Idea.
But I cannot imagine how to deserialize this file in a type safe way. How could I determine if the current line is a ProductVariant, a ProductImage or something else? I cannot influence the JSONL Output to include type information.
I am pretty sure without type information I cannot deserialize it safely. But how should I handle this data then to insert into a database for example?
EDIT the classname in {"id":"gid:\/\/shopify\/Product\/5860091625632"} cannot be used to determine the Type!
I ended up adding some sort of type information to my graphql-query by defining a unique fieldname for each type which may be on a new line in the resulting JSON Lines file.
For that i used GraphQL field aliases:
someQuery {
uniqueFieldAlias : fieldName
}
When i read the file i search on each line for the unique fieldname. Then i deserialize the line into the corresponding class.
using (var file = new StreamReader(await res.Content.ReadAsStreamAsync()))
{
string line;
while ((line = await file.ReadLineAsync()) != null)
{
if (line.Contains("\"uniqueFieldAlias\""))
{
var product = JsonSerializer.Deserialize<Product>(line);
products.Add(product);
continue;
}
if (line.Contains("\"otherUniqueAlias\""))
{
var somethingElse = JsonSerializer.Deserialize<SomeClass>(line);
products[productIndex].Something.Add(somethingElse);
continue;
}
}
}
The idea is inspired by #Caius Jard comments

JSON (deserialized) sends null values to list

Firstly thank you for taking the time to look at this. It's quite alot.
Question:
I'm basically trying to download a json as a string and then deserialize it to a list. The reason why is so i can then call a specific property of that list (in my case 'ips' because it's all i actually need) and insert it into a table if requirements are met.
The problem is that it moves all null values into the array. 114 columns of null, or empty array and i can't figure out why?
I think i'll attach a link to the JSON because its a massive file its here https://endpoints.office.com/endpoints/Worldwide?clientRequestId=b10c5ed1-bad1-445f-b386-b919946339a7
Here is my code:
Getters and setters for JSON
public class GetSetJsonIP {
[JsonProperty("id")]
public int id { get; set; }
[JsonProperty("serviceArea")]
public string ServiceArea { get; set; }
[JsonProperty("serviceAreaDisplayName")]
public string ServiceAreaDisplayName { get; set; }
[JsonProperty("urls")]
public IList<string> urls { get; set; }
[JsonProperty("ips")]
public IList<string> ips { get; set; }
[JsonProperty("tcpPorts")]
public string tcpPorts { get; set; }
[JsonProperty("expressRoute")]
public bool expressRoute { get; set; }
[JsonProperty("category")]
public string category { get; set; }
[JsonProperty("required")]
public bool required { get; set; }
[JsonProperty("notes")]
public string notes { get; set; }
[JsonProperty("udpPorts")]
public string udpPorts { get; set; }
}
List class
public class ConvertJsonIP{
public List<GetSetJsonIP> jsonIpConvert { get; set; }
public List<GetSetJsonIP> jsonIPConvert = new List<GetSetJsonIP>();
}
3.I download the JSON using an empty string called o365IP
o365IP = wc.DownloadString(wc.BaseAddress + "/endpoints/Worldwide?clientRequestId=b10c5ed1-bad1-445f-b386-b919946339a7");
I deserialize using my List to a seperate var
var o365IpVerion = JsonConvert.DeserializeObject<List<ConvertJsonIP>>(o365IP);
This code shows no errors. so i can only assume its a logical one on my part. It should be noted that i had to put the <List< in to stop an error stating that it couldnt convert an object to an array.
Seriously, i've been stuck on this for 3 days so any help on this would be greatly appreciated! Thanks in advance!
the json you have is a list of objects and each of these objects conform to GetSetJsonIp. You should deserialize using List<GetSetJsonIP>
var o365IpVerion = JsonConvert.DeserializeObject<List<GetSetJsonIP>>(o365IP);
public class GetJsonIP works fine.
The reason you must Deserialize into a List<> is because the json object starts with a bracket making the entire object a List or array.
var O365IpVersion = JsonConvert.DeserializeObject<List<GetJsonIP>(O365IP);
There are different ways to fetch the value of a certain property. If you just need ips and want to check the value then update it, then you could loop:
JArray arr = JArray.Parse(O365IP);
foreach (JObject obj in arr.Children<JObject>())
{
foreach (JPRoperty prop in obj.Properties().Where(x => x.Name == "ips"))
{
//use prop.Value and perform tasks
}
}
Or just simply loop like this:
for (int i = 0; i < O365IpVersion.Count; i++)
{
//use O365IpVersion.ElementAt(i).ips

Removing header from a formatted string

I have a formatted log file I'm trying to parse; the file is divided in sections with an header and the data inside each section is formatted with JSON like follows. Link to an extract of the log file here
[UnityCrossThreadLogger]1/8/2019 7:49:19 PM
==> Deck.GetDeckLists(112):
{
"jsonrpc": "2.0",
"method": "Deck.GetDeckLists",
"params": {},
"id": "112"
}
My issue here is manipulating the whole string in a way I get to the section I want and there strip the meaningless data and parse the remaining through Newtonsoft JSON. For now I'm cutting everything I don't need using this function, since the log file is in chronological order and only the latest occurrence of the entry is needed:
//Cut the whole log to the last entry
private static string CutLog(string fromWhereToCut)
{
string log = GetLog();
//In this case fromWhereToCut would be "Deck.GetDeckLists"
string s = log.Substring(log.LastIndexOf(fromWhereToCut));
return s;
}
The problem is the fact it leaves the header in place I need to remove before deserializing the JSON and it's prone to breaking because the name of the sections aren't that unique and they could be repeated further down as non-header titles (as can be seen in my example). Furthermore I don't know how to stop at the end of the section I need before another one begins.
I thought RegEx could be used but this seems way to big even for a RegEx and maybe there's a better solution.
If the Log is the same as the one found in PasteBin, this deserializes fine.
I'm using a support class (JSON_Logs) to contain the extracted data.
The JSON is read from a file in this simulation.
Reading the structure of the data, the most probable candidate to identify the start of the actual data, is the recurring string "Deck.GetDeckLists". In the parsing method it's assigned to a variable called excludedSection.
The data starts right after the last one of those string. I'm using logFile.LastIndexOf(excludedSection) to find the index of the last of these entries, then use this index to identify the first data structure.
JsonConvert.DeserializeObject is then used to deserialize the data into a List of class objects.
I didn't find any problem during the deserialization process.
string searchString = "Deck.GetDeckLists";
List<JSON_Logs.Header> jsonLogs = ParseJsonLog(searchString, "JSON_Logs.txt");
private List<JSON_Logs.Header> ParseJsonLog(string excludedSection, string fileName)
{
string logFile = File.ReadAllText(fileName);
int refIndex = logFile.LastIndexOf(excludedSection);
logFile = logFile.Substring(logFile.IndexOf("[", refIndex));
return JsonConvert.DeserializeObject<List<JSON_Logs.Header>>(logFile);
}
Support class:
public class JSON_Logs
{
public class Header
{
public string id { get; set; }
public string name { get; set; }
public string description { get; set; }
public string format { get; set; }
public string resourceId { get; set; }
public int deckTileId { get; set; }
public MainDeck[] mainDeck { get; set; }
public object[] sideboard { get; set; }
public DateTime lastUpdated { get; set; }
public bool lockedForUse { get; set; }
public bool lockedForEdit { get; set; }
public bool isValid { get; set; }
}
public class MainDeck
{
public string id { get; set; }
public int quantity { get; set; }
}
}
I hope this is what you need. :) Actually, regex finds json in all sections, but I included getting only last section (matches[matches.Count - 1]). Since JToken doesn't have TryParse method, you have to use try/catch:
static void ParseLog()
{
var s = File.ReadAllText(#"C:\log.json");
var pattern =
#"(?s)(?'header'\[\w+\]\d{1,2}/\d{1,2}/\d{4}\s\d{1,2}:\d{1,2}:\d{1,2}\s(A|P)M\r\n" +
#"<?==>?.+?\r\n)" +
#"(?'body'.+?)(?=$|\[\w+\]\d{1,2}/\d{1,2}/\d{4}\s\d{1,2}:\d{1,2}:\d{1,2}\s(A|P)M)";
var matches = Regex.Matches(s, pattern);
if (matches.Count > 0)
{
JToken last_json = null;
try
{
var text = matches[matches.Count - 1].Groups["body"].Value;
last_json = JToken.Parse(text);
WriteLine(last_json.ToString());
}
catch (Exception ex) { WriteLine(ex.ToString()); }
}
else
{
WriteLine("No matches found");
}
}

Multilevel inheritance partial class [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I need to access referenced classes inside a main class and insert values into the objects. The classes are of partial type.
my code:
public partial class Get_CountryInfo_Resp_object
{
public string ReturnCode { get; set; }
public string ErrorMsg { get; set; }
public string Alpha2_Code { get; set; }
public string Digit3_Code { get; set; }
public string CountryName { get; set; }
public string IBAN_Mandatory { get; set; }
public As_SenderCountry[] As_SenderCountry { get; set; }
public As_ReceiverCountry[] As_ReceiverCountry { get; set; }
}
public partial class As_SenderCountry
{
public string SenderCountry_IsSensitive { get; set; }
}
public partial class As_ReceiverCountry
{
public string ReceiverCtry_EFTNotAllowed { get; set; }
public ReceiverCtry_AllowedCCY_Item[] ReceiverCtry_AllowedCCY_List { get; set; }
}
public partial class ReceiverCtry_AllowedCCY_Item
{
public string ReceiverCtry_AllowedCCY { get; set; }
}
private static void Task2()
{
String xmlText = File.ReadAllText(#"../../XML/sample1.xml");
DataSet ds = new DataSet();
ds.ReadXml(new XmlTextReader(new StringReader(xmlText)));
DataTable dt = ds.Tables["column"];
Get_CountryInfo_Resp_object Get_CountryInfo_Resp = new Get_CountryInfo_Resp_object();
//Get_CountryInfo_Resp.As_SenderCountry;
Get_CountryInfo_Resp.ReturnCode = dt.Rows[0]["column_Text"].ToString();
Get_CountryInfo_Resp.ErrorMsg = dt.Rows[1]["column_Text"].ToString();
Get_CountryInfo_Resp.Alpha2_Code = dt.Rows[2]["column_Text"].ToString();
Get_CountryInfo_Resp.Digit3_Code = dt.Rows[3]["column_Text"].ToString();
Get_CountryInfo_Resp.CountryName = dt.Rows[4]["column_Text"].ToString();
Get_CountryInfo_Resp.IBAN_Mandatory = dt.Rows[5]["column_Text"].ToString();
//GetCountryInfo_Resp.As_SenderCountry.SenderCountry_IsSensitive
I need to Insert dt.Rows[6]["column_Text"].ToString(); into the GetCountryInfo_Resp.As_SenderCountry.SenderCountry_IsSensitive .
How shall i proceed?
Please help.
Since As_SenderCountry is an array, it can contain multiple items. You have to assign an array too, not just a single instance.
I would start to create an object, add that to a list and eventually create an array out of it (or change the type to be a list instead of an array). You can also fix-size the array if you know the length already.
As_SenderCountry asc = new As_SenderCountry();
asc.SenderCountry_IsSensitive = dt.Rows[6]["column_Text"].ToString();
And then:
GetCountryInfo_Resp.As_SenderCountry = new As_SenderCountry[] { asc };
Or create the list, loop over items and eventually assign it:
List<As_SenderCountry> list = new List<As_SenderCountry>();
// some sort of loop
As_SenderCountry asc = new As_SenderCountry();
...
list.Add(asc);
// end loop
GetCountryInfo_Resp.As_SenderCountry = list.ToArray();
I don't think I fully understand your code, but As_SenderCountry and As_ReceiverCountry in your public partial class Get_CountryInfo_Resp_object are arrays, if I am not reading it wrong.
Therefore, the messy solution, if you know there is only one sender country:
GetCountryInfo_Resp.As_SenderCountry[0].SenderCountry_IsSensitive = dt.Rows[6]["column_Text"].ToString();
Alternatively, you can use Lists - the advantage with lists being, you don't need to know the array size when instantiating. An example with your variables:
public partial class Get_CountryInfo_Resp_object
{
public string ReturnCode { get; set; }
...
public List<As_SenderCountry> As_SenderCountry { get; set; }
public List<As_ReceiverCountry> As_ReceiverCountry { get; set; }
}
private static void Task2()
{
String xmlText = File.ReadAllText(#"../../XML/sample1.xml");
DataSet ds = new DataSet();
ds.ReadXml(new XmlTextReader(new StringReader(xmlText)));
DataTable dt = ds.Tables["column"];
Get_CountryInfo_Resp_object Get_CountryInfo_Resp = new Get_CountryInfo_Resp_object();
...
GetCountryInfo_Resp.As_SenderCountry.SenderCountry_IsSensitive.Add(dt.Rows[6]["column_Text"].ToString());
p.s. Your variable and class naming is very messy. I would suggest you to clean that up so that you, as well as the people reading the question can understand it better.
You need to create a new instance of the inner class as you would do normally for any other class, and then assign whatever value to the field you need.
First you need to assign the length of the array, then each cell of the array will contain an instance of an object of type (As_SenderCountry ), then you should assign each object the value you need.
GetCountryInfo_Resp.As_SenderCountry = new GetCountryInfo_Resp.As_SenderCountry();
GetCountryInfo_Resp.As_SenderCountry[INDEX_HERE].SenderCountry_IsSensitive = dt.Rows[6]["column_Text"].ToString();

Creating a Parse class to handle parsing of file being read in

I wrote a parse class trying to handle parsing the data from a string array into it's appropriate value. I am trying to test this program to see if it will print out the value parse.open, and it is not. It is printing up 0's for the moment (which isn't accurate), until i could figure out why it's not showing what I need.
while (!r.EndOfStream)
{
ParseFileRead parse = new ParseFileRead();
string line = r.ReadLine();
//Send this to Parse class
string [] values = line.Split(',');
//parse records
Console.WriteLine(values[6]); //This is printing the accurate value for parse.open
ParseFileRead.Parse(values);
Console.WriteLine(parse.open); //This is not printing the accurate value
}
Console.Read();
vWriteFile.Close();
And here is my ParseFileRead class:
public class ParseFileRead
{
public int open { get; set; }
public int buy { get; set; }
public int sell { get; set; }
public double settleMM { get; set; }
public string account { get; set; }
public string underlying { get; set; }
public string symbol { get; set; }
public static void Parse(string[] arr)
{
ParseFileRead parse = new ParseFileRead();
parse.account = arr[0];
parse.underlying = arr[12];
parse.symbol = arr[1];
parse.open = Convert.ToInt32(arr[6]);
parse.buy = Convert.ToInt32(arr[7]);
parse.sell = Convert.ToInt32(arr[8]);
parse.settleMM = Convert.ToDouble(arr[10]);
}
}
This is actually correct.
The default value for an uninitialized int is 0.
You are creating a new instance of your ParseFileRead class which will have a value of 0 for open. You then check your parsed value to make sure it's reading in correctly using Console.WriteLine(values[6]);.
Next, you try to parse your values using the Parse function of your ParseFileRead class; which is a void function so it has no return value.
Inside your Parse function you have: ParseFileRead parse = new ParseFileRead(); which creates yet another new instance of your class with a value of 0 for open. This particular instance is never used anywhere and is not the same as the values of the properties created with your initial instance of ParseFileRead
If you put a Console.Write in your Parse function, I'm sure that you will see it being parsed correctly.
So you have 2 options:
Set the properties of your ParseFileRead inside the Parse class without creating a new instance of ParseFileRead
Return the newly created ParseFileRead instance out of your Parse function.
Or a 3rd Option, which is probably best as suggested by Plutonix:
/*Parse class*/
public class ParseFileRead
{
public int open { get; set; }
public int buy { get; set; }
public int sell { get; set; }
public double settleMM { get; set; }
public string account { get; set; }
public string underlying { get; set; }
public string symbol { get; set; }
public ParseFileRead(string[] arr)
{
this.account = arr[0];
this.underlying = arr[12];
this.symbol = arr[1];
this.open = Convert.ToInt32(arr[6]);
this.buy = Convert.ToInt32(arr[7]);
this.sell = Convert.ToInt32(arr[8]);
this.settleMM = Convert.ToDouble(arr[10]);
}
}
/*Parsing code*/
while (!r.EndOfStream)
{
string line = r.ReadLine();
//Send this to Parse class
string [] values = line.Split(',');
//parse records
Console.WriteLine(values[6]); //This is printing the accurate value for parse.open
ParseFileRead parse = new ParseFileRead(values);
Console.WriteLine(parse.open); //This is not printing the accurate value
}

Categories