I am trying to create a POST endpoint where on success, it will send an HTML email with the POST data. I am struggling trying to iterate over the JSON array of objects and appending that data to a HTML table.
JSON data:
{
"Submitter":[
{
"Obj1":[
"test",
"test2"
]
},
{
"Obj2":[
"test3",
"test4"
]
}
]
}
TestingController:
public class Testing2Controller : ControllerBase
{
public class Submitter
{
public List<string> Obj1 { get; set; }
public List<string> Obj2 { get; set; }
}
public class MyData
{
public List<Submitter> Submitter { get; set; }
}
public string POST([FromBody] MyData data)
{
string composeTableObj1 = #"";
string composeTableObj2 = #"";
foreach (var item in data.Submitter)
{
composeTableObj1 += $"<tr>"; //start row tag
//Column 1 Obj1 data
if (item.Obj1 != null)
{
foreach (var objItem in item.Obj1)
{
composeTableObj1 += $"<td>{objItem}</td>";
}
}
//Column 2 Obj2 data
if (item.Obj2 != null)
{
foreach (var objItem in item.Obj2)
{
composeTableObj1 += $"<td>{objItem}</td>";
}
}
composeTableObj1 += $"</tr>"; //end row tag
}
string body = #$"<table>
<thead>
<tr>
<th>Object 1</th>
<th>Object 2</th>
</tr>
</thead>
<tbody>
{composeTableObj1}
</tbody>
</table>
";
return body;
}
}
The above code gives me the following undesired result:
| Object 1 | Object 2 |
|----------|----------|
| test | test2 |
| test3 | test4 |
This is the desired result I'm after:
| Object 1 | Object 2 |
|----------|----------|
| test | test3 |
| test2 | test4 |
Been stuck on this for quite some time, TIA!
Looks like your HTML is a bit off - writing HTML like this can be tedious. I am guessing based off of the hard coded table headers in the HTML and the structure of the Submitter class that you will only ever have two Objn items in your JSON. If this is true, you can ditch the foreach loop for a for loop, and get the value out of data.Submitter[n] using the loops iterator:
public string Post([FromBody] MyData data)
{
string composeTableObj = "";
for (int i = 0; i < data.Submitter.Count(); i++)
{
composeTableObj += $"<tr>"; //start row tag
composeTableObj += $"<td>{data.Submitter[0].Obj1[i]}</td>";
composeTableObj += $"<td>{data.Submitter[1].Obj2[i]}</td>";
composeTableObj += $"</tr>"; //end row tag
}
return #$"<table><thead><tr><th>Object 1</th><th>Object 2</th></tr></thead><tbody>{composeTableObj}</tbody></table>";
}
Rendered:
Returned HTML:
<table><thead><tr><th>Object 1</th><th>Object 2</th></tr></thead><tbody><tr><td>test</td><td>test3</td></tr><tr><td>test2</td><td>test4</td></tr></tbody></table>
Obviously, this isn't very dynamic. If you find you need more Objn, you'll need to update the Submitter class, and you will have to add to the code within the loop (and add another header for your HTML).
If you have more items in the Obj arrays, you can do the following:
public string Post([FromBody] MyData data)
{
string composeTableObj = "";
int obj1Count = data.Submitter[0].Obj1.Count;
int obj2Count = data.Submitter[1].Obj2.Count;
int loopCount = obj1Count >= obj2Count ? obj1Count : obj2Count;
for (int i = 0; i < loopCount; i++)
{
string obj1String = obj1Count <= i ? " " : data.Submitter[0].Obj1[i];
string obj2String = obj2Count <= i ? " " : data.Submitter[1].Obj2[i];
composeTableObj += $"<tr>"; //start row tag
composeTableObj += $"<td>{obj1String}</td>";
composeTableObj += $"<td>{obj2String}</td>";
composeTableObj += $"</tr>"; //end row tag
}
return #$"<table><thead><tr><th>Object 1</th><th>Object 2</th></tr></thead><tbody>{composeTableObj}</tbody></table>";
}
This first gets the greater of the two .Counts between your Obj lists to set the loops condition, and then performs an index bounds check. If the objnCount is less than or equal to i, set it to a space, otherwise take the value for its respective Obj list. With 6 items in Obj2 and only 2 items in Obj1, the HTML looked like this:
Returned HTML:
<table><thead><tr><th>Object 1</th><th>Object 2</th></tr></thead><tbody><tr><td>test</td><td>test3</td></tr><tr><td>test2</td><td>test4</td></tr><tr><td> </td><td>test5</td></tr><tr><td> </td><td>test6</td></tr><tr><td> </td><td>test7</td></tr><tr><td> </td><td>test8</td></tr></tbody></table>
Related
I am trying to get invisible text that is on a given page using C# Selenium EdgeDriver. I am able to do it, i.e. I get the elements (such as within span, p or b tags, then I am filtering out the elements into a list based on Displayed property, and finally I am calling GetAttribute("textContent") to get the text. The problem I am having is that is slow, about 10 seconds for the page I am doing that on, do you think there is any better way, or making this faster?
Thanks,
public static string GetInvisibleText()
{
Stopwatch s500 = new Stopwatch();
s500.Start();
string returnable = "\r\n";
var elements = driver.FindElements(By.XPath("//b | //span | //p | //a | //h1 | //h2 | //h3 | //h4 | //h5 | //h6 | //div"));
List<string> list = new List<string>();
var displayed_elements = elements.Where(e => !e.Displayed);
foreach(var el in displayed_elements)
{
try
{
string val = el.GetAttribute("textContent");
val = val.Trim();
val = Regex.Replace(val, #"\s+", " ");
list.Add(val);
}
catch (Exception ex)
{
}
}
list = list.Distinct(StringComparer.OrdinalIgnoreCase).ToList();
foreach (string line in list)
{
returnable = returnable + line + "\r\n";
}
s500.Stop();
return returnable;
}
I have a Specflow table that looks like this.
When I Perform POST Operation for "/example/" with body
| answerValue1 | answerValue2 | countryCode | Cash |
| Yes | Yes | AD | 76-100% |
| | | AF | |
The column CountryCode is the only one that can be multiple choices.
What I tried to do was to add the columns to dictionary with a simple tableExtenstions
public class TableExtensions
{
public static Dictionary<string, string> ToDictionary(Table table)
{
var dictionary = new Dictionary<string, string>();
foreach (var row in table.Rows)
{
dictionary.Add(row[0], row[1]);
}
return dictionary;
}
}
and call it from the method.
var dictionary = TableExtensions.ToDictionary(table);
var countryCode = dictionary["countryCode"];
Unfortnally I get error The given key was not present in the dictionary,
since the dictionary only returns two values from the first and the second Key
Ofcourse if I change the keys to row[2], row[3] it gets the right columns.
But I would like to reuse the Table Extension.
Also i tried to increment them, but it only took the first to columns
var i = 0;
foreach (var row in table.Rows)
{
dictionary.Add(row[i], row[i]);
i++;
}
Does anyone has a better solution?
I'm not entirely sure what you want the dictionary to ultimately contain, but as you mention that manually changing the rows it looks for to:
row[2], row[3]
gives the data you want, perhaps this would give you the reusability you're looking for:
public class TableExtensions
{
public static Dictionary<string, string> ToDictionary(Table table, int columnOne, int columnTwo)
{
int i = 0;
var dictionary = new Dictionary<string, string>();
foreach (var row in table.Rows)
{
dictionary.Add(row[columnOne], row[columnTwo]);
}
return dictionary;
}
}
Usage:
var dictionary = TableExtensions.ToDictionary(table, 2, 3);
This produces a dictionary with the following contents:
You could get the country code like this:
foreach (var row in dictionary)
{
var countryCode = row.Key;
var score = row.Value ?? string.empty;
}
Given the simplicity of the country codes I would make them a comma separated list and use a vertical table instead:
When I Perform POST Operation for "/example/"
| Field | Value |
| answerValue1 | ... |
| answerValue2 | ... |
| countryCodes | AD, AF |
| cash | ... |
In your step definition:
var example = table.CreateInstance<ExampleRow>();
// use example.GetCountryCodes();
And the ExampleRow class to parse the table into an object:
public class ExampleRow
{
public string AnswerValue1 { get; set; }
public string AnswerValue2 { get; set; }
private string[] countryCodes;
public string CountryCodes
{
get => string.Join(", ", countryCodes);
set => countryCodes = value.Split(", ");
}
public string[] GetCountryCodes()
{
return countryCodes;
}
}
I want to compare 2 fields. i.e Machine and Pass.
I want to concatenate 'Color' Depending upon the value of Machine and pass.
--------------|------------------|------------
Color1 | Machine 1 | Pass 1
--------------|------------------|------------
Color2 | Machine 2 | Pass 1
--------------|------------------|------------
Color3 | Machine 1 | Pass 1
--------------|------------------|------------
Color4 | Machine 1 | Pass 2
--------------|------------------|------------
Color5 | Machine 2 | Pass 1
--------------|------------------|------------
Color6 | Machine 2 | Pass 2
--------------|------------------|------------
I want results as follows.
Color1/Color3 for Machine1 & Pass1
Color2/Color5 for Machine2 & Pass2
Color4 for Machine1 & Pass2
Color6 for Machine2 & Pass2
I have no clue how to get this results. tried many ways but not getting it the way I want it
First, what you really have here, conceptually, is a single dimension of a complex object that has both a color, machine, and pass value. Storing that in a 2 dimensional array isn't a good idea. A better representation is a List of some custom type, so let's do that conversion first.
First, we'll define a custom type to represent the input data:
public class MachinePass//TODO consider renaming
{
public string Color { get; set; }
public string Machine { get; set; }
public string Pass { get; set; }
}
then we'll parse our input and put it into the appropriate structure:
var list = new List<MachinePass>();
for (int i = 0; i < data.GetLength(0); i++)
{
var next = new MachinePass();
next.Color = data[i, 0];
next.Machine = data[i, 1];
next.Pass = data[i, 2];
list.Add(next);
}
Now that we have this, we can apply our business logic. In this case what you're wanting to do is group these rows by both machine and pass, and then get the colors for those groups. The GroupBy LINQ operator makes this super easy:
var query = list.GroupBy(row => new { row.Machine, row.Pass }
, row => row.Color);
Then we can just print out the results of this query in the defined format:
foreach(var group in query)
Console.WriteLine("{0} for {1} & {2}",
string.Join("/", group),
group.Key.Machine,
group.Key.Pass);
This is your answer,
private void button1_Click(object sender, EventArgs e)
{
var ary = new[]
{
"Color1 | Machine 1 | Pass 1 ",
"Color2 | Machine 2 | Pass 1 ",
"Color3 | Machine 1 | Pass 1 ",
"Color4 | Machine 1 | Pass 2 ",
"Color5 | Machine 2 | Pass 1 ",
"Color6 | Machine 2 | Pass 2 "
};
var seprated = from x in ary.Select(x => x.Split('|'))
select new
{
key = x[1].Trim() + "&" + x[2].Trim(),
value = x[0]
};
var sb = new StringBuilder();
foreach (var key in seprated.Select(x => x.key).Distinct())
{
var colors = seprated.Where(x => x.key == key).Select(x => x.value.Trim()).ToArray();
sb.AppendLine(string.Format("{0} for {1}", string.Join("/", colors), key));
}
textBox1.Text = sb.ToString();
}
sb.toString() has result:
Color1/Color3 for Machine 1&Pass 1
Color2/Color5 for Machine 2&Pass 1
Color4 for Machine 1&Pass 2
Color6 for Machine 2&Pass 2
You did not provide a class definition so I am going to assume it is the following
class PaintMachineInfo
{
public string ColorName {get; set;}
public string MachineName {get; set;}
public string Pass {get; set;}
}
You can easily get the results you want by using .ToLookup(, what it will allow you to do is to provide a key and it will give you a IEnumerable of results that match that key.
I like to use a custom class for the lookup key instead of a Tuple because it makes it much more obvious what you are looking at and it is not too much code to create the class.
private static void Main(string[] args)
{
List<PaintMachineInfo> info = GenerateInfo();
var filteredResults = info.ToLookup(line => new PaintMachineLookup(line.MachineName, line.Pass), line => line.ColorName);
//Contains a IEnumerable<string> containing the elements "Color1" and "Color3"
var result1 = filteredResults[new PaintMachineLookup("Machine 1", "Pass 1")];
}
private static List<PaintMachineInfo> GenerateInfo()
{
//...
}
class PaintMachineInfo
{
public string ColorName { get; set; }
public string MachineName { get; set; }
public string Pass { get; set; }
}
internal class PaintMachineLookup
{
public PaintMachineLookup(string machineName, string pass)
{
MachineName = machineName;
Pass = pass;
}
public string MachineName { get; private set; }
public string Pass { get; private set; }
public override int GetHashCode()
{
unchecked
{
int x = 27;
x = x * 11 + MachineName.GetHashCode();
x = x * 11 + Pass.GetHashCode();
return x;
}
}
public override bool Equals(object obj)
{
var other = obj as PaintMachineLookup;
if (other == null)
return false;
return MachineName.Equals(other.MachineName) && Pass.Equals(other.Pass);
}
}
var multidimensionalArray = new[,]
{
{"Color1", "Machine 1", "Pass 1"},
{"Color2", "Machine 2", "Pass 1"},
{"Color3", "Machine 1", "Pass 1"},
{"Color4", "Machine 1", "Pass 2"},
{"Color5", "Machine 2", "Pass 1"},
{"Color6", "Machine 2", "Pass 2"}
};
var tuple = new List<Tuple<string, string, string>>();
for (var i = 0; i < multidimensionalArray.Length/3-1; i++)
{
tuple.Add(new Tuple<string, string, string>(multidimensionalArray[i, 0], multidimensionalArray[i, 1], multidimensionalArray[i, 2]));
}
foreach (var el in tuple.GroupBy(x => String.Format("{0} & {1}", x.Item2, x.Item3), y => y.Item1))
{
Console.WriteLine(String.Join("/", el) + " for " + el.Key);
}
This question already has answers here:
What is the best way to dump entire objects to a log in C#?
(16 answers)
Closed 8 years ago.
i have class basically just a row of a table.
this row contains many columns.
for testing purpose, i will need to output the reads i get .
so i need to output all of the columns in the row.
the class is like
public class tableRow
{
public tableRow()
{}
public string id
public string name
public string reg
public string data1
....
....
....
<lot more columns>
}
then i need to write like:
Console.WriteLine("id: " + tableRow.id);
Console.WriteLine("name: " + tableRow.name);
Console.WriteLine("reg: " + tableRow.reg);
Console.WriteLine("data1: " + tableRow.data1);
...
...
...
<lot more Console.WriteLine>
So i want to know , is there an easy way to get all of these output , without so much console.writeLine?
thanks
You can serialize tableRow into JSON and all columns will be printed. E.g. with JSON.NET (available from NuGet):
tableRow tr = new tableRow { id = "42", name = "Bob", reg = "Foo" };
Console.WriteLine(JsonConvert.SerializeObject(tr, Formatting.Indented));
Output:
{
"id": "42",
"name": "Bob",
"reg": "Foo",
"data1": null
}
I use this approach for logging (to show object state) - it's nice to have extension
public static string ToJson<T>(this T obj)
{
return JsonConvert.SerializeObject(tr, Formatting.Indented);
}
Usage is simple:
Console.WriteLine(tr.ToJson());
Here's a short example using reflection:
void Main()
{
var myObj = new SomeClass();
PrintProperties(myObj);
myObj.test = "haha";
PrintProperties(myObj);
}
private void PrintProperties(SomeClass myObj){
foreach(var prop in myObj.GetType().GetProperties()){
Console.WriteLine (prop.Name + ": " + prop.GetValue(myObj, null));
}
foreach(var field in myObj.GetType().GetFields()){
Console.WriteLine (field.Name + ": " + field.GetValue(myObj));
}
}
public class SomeClass {
public string test {get; set; }
public string test2 {get; set; }
public int test3 {get;set;}
public int test4;
}
Output:
test:
test2:
test3: 0
test4: 0
test: haha
test2:
test3: 0
test4: 0
This should work for classes as well as types with custom type descriptors:
private static void Dump(object o)
{
if (o == null)
{
Console.WriteLine("<null>");
return;
}
var type = o.GetType();
var properties = TypeDescriptor.GetProperties(type);
Console.Write('{');
Console.Write(type.Name);
if (properties.Count != 0)
{
Console.Write(' ');
for (int i = 0, n = properties.Count; i < n; i++)
{
if (i != 0)
Console.Write("; ");
var property = properties[i];
Console.Write(property.Name);
Console.Write(" = ");
Console.Write(property.GetValue(o));
}
}
Console.WriteLine('}');
}
If you want to dump fields, and not properties, you can use type.GetFields() and make the necessary modifications to the above code. FieldInfo has a similar GetValue() method.
Note that this will not print "deep" representations of records. For that, you could adapt this into a recursive solution. You may also want to support collections/arrays, quote strings, and identify circular references.
You could use reflection, to do it ... Give me a minute, I'll post some code
Something along:
PropertyInfo[] propertyInfos;
propertyInfos = typeof(MyClass).GetProperties(BindingFlags.Public |
BindingFlags.Static);
// sort properties by name
Array.Sort(propertyInfos,
delegate(PropertyInfo propertyInfo1, PropertyInfo propertyInfo2)
{ return propertyInfo1.Name.CompareTo(propertyInfo2.Name); });
// write property names
foreach (PropertyInfo propertyInfo in propertyInfos)
{
Console.WriteLine(propertyInfo.Name);
}
You could add a check to see it's a string, and add some output as well, but that's the git of it.
Source from here
if you set up an array of strings
var dataFields = new string[] { "id", "name", ...
you could do a foreach loop to populate the table, and reference the table data using the same array, allowing you to do another foreach loop to do the Console.WriteLine calls
If you wanted to, each table row could just be a dictionary, with the datafield as the key, and the data as a value. Then you can just loop though the dictionary
I am trying to do the crazy formatting instructions my teacher gave me. After perusing for probably an hour (This is my first C# program), I came up with this line of code.
`Console.WriteLine(String.Format("{0," + -longestTitle + "} | {1," + -longestAlbumTitle + "} | {2," + -longestArtist + "} | {3:0.00, 8} | {4," + -longestYearAndRating + "} |", songArray[arraySearcher].title, songArray[arraySearcher].albumTitle, songArray[arraySearcher].artist, songArray[arraySearcher].length, songArray[arraySearcher].yearAndRating));`
longestX is an int containing the number of characters of the longestX (where x = title, album, etc).
The output I would like looks something like this:
Stuff | morestuff | extrastuff | 5.92 | 1992:R |
Stuf | est | sfafe | 232.44 | 2001:PG |
S uf | e | sfe | .44 | 2001:G |
(Where all padding is determined dynamically based on the longest title input by the user or file).
The output I get looks like this:
Program_Example.ClassName
Program_Example.ClassName
(or, specifically, Tyler_Music_Go.Song)
I have printed songArray[arraySearcher].title in this same method, and it works fine.
Could someone please help me?
Full relevant code:
class Song {
public string title, albumTitle, yearAndRating, artist;
public float length;
public Song(string titl, string albumTitl, string art, float leng, string yrNRating)
{
title = titl;
albumTitle = albumTitl;
yearAndRating = yrNRating;
length = leng;
artist = art;
}
}
//This class contains a Song array (with all Songs contained within), an array index, a search index, and ints to determine the longest of each category.
class SongList
{
Song[] songArray;
private int arrayKeeper, longestTitle, longestArtist, longestAlbumTitle, longestYearAndRating, checker;
int arraySearcher = 0;
public SongList()
{
songArray = new Song[10000];
arrayKeeper = 0;
longestTitle = 0;
longestArtist = 0;
longestAlbumTitle = 0;
longestYearAndRating = 0;
}
public void AddSong(string title, string albumTitle, string artist, float length, string yearAndRating)
{
songArray[arrayKeeper] = new Song(title, albumTitle, artist, length, yearAndRating);
arrayKeeper++;
checker = 0;
//This section of code is responsible for formatting the output. Since the longest values are already known, the list can be displayed quickly.
//Once a song is deleted, however, previously calculated longest lengths still stand.
foreach (char check in title)
{
checker++;
}
if (checker > longestTitle)
{
longestTitle = checker;
}
foreach (char check in albumTitle)
{
checker++;
}
if (checker > longestAlbumTitle)
{
longestAlbumTitle = checker;
}
foreach (char check in artist)
{
checker++;
}
if (checker > longestArtist)
{
longestArtist = checker;
}
foreach (char check in yearAndRating)
{
checker++;
}
if (checker > longestYearAndRating)
{
longestYearAndRating = checker;
}
}
//public bool RemoveSong(string title)
// {
//}
public void DisplayData()
{
Console.WriteLine("| Title | Album Title | Artist | Length | Year and Rating |");
for (arraySearcher = 0; arraySearcher < arrayKeeper; arraySearcher++)
{
//This line for testing purposes. (works)
Console.WriteLine(songArray[arraySearcher].title);
Console.WriteLine(songArray[arraySearcher].ToString());
}
}
public override string ToString()
{
//This line for testing purposes. (works)
Console.WriteLine(songArray[arraySearcher].title);
return String.Format("{0," + -longestTitle + "} | {1," + -longestAlbumTitle + "} | {2," + -longestArtist + "} | {3:0.00, 8} | {4," + -longestYearAndRating + "} |", songArray[arraySearcher].title, songArray[arraySearcher].albumTitle, songArray[arraySearcher].artist, songArray[arraySearcher].length, songArray[arraySearcher].yearAndRating);
}
}
`
EDIT:
Well, now I feel all manor of stupid. I was overwriting the tostring() method for the SongList, and then calling the tostring method for Song. Guy who answered made me realize it. Thanks to everyone who gave me advice, though.
You have to either access a property directly (songVariable.Title) or override ToString() in your song class to have that output the title.
public class Song
{
public string Title {get; set;}
public override string ToString()
{
return Title;
}
}