0

with the help of the Stack overflow community I have been able to take some data from an XML document and add it to a list using the following code.

String URLString = "https://api3.libcal.com/api_hours_grid.php?iid=4246&format=xml&weeks=1&systemTime=0";
XDocument xdoc = new XDocument();
xdoc = XDocument.Load(URLString);    

var location = (from p in xdoc.Descendants("location")
                from s in p.Descendants("week").Elements()            
                select new
                {

                    CampusName = (string)p.Element("name"),
                    WeekD = (string)s.Element("date"),
                    OpenHours = (string)s.Element("rendered"),
                    Day = s.Name.LocalName

                }).ToList();

this works great, and I can output to an unordered list with the following

string currentLocation = "";

foreach (var item in location)
{
    if (item.CampusName != currentLocation)
    {
        <h2>@item.CampusName</h2>
        currentLocation = @item.CampusName;
    }
    <ul>
        <li>@item.WeekD - @item.OpenHours</li>
    </ul>
} 

however, after reviewing my requirements I feel the data would be better displayed using a HTML Table. I have tried a few different ways using String builder to achieve this

string dayStop = "Sunday";    
StringBuilder sb = new StringBuilder();
sb.Append("<TABLE>\n");
sb.Append("<TR>\n");
foreach (var item in location)
{

    if (item.Day != dayStop)

    {
        sb.Append("<TH>\n");
        sb.Append(@item.Day);
        sb.Append("</TH>\n");

        //dayStop = @item.Day;
    }

}

sb.Append("</TR>\n");

sb.Append("<TR>\n");

sb.Append("<TD>\n");
sb.Append(@item.CampusName);
sb.Append("</TD>\n");
sb.Append("<TD>\n");
sb.Append(@item.OpenHours);
sb.Append("</TD>\n");

sb.Append("</TR>\n");
sb.Append("</TABLE > ");
}
Html.Raw(sb)

but its not working. I'd like the value of @item.Day to populate the value of TH, but as each item in my list contains value day value for each @item.CampusName I end up with a whole bunch of unnecessary @item.Day values and I feel like foreach is not the right answer here. What I'd like to end up with is something like the following snippet;

  <table>
    <tr>
    <th>
    Location
    </th>
    <th>
    Sunday
    </th>
    <th>
    Monday
    </th>
    <th>
    Tueday
    </th>
    </tr>
    <tr>
    <td>
    Campus 1
    </td>
    <td>
    9 - 5
    </td>
    <td>
    9 - 5
    </td>
    <td>
    9 - 5
    </td>
    </tr>
    <tr>
    <td>
    Campus 2
    </td>
    <td>
    9 - 5
    </td>
    <td>
    9 - 5
    </td>
    <td>
    9 - 5
    </td>
    </tr>
    </table>
er-sho
  • 9,581
  • 2
  • 13
  • 26
IcantCode
  • 11
  • 1
  • 7
  • Are you outputting the stringbuilder to an .HTML file? How are you rendering the HTML? – Tjaart van der Walt Sep 26 '18 at 05:56
  • @TjaartvanderWalt I am using Html.Raw(sb) – IcantCode Sep 26 '18 at 06:17
  • your xml contains too much repeating days like monday comes 6 times and same for other so what will be your html in this case? – er-sho Sep 26 '18 at 06:18
  • @DragandDrop I am not quite sure I understand what you mean, but I think the answer is now. This needs to sit in a razor file and generate the html table using something like Html.Raw(sb) – IcantCode Sep 26 '18 at 06:19
  • @IcantCode If you are using`Html.Raw(sb)` you are using ASP.NET MVC correct? Or at least Razor pages? – Tjaart van der Walt Sep 26 '18 at 06:19
  • @TjaartvanderWalt yes, I am using Razor – IcantCode Sep 26 '18 at 06:23
  • Is there any reason you've moved from using Razor to constructing the HTML directly with a StringBuilder? Note that this means you should handle any HTML escaping (e.g. for an event with a location of "Dog & Bone") in your code. – Jon Skeet Sep 26 '18 at 06:26
  • @ershoaib Hi, I only what the day values to come out once as table header values and the campus names to come out once as the first column in the table, does that make sense? – IcantCode Sep 26 '18 at 06:27
  • why you are not using html to render your table instead of using stringbuilder. is there any reason? – er-sho Sep 26 '18 at 06:33
  • @ershoaib only because I don't really know what I am doing at stringbuilder is what turned up in my research – IcantCode Sep 26 '18 at 06:47
  • @JonSkeet Only because I don't really know what I am doing at stringbuilder is what turned up in my research – IcantCode Sep 26 '18 at 06:49
  • I would stick to Razor syntax. It'll be much easier to get right, and the code is likely to be simpler too. – Jon Skeet Sep 26 '18 at 07:10
  • Maybe help you, Generic list to HTML table Visit – Cemal Can AKGÜL Oct 12 '18 at 13:51

