Reputation: 91
I'm trying to make an application which shows the bushours including bus numbers, Aimed hour and Expected hour.
I get my (live) information from a HttpWebRequest
. My response from the request is stored in a string variable in XML format.
I can get all the information that I want; like the bus hour, Aimed hour and Expected hour.
The problem is that if there is no Expected hour nothing will be showed. I like to have when there is no Expected hour my code just takes the same value as the Aimed hour:
An example
Bus | Aimed | Execepted
-----------------------
1 | 17:05 | 17:07
2 | 17:05 | <nothing> so take value of aimed -> 17:05
I have already the following code
//XMLResponse put in documentRoot
//responseFromServer is a string variable in XML format with all the information
XElement documentRoot = XDocument.Parse(responseFromServer).Root;
XNamespace ns = "http://www.siri.org.uk/";
var buses = (from tblBuses in documentRoot.Descendants(ns + "PublishedLineName")
select tblBuses.Value).ToList();
var expHours = (from tblHours in documentRoot.Descendants(ns + "ExpectedDepartureTime")
select tblHours.Value).ToList();
foreach (var bus in buses)
{
string output = bus.Substring(bus.IndexOf('T') + 1);
int index = output.IndexOf(".");
if (index > 0)
output = output.Substring(0, index);
listBox1.Items.Add("Bus: " + output);
}
//Show every ExpectedDepartureTime
//If there is no expectedTime take value AimedDepartureTime
foreach (var expH in expHours)
{
string output = expH.Substring(expH.IndexOf('T') + 1);
int index = output.IndexOf(".");
if (index > 0)
output = output.Substring(0, index);
lstHours.Items.Add(output);
}
for being more clear to having an understand of my XML response, below an example of my XML response (One with AimedDeparturetime and Expected and one with without Expected)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Siri version="1.0" xmlns="http://www.siri.org.uk/">
<ServiceDelivery>
<ResponseTimestamp>2013-03-26T16:09:48.181Z</ResponseTimestamp>
<StopMonitoringDelivery version="1.0">
<ResponseTimestamp>2013-03-26T16:09:48.181Z</ResponseTimestamp>
<RequestMessageRef>12345</RequestMessageRef>
<MonitoredStopVisit>
<RecordedAtTime>2013-03-26T16:09:48.181Z</RecordedAtTime>
<MonitoringRef>020035811</MonitoringRef>
<MonitoredVehicleJourney>
<FramedVehicleJourneyRef>
<DataFrameRef>-</DataFrameRef>
<DatedVehicleJourneyRef>-</DatedVehicleJourneyRef>
</FramedVehicleJourneyRef>
<VehicleMode>bus</VehicleMode>
<PublishedLineName>2</PublishedLineName>
<DirectionName>Elstow P+R</DirectionName>
<OperatorRef>STB</OperatorRef>
<MonitoredCall>
<AimedDepartureTime>2013-03-26T16:11:00.000Z</AimedDepartureTime>
<ExpectedDepartureTime>2013-03-26T16:11:28.000Z</ExpectedDepartureTime>
</MonitoredCall>
</MonitoredVehicleJourney>
</MonitoredStopVisit>
---------------------------------------------------
<MonitoredStopVisit>
<RecordedAtTime>2013-03-26T16:09:48.181Z</RecordedAtTime>
<MonitoringRef>020035811</MonitoringRef>
<MonitoredVehicleJourney>
<FramedVehicleJourneyRef>
<DataFrameRef>-</DataFrameRef>
<DatedVehicleJourneyRef>-</DatedVehicleJourneyRef>
</FramedVehicleJourneyRef>
<VehicleMode>bus</VehicleMode>
<PublishedLineName>53</PublishedLineName>
<DirectionName>Wootton</DirectionName>
<OperatorRef>STB</OperatorRef>
<MonitoredCall>
<AimedDepartureTime>2013-03-26T16:19:00.000Z</AimedDepartureTime>
</MonitoredCall>
</MonitoredVehicleJourney>
</MonitoredStopVisit>
</StopMonitoringDelivery>
</ServiceDelivery>
</Siri>
So for this moment my application doesn't show every departure time of a bus.
How can I solve this?
Thanks!
Upvotes: 0
Views: 249
Reputation: 48496
Apologies upfront, because this is not the greatest XML parsing ever, but I would adjust my LINQ query:
var buses = from tblBuses in documentRoot.Descendants(ns + "MonitoredVehicleJourney")
select new
{
LineName = tblBuses.Descendants(ns + "PublishedLineName").Single().Value,
AimedHours = tblBuses.Descendants(ns + "AimedDepartureTime").Single().Value,
ExpectedHours = tblBuses.Descendants(ns + "ExpectedDepartureTime").Select(el => el.Value).SingleOrDefault()
};
This will create an IEnumerable
of some anonymous type which allows you to access the bus data more easily in subsequent code:
foreach (var bus in buses)
{
// Take ExpectedHours, or AimedHours if the first is null
string expH = bus.ExpectedHours ?? bus.AimedHours
// Same code as before here
string output = expH.Substring(expH.IndexOf('T') + 1);
int index = output.IndexOf(".");
if (index > 0)
output = output.Substring(0, index);
lstHours.Items.Add(output);
}
In your original code, buses that did not have an <ExpectedDepartureTime>
were never iterated over because they never show up in your expHours
List. In contrast, this LINQ query will contain all buses. It assumes that they all have a single <AimedDepartureTime>
and an optional <ExpectedDepartureTime>
.
For the expected departure time, I used a Select
to get the element value for each of the descendants. Using SingleOrDefault().Value
cannot be used, because the query might yield no elements and get_Value()
would be called on a null
reference.
One last comment about my query: for production code I would refrain from using Descendants
and do more strict querying of the XML structure.
Upvotes: 3
Reputation: 149050
You can do everything inside one linq query and use an ?:
conditional operator to select the correct output:
var buses =
(from tblBuses in documentRoot.Descendants(ns + "PublishedLineName")
let bus = tblBuses.Value
let output = bus.Substring(bus.IndexOf('T') + 1)
let index = output.IndexOf(".")
select (index > 0) ? output.Substring(0, index) : output);
foreach (var bus in buses)
{
listBox1.Items.Add("Bus: " + bus);
}
or even
var buses =
(from ...
select "Bus: " + ((index > 0) ? output.Substring(0, index) : output));
.ToArray();
listBox1.Items.AddRange(buses);
The same pattern can be applied to expHours
.
Upvotes: 2