I am trying to loop over an object properties and values and build a string with them.
The problem is i cant seem to access the values of the properties which are not string...
Here is what i have so far:
private string ObjectToStringArray(CustomType initParameters)
{
var stringArray = "";
foreach (var parameter in initParameters.GetType().GetProperties())
{
if (parameter.PropertyType.Name == "String")
stringArray += "\"" + parameter.Name + "\" => \"" + parameter.GetValue(initParameters) + "\",\r\n";
else
{
stringArray += "array(\r\n";
foreach (var subParameter in parameter.PropertyType.GetProperties())
{
stringArray += "\"" + subParameter.Name + "\" => \"" + subParameter.GetValue(parameter) + "\",\r\n";
}
stringArray += "),";
}
}
return stringArray;
}
i can get to the values of all the string properties but one level down i just cant extract the property object itself.
My exception is: System.Reflection.TargetException: Object does not match target type.
When calling subParameter.GetValue(parameter), you are passing a PropertyInfo, whereas you seemingly want to pass the value of that property for initParameters instead.
You should thus pass parameter.GetValue(initParameters) to subParameter.GetValue() instead.
Related
I have the following method that I use to traverse collections like IQueryCollection, IHeadersDictionary and IFormCollection
public static string ToString(IEnumerable<KeyValuePair<string, StringValues>> dic)
{
string res = "";
foreach (var item in dic)
{
res += item.Key + "=" + item.Value + "&";
}
return res.TrimEnd('&');
}
I was wondering if it would be possible to do it in a more " functional" style, using something like Select and String.Join method
I'm using dot-net-core 2.0
Yes you can write it using a select, it's actually quite simple:
public static string ToString(IEnumerable<KeyValuePair<string, StringValues>> dic)
{
return string.Join("&", dic.Select(_ => $"{_.Key}={_.Value}"));
}
Will it perform better, hard to say without testing, your version uses string concatenations which are probably not a good idea for a large number of string (StringBuilder) would be better. But I expect string.Join to do a better job than your version.
You have a few options:
Using Aggregate
dic.Aggregate((res, item) => res + item.Key + "=" + item.Value + "&")
.TrimEnd('&')
Using Select and string.Join
string.Join("&", dic.Select(item => item.Key + "=" + item.Value))
Using Zip and string.Join if you are using a Dictionary instance instead of a list of KeyValuePairs:
string.Join("&", dict.Keys.Zip(dict.Values, (key, value) => key + "=" + value))
You can use
var result = from keyvaluepair in dictionary
from value in keyvaluepair .Value
select keyvaluepair .Key + "=" + value;
or try aggregate,
string result = values.Aggregate("",
(key, pair) =>
key + "=" + pair.Key + ":"
+ pair.Value.Aggregate("",
(str, val) => str + "=" + val)
);
There is already a post related to same in How do I combine the keys and values of a Dictionary into one List using LINQ?
I am trying to build a method which is able to write in a file the number of files in another folder. I already got it but it does not work because gives me an error:
Could not find a part of the path '
C:\Users\Administrator\Desktop...\bin\release\%USERPROFILE%\AppData\Local_logC'
This is my code:
private static int GetCountGFiles(string dirname)
{
int fileCount = 0;
dirname = dirname.TrimEnd(new char[] { '\\' }) + #"\";
var fixedDirname = Environment.ExpandEnvironmentVariables(dirname);
fileCount += System.IO.Directory.GetFiles(fixedDirname).Length;
string[] all_subdirs = System.IO.Directory.GetDirectories(dirname);
foreach (string dir in all_subdirs)
fileCount += GetCountGFiles(dirname);
string data = string.Format("LogReg.txt");
System.IO.Directory.CreateDirectory(filePath);
var fileandpath = filePath + data;
using (StreamWriter writer = File.AppendText(fileandpath))
{
writer.WriteLine("[" + DateTime.Now + "]" + " - " + "NÂș de Records: (" + fileCount.ToString() + ")");
}
return fileCount;
}
And then I call the method like this :
GetCountGFiles(#"%USERPROFILE%\AppData\Local\Cargas - Amostras\_logsC\");
What should I do?
Methods like Directory.GetFiles and Directory.GetDirectories will not automatically figure out what the environment variables that you pass to it are. You need to do that manually with Environment.ExpandEnvironmentVariables. For example:
var fixedDirname = Environment.ExpandEnvironmentVariables(dirname);
Now you can do this:
fileCount += System.IO.Directory.GetFiles(fixedDirname).Length;
(PS No need to use ToString() on a string)
That "%USERPROFILE%" part is a placeholder for an environment variable. The file APIs have no idea how to handle that.
You need to use Environment.ExpandEnvironmentVariables on your input string,
before passing it to Directory.GetFiles
My code searches through a list and then if it finds a match, it displays the object in my listbox. My problem is that if there is more than 1 object in the list (if im searching for Alex and there is two objects with the name Alex), it returns it all on the same line instead of separating them to separate lines.
I coulda swore match += request + "\n"; was how to do it correctly, but it's not working.
Edit: One thing I dont understand is that if i just have match += request; it will allow me to use the horizontal scroll bar on my listbox to see everything written. And if i use match += request + "\n"; or match += request + Environment.NewLine; it doesn't let me use the scroll box and just cuts off.
public string SearchRequest(string keyword)
{
bool found = false;
string noMatch = "No requests with the name " + "'" + keyword + "'" + " were found";
string match = "";
foreach (ServiceRequest request in Requests)
{
if (request.searchKeywords(keyword))
{
match += request + "\n";
found = true;
}
}
if (found)
return match;
else
return noMatch;
}
/
public bool searchKeywords(string keyword)
{
if (keyword == name)
return true;
else
return false;
}
/
private void btnSearch_Click(object sender, EventArgs e)
{
lstSearch.Items.Clear();
lstSearch.Items.Add(myRequest.SearchRequest(txtSearch.Text));
}
Try
match += request + Environment.NewLine;
If you put all the results in a single string then it will still be a single item in the list.
Return an array of strings from the method instead of a single string:
public string[] SearchRequest(string keyword) {
List<string> match = new List<string>();
foreach (ServiceRequest request in Requests) {
if (request.searchKeywords(keyword)) {
match.Add(request.ToString());
}
}
if (match.Count > 0) {
return match.ToArray();
} else {
return new string[] { "No requests with the name " + "'" + keyword + "'" + " were found" };
}
}
Then use AddRange to add the strings as separate items in the list:
lstSearch.Items.AddRange(myRequest.SearchRequest(txtSearch.Text));
In Windows OS, the new line is two characters, the Carriage Return \r followed by Line Feed: \n. You can use Environment.NewLine as a shortcut (preferred) or append "\r\n" yourself. See further wikipedia entry on newline
Use one of these:
match += request + "\r\n";
Use an string literal:
match += request + #"
";
OR only at runtime will this resolve:
match += request + System.Environment.NewLine;
On Unix "\n"
You can't add a string with newlines to a listbox and expect it to show up as multiple items. Either split the string on newline and add each line separately to the listbox, or return a list of strings from your search function to begin with, avoiding the need for a split afterwards.
I am currently working on a program that takes JSON input and deserializes it using JSON.NET into a dynamic ExpandoObject. Then through a function, I want to dump it out into another textbox to show what it could possibly represent as C# objects. How would I indent my string to show my hierarchical ExpandoObject and take it from a flat list of data to a tree structure of data I can send to a textbox as a string?
Here is some of the code I am using:
if (tbxJSON.Text != "")
{
// Create an ExpandoObjectConverter in order to hold the dynamic parsed JSON.
var converter = new ExpandoObjectConverter();
dynamic convertedJSON = JsonConvert.DeserializeObject<ExpandoObject>(tbxJSON.Text, converter);
tbxCSharp.Text = "";
// Loop through the ExpandoObject and print out all of the values in the dynamic object using a recursive function.
foreach (var property in (IDictionary<string, object>)convertedJSON)
sb.Append(ExpandoToString(property.Value, property.Key));
// Set C# output to contents of StringBuilder.
tbxCSharp.Text = sb.ToString();
}
private string ExpandoToString(object propVal, string propName)
{
string retStr = "";
// If we are dealing with nested JSON
if (propVal.GetType() == typeof(ExpandoObject))
{
// Append this object type.
sb.Append(Indent(indentIdx) + UppercaseFirst(propName) + " " + propName + " consists of..." + Environment.NewLine);
foreach (var prop in (IDictionary<string, object>)propVal)
{
if (prop.Value.GetType() == typeof(ExpandoObject))
sb.Append(ExpandoToString(prop.Value, prop.Key));
else
{
if (prop.Value.GetType() == typeof(List<dynamic>))
{
// TO DO
}
else
sb.Append(ExpandoToString(prop.Value, prop.Key));
}
}
}
else
retStr = propVal.GetType() + " : " + propName + " = " + propVal + Environment.NewLine;
return retStr;
}
Test JSON:
{"airline":"Oceanic","number":815,"departure":{"IATA":"SYD","time":"2004-09-22 14:55","city":"Sydney"},"arrival":{"IATA":"LAX","time":"2004-09-23 10:42","city":"Los Angeles"}}
You should supply your current indent as a parameter to the ExpandoToString() method and increment it any time it calls itself.
private string ExpandoToString(object propVal, string propName, int indent = 0)
{
...
// Append this object type.
sb.Append(Indent(indent) + UppercaseFirst(propName) + " " + propName + " consists of..." + Environment.NewLine);
...
sb.Append(ExpandoToString(propVal, propName, indent + 1) ...);
I have the following function:
private string ParseJson(dynamic q)
{
string returnJSON = "[{ \"type\" : \"pie\", \"name\" : \"Campaigns\", \"data\" : [ ";
foreach (var grp in q)
{
double currCount = grp.Count();
if (grp.Key != null)
returnJSON += "['" + grp.Key + "', " + currCount + "],";
else
returnJSON += "['none', " + currCount + "],";
}
returnJSON = returnJSON.Substring(0, returnJSON.Length - 1);
returnJSON += "]}]";
return returnJSON;
}
I call it from methods like this one:
public string GetCampaignData()
{
PaymentModelDataContext db = new PaymentModelDataContext();
var q = from Event in db.TrackingEvents
group Event by Event.campaignID;
return ParseJson(q);
}
I use q for several different queries, all grouping data.
The problem is that the runtime can't bind a type to q for some reason. Is this a proper use of dynamic? Is there a different way to do this?
The problem is Count() is an extension method off of IEnumerable<T>, as such it can't be called from dynamic (because it's not a true method of the class).
Your variable grp is also dynamic because it results from an expression on dynamic variable q:
foreach (var grp in q)
Since we can't call extension methods off of dynamic (again, they aren't true members of the class), we need to explicitly call the extension method instead off the Enumerable static class. So change your code to:
double currCount = Enumerable.Count(grp);
And you'll see it work properly for the Count(), if you want to really use dynamic.
That said, I do agree with #John's comment that you should consider changing this to a non-dynamic. Actually, what your method would accept would be an IEnumerable> like so:
private string ParseJson<TKey,TValue>(IEnumerable<IGrouping<TKey,TValue>> q)
{
string returnJSON = "[{ \"type\" : \"pie\", \"name\" : \"Campaigns\", \"data\" : [ ";
foreach (var grp in q)
{
double currCount = grp.Count();
if (grp.Key != null)
returnJSON += "['" + grp.Key + "', " + currCount + "],";
else
returnJSON += "['none', " + currCount + "],";
}
returnJSON = returnJSON.Substring(0, returnJSON.Length - 1);
returnJSON += "]}]";
return returnJSON;
}
You can also make the parameter type non-generic specific to your usage if you like. But this would work with all groupings...