1

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!

3
  • stackoverflow.com/questions/51075865/… please go through this link. Commented Sep 26, 2021 at 4:22
  • 1
    @AmalPs that post you linked has absolutely nothing to do with my question Commented Sep 26, 2021 at 7:43
  • 1
    String concatenation in a loop is very inefficient, because it creates a lot of garbage in memory. Therefore, you need to use either StringBuilder, or write directly to the output stream. Alternatively, you can use the capabilities of the XElement class from the System.Xml.Linq namespace - this will allow you to create html in an object-oriented style. Another way is to create html in a view using Razor. You have chosen the worst way, inefficient, error-prone and difficult to maintain. Commented Sep 29, 2021 at 4:09

1 Answer 1

1

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:

html

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:

rendered html with more objs

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>

Sign up to request clarification or add additional context in comments.

4 Comments

This is great although like you said, its not very dynamic. If there are more values in the Obj1 JSON array for example then it won't be rendered because its only looping until data.Submitter.Count ends. Is there a way to make it a bit more dynamic if in case Obj1 or Obj2 arrays contain more values?
@ayushlal you could get the highest count of the of items in Obj1 or Obj2 and use that as the loops condition, and perform a count check on each index within the respective Obj lists. If i is > then the Obj's count, then insert a blank string for the <td>, otherwise insert its value. I am assuming the blank won't throw the HTML off. Probably safer to insert " " So if Obj1 has 5 items, and Obj2 has 6, you'd loop 6 times, and on the 6th iteration, data.Submitter[0].Obj1[i] would be out of bounds, so insert a space for it.
Thanks heaps for that, I was thinking the same thing on the lines. If you could please update your answer that would be awesome!
@ayushlal looks to work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.