1 Answers1

1

In your xml you have multiple Days so for th you have to take distinct from those days and render And for successive tr you have to group each of your row by its CampusName

Try below code that will help you to gt your desired result.

The best practice is to write the below code in your action method and pass result to your razor view by using viewbag or viewmodel. Your posted code in question is in razor view so I implemented my code in razor also.

<div>
    @{
        String URLString = "https://api3.libcal.com/api_hours_grid.php?iid=4246&format=xml&weeks=1&systemTime=0";
        XDocument xdoc = new XDocument();
        xdoc = XDocument.Load(URLString);


        var location = (from p in xdoc.Descendants("location")
                        from s in p.Descendants("week").Elements()
                        select new
                        {
                            CampusName = (string)p.Element("name"),
                            WeekD = (string)s.Element("date"),
                            OpenHours = (string)s.Element("rendered"),
                            Day = s.Name.LocalName
                        }).ToList();


        string dayStop = "Sunday";

        StringBuilder sb = new StringBuilder();
        sb.Append("<table>\n");
        sb.Append("<tr>\n");


        string currentLocation = "";

        foreach (var item in location.GroupBy(x => x.Day).Select(x => x.First()))
        {
            if (item.CampusName != currentLocation)
            {
                sb.Append("<th style='width:150px;'>\n");
                sb.Append("Location");
                sb.Append("</th>\n");
                currentLocation = item.CampusName;
            }

            if (item.Day != dayStop)
            {
                sb.Append("<th style='width:150px;'>\n");
                sb.Append(item.Day);
                sb.Append("</th>\n");
            }

        }

        sb.Append("</tr>\n");


        currentLocation = "";

        foreach (var item in location.GroupBy(x => x.CampusName))
        {
            sb.Append("<tr>\n");
            foreach (var innerItem in item)
            {
                if (innerItem.CampusName != currentLocation)
                {
                    sb.Append("<td>\n");
                    sb.Append(innerItem.CampusName);
                    sb.Append("</td>\n");
                    currentLocation = innerItem.CampusName;
                }


                if (innerItem.Day != dayStop)
                {
                    sb.Append("<td>\n");
                    sb.Append(innerItem.OpenHours);
                    sb.Append("</td>\n");
                }
            }

            sb.Append("</tr>\n");

        }

        @Html.Raw(sb.ToString());
    }

</div>

OR You can also use html to razor view and in that you don't need to use StringBuilder and this even more easy than above code.

@{
    string currentLocation1 = "";
    string currentLocation2 = "";
    string dayStop1 = "Sunday";
}

<table>
    <thead>
        <tr>
            @foreach (var item in location.GroupBy(x => x.Day).Select(x => x.First()))
            {
                if (item.CampusName != currentLocation1)
                {
                    <th style='width:150px;'>Location</th>
                    currentLocation1 = item.CampusName;
                }

                if (item.Day != dayStop1)
                {
                    <th style='width:150px;'>@item.Day</th>
                }
            }
        </tr>
    </thead>
    <tbody>
        @foreach (var item in location.GroupBy(x => x.CampusName))
        {
            <tr>
                @foreach (var innerItem in item)
                {
                    if (innerItem.CampusName != currentLocation2)
                    {
                        <td>@innerItem.CampusName</td>
                        currentLocation2 = innerItem.CampusName;
                    }


                    if (innerItem.Day != dayStop1)
                    {
                        <td>@innerItem.OpenHours</td>
                    }
                }

            </tr>
        }
    </tbody>
</table> 

Output from both of above snippet:

enter image description here

er-sho
  • 9,581
  • 2
  • 13
  • 26