I am trying to get address from the XML file returned by google map api (by giving lat/lng as parameters). I am using the following code.
XDocument doc = XDocument.Load("uri");
var city = doc.Descendants("result").Where(s => s.Descendants("type").FirstOrDefault().Value == "locality");
Then I am reading for a specific descendant
address = Convert.ToString(city.Descendants("formatted_address").First().Value);
which I am trying to show on a map. All works fine for the first time(when I get the lat/lng values based on my IPAddress) but when the user double clicks on the map (am supposed to get the location of the click) the program is crashing. Works fine sometimes. I checked and its because in the xml file returned, sometimes there is no node type "locality" under "result". I wrote if and else if statements for this case but the debugger is still going inside the if loop and later showing the error "Sequence contains no elements". Doing this in a WPF application.
For ex : The only result nodes returned for 27/14 are type country & administrative_area_level_1.
You haven't shown us the XML involved, but I strongly suspect the problem is that you're not specifying the namespace. You probably want something like:
XNamespace ns = "some namespace URI";
XDocument doc = XDocument.Load("uri");
var city = doc.Descendants(ns + "result")
.Where(s => s.Descendants(ns + "type")
.FirstOrDefault().Value == "locality");
Of course there may be multiple namespaces involved - be aware of namespace inheritance due to xmlns="..." as well.
I would personally suggest using a simple cast from XElement to string instead of using the Value property - that way if FirstOrDefault() returns null, the result of the cast is null as well.
(It's not clear that your query really makes much sense, to be honest.)
Related
I'm using an anonymous type to grab some XML data. All was going well until I ran across a section of XML where there can be 2 or 3 similar nodes. Like in the XML sample below there are 3 separate "Phones". My code was working fine when there was only ONE element that was possible to grab after following the "element path" I led it to. How can i grab a specific one? Or all 3 for that matter? Handling XML is still new to me and there seems to be soo many ways of handling it Searching the web for my exact need here didn't prove successful. Thanks.
var nodes = from node in doc.Elements("ClaimsSvcRs").Elements("ClaimDownloadRs")
select new
{
Phone1 = (string)node.Elements("Communications").Elements("PhoneInfo").Elements("PhoneNumber").FirstOrDefault(),
Phone2 = (string)node.Elements("Communications").Elements("PhoneInfo").Elements("PhoneNumber").FirstOrDefault(),
};
The XML Code is
<?xml version="1.0" encoding="UTF-8"?>
<TEST>
<ClaimsSvcRs>
<ClaimDownloadRs>
<Communications>
<PhoneInfo>
<PhoneTypeCd>Phone</PhoneTypeCd>
<CommunicationUseCd>Home</CommunicationUseCd>
<PhoneNumber>+1-715-5553944</PhoneNumber>
</PhoneInfo>
<PhoneInfo>
<PhoneTypeCd>Phone</PhoneTypeCd>
<CommunicationUseCd>Business</CommunicationUseCd>
<PhoneNumber>+1-715-5552519</PhoneNumber>
</PhoneInfo>
<PhoneInfo>
<PhoneTypeCd>Phone</PhoneTypeCd>
<CommunicationUseCd>Cell</CommunicationUseCd>
<PhoneNumber>+1-715-5551212</PhoneNumber>
</PhoneInfo>
</Communications>
</ClaimDownloadRs>
</ClaimsSvcRs>
</TEST>
I haven't used xpath in a while so i'll let someone else stand in there... but there's a way to select a particular PhoneInfo object based upon its subelements. So if you knew whether you wanted Home or Business or Cell or whatever, you'd be able to select that particular PhoneInfo object. Otherwise if you wanted simple Phone1,2,3 and nulls where ok, use the Skip linq function. Phone2 = query.Skip(1).FirstOrDefault()
lol no worries ;) xpath can be intermixed in here, was my thought, and might be more elegant if your CommunicationUseCd fields were deterministic. Then you could have Home = ... and Work = ..., etc, instead of Phone1 & Phone2
The same could be accomplished by slipping a where clause into each your query lines
If you're up for LINQ you can get all your elements in one go:
foreach(XElement phone in XDocument.Parse(xmlString).Descendants("PhoneInfo"))
{
Console.WriteLine(phone.Element("PhoneNumber").Value);
//etc
}
I find XDocument & LINQ a lot easier than XmlDocument & XPath, if you're okay with the alternative. There's more info on them here
To retieve the value of a nested XElement I have the following in my code:
XElement xml = new XElement("UserDefinedSettings", new XElement("RootFolder", new XElement("FolderName", "User Input Goes Here")));
xml.Save("UserDefinedSettings.xml");
Which gives me this saved to the hard drive:
<?xml version="1.0" encoding="utf-8"?>
<UserDefinedSettings>
<RootFolder>
<FolderName>User Input Goes Here</FolderName>
</RootFolder>
</UserDefinedSettings>
Later, To retrieve the name of the folder that the user has chosen I am using:
XDocument xdoc = XDocument.Load("UserDefinedSettings.xml");
var myVar = xdoc.Descendants("FolderName").Single();
textBox1.Text = myVar.Value;
I am new to Linq-XML and I am wondering if what I have done is the right way to go about it?
Initially I had been using the following line of code to get the name of the folder, but I knew there had to be a better way and after searching here on SO for examples I am using the code above instead.
string s =xdoc.Element("UserDefinedSettings").Element("RootFolder").Element("FolderName").Value;
What you have should be fine (the newer way), as long as you are certain those elements exist. It runs the risk of throwing a null reference exception however if any of them do not exist. I typically query with Elements() rather than Element(). Elements() returns an IEnumerable which you can safely chain together with more Elements() queries (or whatever). For example, you might consider:
var folder = (string)xdoc.Elements("UserDefinedSettings").Elements("RootFolder").Elements("FolderName").FirstOrDefault();
Another thing I typically do when I want the value from an attribute or element is cast my XElements and XAttributes with a string as I did above. A null value cast as a string will return a null string, preventing a null reference exception that you would get with a .Value call.
Also would work:
xdoc.XPathSelectElement("/UserDefinedSettings/RootFolder/FolderName").Value
I prefer to use XPath for it's succinctness but it's your choice.
I need to get info from an XML-file in C#.
Here is some fragments from the XML.
<Surface id="su-62" surfaceType="InteriorWall">
<Name>S-3-7-I-W-62</Name>
<AdjacentSpaceId spaceIdRef="sp-3-TUIN">
</AdjacentSpaceId>
<AdjacentSpaceId spaceIdRef="sp-7-huizen">
</AdjacentSpaceId>
<CADObjectId>Basic Wall: _omgevingsmuur [184610]</CADObjectId>
</Surface>
...
<Surface id="su-63" surfaceType="ExteriorWall">
<Name>N-4-E-W-63</Name>
<AdjacentSpaceId spaceIdRef="sp-4-onthaal">
</AdjacentSpaceId>
<Opening id="su-63-op-1" openingType="NonSlidingDoor">
</Opening>
<CADObjectId>Basic Wall: _detentiemuur [193431]</CADObjectId>
</Surface>
...
<Surface id="su-282" surfaceType="Shade">
<Name>X-S-282</Name>
<CADObjectId>Basic Roof: Generic - 400mm [190976]</CADObjectId>
</Surface>
As you see there are some surfaces that don't have all the info others have.
I have to know which surfaces are adjacent to which space and if there is an opening or not.
(The ultimate goal is to make an 2d array where you can see which SPACE is adjacent to which SPACE and another array to see if the have a joined opening.)
Assuming you have .NET 3.5 or greater, than this is a good use for LINQ2XML.
You can write a query that will grab the associated areas an identify which space are adjacent to each other.
// Load your XML File into an XDocument object
var xDoc = XDocument.Load(xmlPath);
// this is your query, in the end result with my a Dictionary with the Surface
// Id attribute as the key and the AdjacentSpaceId as the value
var result = (from x in xDoc.Descendants("AdjacentSpaceId")
select new
{
SurfaceId = (String)x.Parent.Attribute("id"),
SurfaceName = (String)x.Parent.Element("Name"),
AdjacentSpace = (String)x
}).GroupBy(sp => sp.SurfaceId)
.ToDictionary(grp => grp.Key,
grp => grp.Select(value => value.AdjacentSpace));
If you are unfamiliar with any of the LINQ varients, I will attempt to explain the query in a bit more detail. The local field containing your query results is just called results. The var type is another .NET 3.5 addition that tells the compiler to identify the type by the right side of the expression. In this case, result will be a Dictionary<String, IEnumerable<String>>.
In the first line of the query:
from x in xDoc.Descendants("AdjacentSpaceId")
You are basically telling LINQ to iterate through all nodes in the XML called AdjacentSpaceId. It doesn't matter where these nodes are located or what the parent nodes are called. The Descendants() method in LINQ can be very powerful for this reason as it means you don't need to know the exact XML Path to a particular node to select it, you just need the name (see my note at the end)
The next lines of the query are defining exactly what you want to return from the query:
select new
{
SurfaceId = (String)x.Parent.Attribute("id"),
SurfaceName = (String)x.Parent.Element("Name"),
AdjacentSpace = (String)x
})
All LINQ queries using this syntax must have a select statement (there is also a method syntax that can be used instead of or in addition too that doesn't need a select necessarily, but this syntax is easier to learn in my opinion).
In the select clause, you are basically defining a new Anonymous Type that has 3 properties, SurfaceId, SurfaceName, and AdjacentSpace. I'll explain one of the assignments and it should let you understand all of them:
SurfaceId = (String)x.Parent.Attribute("id")
In this line, x refers to the initial line of the LINQ query where you were iterating all of your AdjacentSpaceId nodes, so x is of type XElement.
The Parent property of x is going to select the parent node, in this case Surface. (NOTE: If your root node in your XML happens to be called AdjacentSpaceId, then this line is going to throw an exception since the parent of a root node will be null, but seems to be a safe assumption that it won't be a problem in this case). The Parent property on XElement is also going to be another XElement.
The next part is the Attribute method of XElement, and you are selecting the first attribute of node called id.
So effectively you are selecting the id attribute of all parent nodes to each AdjacentSpaceId nodes.
And lastly, I am casting this to a String. There is a Value property that can be used instead, but my personal preference is to cast to a String because if the Attribute("id") method fails (which could happen if there is no attribute called "id"), calling the Value property will throw an exception, whereas casting to a string will just return null in these cases.
The other parts of this select clause are virtually the same.
The rest of this query are effectively separate queries. I just chained them together, but they could easily be broken out and put on their own.
The next piece:
GroupBy(sp => sp.SurfaceId)
is grouping the result of your LINQ query by the SurfaceId property. This is a key step because it sounds like you want to know where spaces are adjacent to each surface and this will effectively group all of the adjacent spaces together by the surface.
If you aren't familiar, sp => sp.SurfaceId is a Lambda Expression for creating anonymous functions or delegates quickly and easily.
And the final piece will take your grouped results and convert them into something more usable, usually a Dictionary<>, or in this case a Dictionary<String, IEnumerable<String>>
ToDictionary(grp => grp.Key,
grp => grp.Select(value => value.AdjacentSpace));
Just one side note about the Descendants() extension method for Linq-to-Xml objects, while it can be very useful as I mentioned above, it can also be very dangerous. When you have multiple nodes in the same XML with the same name but with different purposes or in different parents of the tree, Descendants() will return ALL objects. If you only want to return some of the nodes matching a specific name, you need to filter it down first by using the Element() or Elements() extension methods first to select the proper parent node, then you can safely call Descendants().
This is driving me a little crazy. I am pulling an XML string from a database column and successfully creating an XDocument using XDocument.Parse. I've used linq to xml before to query xml trees but for some reason on this everything I am doing is returning null. Is it something to do with the namespace?
Here is a sampling of the text visualizer for my XDocument object:
// removed XML for privacy reasons
An example of the query I am trying:
XElement algorithmId = (from algoId in reportLogXml.Descendants(ALGORITHM_ID)
select algoId).FirstOrDefault();
I am using a constant for the string value and I have quadruple checked that the spelling matches as well as trying several different elements that are clearly in the document but they all return null. What am I doing wrong here?
Yes, it probably has to do with the namespace but also the <AlgorithmId> element has no descendants.
You can fix the ns problem like this:
//untested
XNameSpace ns0 = "http://schemas.datacontract.org/2004/07/Adapters.Adapter";
var ns1 = reportLogXml.Root.GetDefaultNamespace();
// check: ns0 and ns1 should be equal here
... in reportLogXml.Descendants(ns1 + ALGORITHM_ID)
Note that this is a special + operator, follow the format exactly.
I have an application that I'm writing that allows users to search Twitter, written mainly just for fun and learning about how XML and LINQ play together. I've written code to fetch the atom feed (example query: http://search.twitter.com/search.atom?q=twitter), and I can verify that it is, in fact, receiving XML.
Just to get started with parsing the document, I figured it would be simple enough to just parse out the content of each tweet. Once I verify that works, then I would move on to the author, then date, and so on and so forth until everything is parsed.
Here is what I'm using to get the content:
var list = from tweet in doc.Element("feed").Descendants("entry")
select new Tweet("AUTHOR", tweet.Element("content").Value, new DateTime(), "TITLE");
As you can see, the document structure looks something like this:
<feed><entry><content></content></entry>.....</feed>
At least as far as we are concerned. I get a NullReferenceException on this line of code, but the debugger shows that the document is not null (it does in fact have the whole feed loaded in it). The previous line calls XDocument.Parse(), which throws no exceptions.
Does anyone know what could be causing my downfall?
The feed element includes this:
xmlns="http://www.w3.org/2005/Atom"
which changes the default namespace for the elements. You should use that like this:
XNamespace ns = "http://www.w3.org/2005/Atom";
var list = from tweet in doc.Element(ns + "feed")
.Descendants(ns + "entry")
select new Tweet("AUTHOR",
tweet.Element(ns + "content").Value,
new DateTime(), "TITLE");
(Just to explain, you were looking for a namespace-less "feed" element; that didn't exist, so Element returned null. If you'd fixed just that, Descendants would have returned an empty sequence. If you'd fixed those two, tweet.Element("content") would have returned null, causing a different NullPointerException.)
There are several possibilities:
Element("feed") is null
Descendants("entry") is null
tweet.Element("content") is null
You'll need to step through to figure out which is the culprit.