How to access this nested XElement? - c#

hopefully you can help, this is the SOAP response message that I'm trying to read data from:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetVehicleImageDocDescriptorsResponse xmlns="http://kyle/webservices/">
<GetVehicleImageDocDescriptorsResult>
<VehicleImageDocDescriptor>
<Id>91222</Id>
<Epoch>2014-09-12T16:46:22.643</Epoch>
<ImageSetId>172308</ImageSetId>
<ImageMetadata>
<Group>public</Group>
<POV>Beauty Master</POV>
<RightHandDrive>false</RightHandDrive>
<ImageHashCode>1123</ImageHashCode>
</ImageMetadata>
</VehicleImageDocDescriptor>
<VehicleImageDocDescriptor>
<Id>123</Id>
...REPEATING....
And this is the code snippet I have to access the values in those fields:
XDocument doc = XDocument.Parse(getVehicleImageDocDescriptorsResult);
XNamespace ns = "http://kyle/webservices/";
IEnumerable<XElement> responses = doc.Descendants(ns + "VehicleImageDocDescriptor");
foreach (XElement response in responses)
{
pov = (string)response.Element(ns + "POV");
epoch = (string)response.Element(ns + "Epoch");
imageSetId = (string)response.Element(ns + "ImageSetId");
parsedDate = DateTime.Parse(epoch);
}
This works for 'epoch', 'imageSetId' but doesn't work for 'pov', I'm assuming because the 'pov' field is indented another level down it needs an extra level of 'address' if you would, I've been trying different ideas for hours and can't seem to retrieve the value, I've even tried having the full namespace as variable and using that in the 'pov's 'element' bracket but that doesn't work either.
Can anyone please provide me with some ideas?
Many thanks,
Kyle.

In that XML of yours, the POV is nested within ImageMetadata. This should work:
foreach (XElement response in responses)
{
var imageMetadata = response.Element(ns + "ImageMetadata");
var pov = (string)imageMetadata.Element(ns + "POV");
var epoch = (string)response.Element(ns + "Epoch");
var imageSetId = (string)response.Element(ns + "ImageSetId");
var parsedDate = DateTime.Parse(epoch);
}

Related

LINQ to read XML String and put into a variable

I am trying to read XML using LINQ. Previously I use XMLDocument to read but it gives an error and someone on StackOverflow encourage me to use LINQ.
Below is the code i previously used for the XMLDocument
string soapmessage = #"<?xml version=""1.0"" encoding=""UTF - 8""?>" + "\n" + response.Content;
XmlDocument xml = new XmlDocument();
xml.LoadXml(soapmessage); //loading soap message as string
XmlNamespaceManager manager = new XmlNamespaceManager(xml.NameTable);
manager.AddNamespace("d", "http://tempuri.org/");
manager.AddNamespace("bhr", "http://52.187.127.196:5000/api/gsowebservice.asmx");
XmlNodeList xnList = xml.SelectNodes("//bhr:FourMonthsAhead1Response", manager);
int nodes = xnList.Count;
string Status = xnList[0]["FourMonthsAhead1Result"]["PlantForecastIntervals"]["PlantForecastIntervalNode"]["IntervalStartTime"].InnerText;
Console.WriteLine(Status);
Console.ReadLine();
I am trying to get the <IntervalStartTime> from the first <PlantForecastIntervalNode> into a datetime variable;
Below attaced the XML im trying read:
Below is some of the XML code. I can't paste it here because the code is 2322 lines long so I shortened the code to this.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<s:Body>
<FourMonthsAhead1Response xmlns="http://tempuri.org/">
<FourMonthsAhead1Result xmlns="LSS.solar.webservice">
<PlantDescription xmlns="http://base.datacontract">*PlantName*</PlantDescription>
<PlantForecastIntervalsCount xmlns="http://base.datacontract">2976</PlantForecastIntervalsCount>
<ForecastStartDate xmlns="http://base.datacontract">2021-10-08T13:35:55.912612</ForecastStartDate>
<ForecastEndDate xmlns="http://base.datacontract">2021-10-08T13:35:55.9126123</ForecastEndDate>
<PlantForecastIntervals xmlns="http://base.datacontract">
<PlantForecastIntervalNode>
<IntervalStartTime>2021-10-01T00:00:00</IntervalStartTime>
<IntervalEndTime>2021-10-01T00:15:00</IntervalEndTime>
<IntervalLength>15</IntervalLength>
<ForecastResultParameter>FourMonthsAhead1</ForecastResultParameter>
<ForecastValue>0</ForecastValue>
<ValueUnit>MW</ValueUnit>
</PlantForecastIntervalNode>
<PlantForecastIntervalNode>
<IntervalStartTime>2021-10-01T00:15:00</IntervalStartTime>
<IntervalEndTime>2021-10-01T00:30:00</IntervalEndTime>
<IntervalLength>15</IntervalLength>
<ForecastResultParameter>FourMonthsAhead1</ForecastResultParameter>
<ForecastValue>0</ForecastValue>
<ValueUnit>MW</ValueUnit>
</PlantForecastIntervalNode>
</PlantForecastIntervals>
</FourMonthsAhead1Result>
</FourMonthsAhead1Response>
</s:Body>
</s:Envelope>
Update
After exploring other threads on StackOverflow I come up with this line below but receive another error of System.UriFormatException: 'Invalid URI: The Uri string is too long.':
XDocument xdoc = XDocument.Load(soapmessage);
var ids = xdoc.Element("FourMonthsAhead1Result")
.Elements("PlantForecastIntervals")
.Elements("<PlantForecastIntervalNode>")
.Select(item => item.Element("IntervalStartTime").Value);
Console.WriteLine(ids);
Try this using LINQ
var response = File.ReadAllText("XMLFile1.xml");
var xe = XElement.Parse(response);
XNamespace ns = "http://base.datacontract";
var obj = xe.Descendants(ns + "PlantForecastIntervals")
.Descendants(ns + "PlantForecastIntervalNode")
.Select(x => x.Element(ns + "IntervalStartTime").Value);
Console.WriteLine(obj);
Look at below solution,
var xmlRead = File.ReadAllText(#"XMLFile1.xml"); /// Add your xml file path
var xElement = XElement.Parse(xmlRead);
XNamespace xNamespace = "http://base.datacontract";
var obj = xElement.Descendants(xNamespace + "PlantForecastIntervals")
.Descendants(xNamespace + "PlantForecastIntervalNode")
.Select(x => x.Element(xNamespace + "IntervalStartTime").Value);
string[] dateTime = obj.ToArray();
foreach (string x in dateTime)
{
Console.WriteLine(x.ToString()); /// it will print time from all IntervalStartTime tags
}

.Net Core - Use LINQ to XML to get element by name from SOAP response

I have found quite a few instances of this type of question asked but for some reason none of the answers in all of those questions have worked for me.
The SOAP response that I get is this very simple one:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetDataAsStringResponse xmlns="http://tempuri.org/">
<GetDataAsStringResult>true</GetDataAsStringResult>
</GetDataAsStringResponse>
</s:Body>
</s:Envelope>
And these are all the options that I have tried in order to parse it and get to the "GetDataAsStringResult" element:
var doc = XDocument.Parse(responseString);
XNamespace xmlns = "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"";
XNamespace nsTempuri = "xmlns=\"http://tempuri.org/\"";
var namespacePrefix = doc.Root.GetNamespaceOfPrefix("s");
var resultElement = doc.Descendants("GetDataAsStringResult"); //yields no results
var resultElement1 = doc.Descendants(nsTempuri + "GetDataAsStringResult"); //yields no results
var resultElement2 = doc.Descendants(xmlns + "GetDataAsStringResult"); //yields no results
var resultElement3 = doc.Descendants(namespacePrefix + "GetDataAsStringResult"); //yields no results
var resultElement4 = doc.Descendants(namespacePrefix + "Body"); //Gets s:Body element with descendants
var resultElement5 = resultElement3.Descendants("GetDataAsStringResponse"); //yields no results
var resultElement6 = resultElement3.Descendants(nsTempuri + "GetDataAsStringResponse"); //yields no results
var resultElement7 = resultElement3.Descendants(namespacePrefix + "GetDataAsStringResponse"); //yields no results
var resultElement8 = resultElement4.Descendants(); //Gets IEnumerable with both GetDataAsStringResponse and GetDataAsStringResult
Based on all this experimentation I could do something like the following:
var doc = XDocument.Parse(responseString);
var namespacePrefix = doc.Root.GetNamespaceOfPrefix("s");
var dataAsStringResult = from data in doc.Descendants(namespacePrefix + "Body")
let descendats = data.Descendants()
select descendats.TakeLast(1).Single();
That looks like too many steps to get to what I need, especially when according to quite some answers in SO I should be able to do it in a much simpler fashion.
Any insights about why I am unable to get directly to the GetDataAsStringResult by using the "Descendants" method even when adding the namespace would be much appreciated.
Thank you.
After a couple of hours of going crazy it turned out to be a "you missed the semicolon" issue. The namespace object should be declared without the "xlmns=" part as follows:
XNamespace nsTempuri = "http://tempuri.org/";
By doing that then the following works:
doc.Descendants(nsTempuri + "GetDataAsStringResult");

Having issue parsing xml with linq to xml

I am having a problem parsing xml that I receive from Web Service.
The xml looks very simple:
<Result xsi:schemaLocation="urn:yahoo:developer http://developer.yahooapis.com/TimeService/V1/GetTimeResponse.xsd" type="web"><Timestamp>1320677359</Timestamp></Result>
But when I try to parse it with following code I am getting no return results.
XDocument doc = XDocument.Load("http://developer.yahooapis.com/TimeService/V1/getTime?appid=StackSolution");
var datestamp = from ds in doc.Descendants("Result")
select new { currentstamp = ds.Element("Timestamp").Value };
Is there a solution or way to parse it?
Thanks you in advance
You have a couple issues: First, the Result node isn't a descendant. It's the root. Second, you ran into the most common issue when using LINQ to XML - you forgot the namespace. The following should give you what you need:
XElement doc = XElement.Load("http://developer.yahooapis.com/TimeService/V1/getTime?appid=StackSolution");
XNamespace ns = "urn:yahoo:developer";
var datestamp = from ds in doc.DescendantsAndSelf(ns + "Result")
select new { currentstamp = ds.Element(ns + "Timestamp").Value };
Note, this produces an IEnumerable. If you only want the datestamp, consider using FirstOrDefault instead. You may be able to make this simpler by just doing the following:
XElement doc = XElement.Load("http://developer.yahooapis.com/TimeService/V1/getTime?appid=StackSolution");
XNamespace ns = "urn:yahoo:developer";
var datestamp = doc.Element(ns + "Timestamp").Value;
This method avoids the namespace issue using LocalName (unqualified identifier).
var datestamp = doc.Root.Descendants().Where(c => c.Name.LocalName.Equals("Timestamp")).FirstOrDefault().FirstNode.ToString()

c# XML from descendants

I am coding for WP7. I am expecting this code below to read the XML below:
_xml = XElement.Parse(e.Result);
results.Items.Clear();
foreach (XElement value in _xml
.Descendants("ResourceSets").Descendants("ResourceSet")
.Descendants("Resources").Descendants("Location"))
{
Results _item = new Results();
_item.Place = value.Element("Name").Value;
_item.Lat = value.Element("Point").Element("Latitude").Value;
_item.Long = value.Element("Point").Element("Longitude").Value;
results.Items.Add(_item);
}
But the foreach loop wont read it and place it in the _items.
<?xml version="1.0" encoding="utf-8" ?>
<Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/search/local/ws/rest/v1">
<Copyright>Copyright © 2011 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.</Copyright>
<BrandLogoUri>http://dev.virtualearth.net/Branding/logo_powered_by.png</BrandLogoUri>
<StatusCode>200</StatusCode>
<StatusDescription>OK</StatusDescription>
<AuthenticationResultCode>ValidCredentials</AuthenticationResultCode>
<TraceId>703e7f1427dd425185ded546ba8a0d2c|LTSM001154|02.00.126.3000|LTSMSNVM002008, LTSMSNVM001854, LTSMSNVM001853</TraceId>
<ResourceSets>
<ResourceSet>
<EstimatedTotal>4</EstimatedTotal>
<Resources>
<Location>
<Name>Ashford, Kent, United Kingdom</Name>
<Point>
<Latitude>51.146636679768562</Latitude>
<Longitude>0.87603025138378143</Longitude>
</Point>
<BoundingBox>
<SouthLatitude>51.076602190732956</SouthLatitude>
<WestLongitude>0.72853825986385345</WestLongitude>
<NorthLatitude>51.21656522154808</NorthLatitude>
<EastLongitude>1.0235222429037094</EastLongitude>
</BoundingBox>
<EntityType>PopulatedPlace</EntityType>
<Address>
<AdminDistrict>England</AdminDistrict>
<AdminDistrict2>Kent</AdminDistrict2>
<CountryRegion>United Kingdom</CountryRegion>
<FormattedAddress>Ashford, Kent, United Kingdom</FormattedAddress>
<Locality>Ashford</Locality>
</Address>
<Confidence>High</Confidence>
</Location>
</Resources>
</ResourceSet>
</ResourceSets>
</Response>
It looks like you're missing the namespace on each of your element names. Try this:
XNamespace xns = "http://schemas.microsoft.com/search/local/ws/rest/v1";
_xml = XElement.Parse(e.Result);
results.Items.Clear();
foreach (XElement value in _xml
.Descendants(xns + "ResourceSets").Descendants(xns + "ResourceSet")
.Descendants(xns + "Resources").Descendants(xns + "Location"))
{
Results _item = new Results();
_item.Place = value.Element(xns + "Name").Value;
_item.Lat = value.Element(xns + "Point").Element(xns + "Latitude").Value;
_item.Long = value.Element(xns + "Point").Element(xns + "Longitude").Value;
results.Items.Add(_item);
}
Is there a particular reason why you're using Descendants?
You could just do:
XmlDocument doc = new XmlDocument();
doc.Load(YourXMLPath);
XmlNode locationNode = doc["ResourceSets"]["ResourceSet"]["Resources"]["Location"];
foreach(XmlElement value in locationNode.Children)
{
Results _item = new Results();
_item.Place = value.Element("Name").Value;
_item.Lat = value.Element("Point").Element("Latitude").Value;
_item.Long = value.Element("Point").Element("Longitude").Value;
results.Items.Add(_item);
}
I don't have VS right now, but that should be close to it.
Of course a good behavior would be to check if nodes are null before getting the next one.
public ObservableCollection<Results> result = new ObservableCollection<Results>();
XDocument xmldoc = XDocument.Parse(e.Result.ToString());
var data = from c in xmldoc.Descendants("ResourceSets").Descendants("ResourceSet").Descendants("Resources").Descendants("Location")
select new Results{
Place = c.Element("Name").Value;
Lat = c.Element("Point").Element("Latitude").Value;
Long = c.Element("Point").Element("Longitude").Value;
};
foreach (Results obj in data)
{
result.Add(obj);
}
Have not tried, but this is how I do it.

XDocument and Linq returns null if the element has xmlns attribute

Newbie with XDocuments and Linq, please suggest a solution to retrieve the data from a particular tag in the xml string:
If I have a XML string from webservice response (I formatted xml for ease):
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetCashFlowReportResponse xmlns="http://tempuri.org/">
<GetCashFlowReportPdf>Hello!</GetCashFlowReportPdf>
</GetCashFlowReportResponse>
</soap:Body>
</soap:Envelope>
Using the following code, I can get the value only if GetCashFlowReportResponse tag doesn't have "xmlns" attribute. Not sure why? Otherwise, it always return null.
string inputString = "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><soap:Body><GetCashFlowReportResponse xmlns=\"http://tempuri.org/\"><GetCashFlowReportPdf>Hello!</GetCashFlowReportPdf></GetCashFlowReportResponse></soap:Body></soap:Envelope>"
XDocument xDoc = XDocument.Parse(inputString);
//XNamespace ns = "http://tempuri.org/";
XNamespace ns = XNamespace.Get("http://tempuri.org/");
var data = from c in xDoc.Descendants(ns + "GetCashFlowReportResponse")
select (string)c.Element("GetCashFlowReportPdf");
foreach (string val in data)
{
Console.WriteLine(val);
}
I can't change the web service to remove that attribute. IS there a better way to read the response and get the actual data back to the user (in more readable form)?
Edited:
SOLUTION:
XDocument xDoc = XDocument.Parse(inputString);
XNamespace ns = "http://tempuri.org/";
var data = from c in xDoc.Descendants(ns + "GetCashFlowReportResponse")
select (string)c.Element(ns + "GetCashFlowReportPdf");
foreach (string val in data)
{
Console.WriteLine(val);
}
Note: Even if all the child elements doesn't have the namespace attribute, the code will work if you add the "ns" to the element as I guess childs inherit the namespace from parent (see response from SLaks).
You need to include the namespace:
XNamespace ns = "http://tempuri.org/";
xDoc.Descendants(ns + "GetCashFlowReportResponse")
XName qualifiedName = XName.Get("GetCashFlowReportResponse",
"http://tempuri.org/");
var data = from d in xDoc.Descendants(qualifiedName)
Just ask for the elements using the qualified names:
// create a XML namespace object
XNamespace ns = XNamespace.Get("http://tempuri.org/");
var data = from c in xDoc.Descendants(ns + "GetCashFlowReportResponse")
select (string)c.Element(ns + "GetCashFlowReportPdf");
Note the use of the overloaded + operator that creates a QName with a XML namespace and a local name string.

Categories

Resources