XML
<?xml version="1.0" encoding="utf-8" ?>
<animals>
<animal id="fisrt">
<type>Dog</type>
<name>Han</name>
</animal>
<animal id="second">
<type>Cat</type>
<name>Leia</name>
</animal>
</animals>
C#
using System.Xml.Linq;
string id = "second";
var filter = from ab in element.Elements("animal") where ab.Attribute("id").Equals(id) select ab;
foreach (XElement selector in filter)
{
label1.Content = selector.Element("name").Value;
}
What I need help with is selecting elements based on the parent element's id. The goal is to select the name who's parent's id is "second", so I'm trying to get "Leia". The problem I'm encountering is that nothing is happening to the label.
What am I doing wrong and how can I fix this issue. I'm also open to different approach if someone knows of a better way of achieving my goal.
You miss to check the value of attribute:
where ab.Attribute("id").Value.Equals(id)
Hope this help!
How about this:
string name = xdoc.Elements("animal")
.Where (e=>e.Attribute("id")=="first")
.Elements("name")
.Select(e=>e.Value)
.FirstOrDefault();
Essentially you want to put the condition about id attribute inside the where and continue the query.
I know this is the method annotation instead of linq syntax, I prefer it for being easier to read when things get hairy.
Related
With this XML data:
<item>
<placemarks>
<placemark>
<uid>{EA5FA2B2-78CB-4FAA-9F17-EBB361410499}</uid>
</placemark>
</placemarks>
<links>
<link>
<title>Book Online</title>
<link>https://blah</link>
</link>
<link>
<title>Call to Book</title>
<link>tel:1-866-555-5555</link>
</link>
</links>
</item>
I'm trying to create Hotel objects w/ various attributes from the XML. Everything works fine until I hit nested XML tags and then I got stuck:
var items = root.Descendants ("item");
var hotels = from it in items
select new Hotel () {
Uid = it.Element ("uid").Value,
Name = it.Element ("name").Value,
Description = it.Element ("description").Value,
ImageUrl = it.Element ("image_url").Value,
RoomType = it.Element("custom_1").Value,
PlacemarkId = it.Element("placemarks").Element("placemark").Element("uid").Value,
BookUrl = (from links in it.Descendents("links") where links.Element("link").Element("title) = "Book Online").Value
};
How do I get PlacemarkId to work? I keep getting null because the methods after the first Element("placemarks") evidently fails :-(
And obviously, setting the BookUrl property won't compile, but that's what I'd like to do. It's really ugly because of the weird XML schema w/ nested link tags :-(
Sorry for the noob question. I tried googling for every combo of "nested xml linq select" I could think of w/ no luck :-P
Would help even more if someone can let me know what I'm trying to do is called in LINQ. I would think it's possible...
Thanks in advance :-)
You can use XPathSelectElement() extension method to avoid null reference exception in case some <item> don't have placemark child (without having to manually check for nulls from C# code) :
var hotels = from it in items
select new Hotel()
{
......
......
PlacemarkId = (string)it.XPathSelectElement("placemarks/placemark/uid"),
BookUrl = (string)it.XPathSelectElement("links/link[title='Book Online']/link"),
};
Getting BookUrl value can also be done using XPath as demonstrated above. Or if you're sure the XML structure is consistent for this part (no element is ever missing), you can use LINQ without null checking like so :
BookUrl = (from link in it.Elements("links").Elements("link")
where (string)link.Element("title") == "Book Online"
select link.Element("link")).First().Value
For reference :
W3C : XML Path Language (XPath) Version 1.0 specification (formal & complete spec of xpath)
w3schools : XPath syntax (relaxed intro to basic xpath syntax)
I'm working with a specific FundsXML-Schema trying to get all Assetss of a specific XML-File to iterate through.
Short example of xml-file:
<?xml version="1.0" encoding="utf-8"?>
<FundsXML xmlns="http://www.fundsxml.org/XMLSchema/3.0.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="3.0.5" xsi:schemaLocation="http://www.fundsxml.org/XMLSchema/3.0.5 FundsXML3.0.5.xsd">
<Date>2015-02-27</Date>
...
<AssetMasterData>
<Asset>
<SecurityCodes>
<ISIN>XXXXXXXXXXXX</ISIN>
</SecurityCodes>
</Asset>
...
<Asset>
</AssetMasterData>
</FundsXML>
I want to iterate through Assets in there. I tried:
XDocument xmlTree = XDocument.Load(xmlPath);
XElement root = xmlTree.Root;
foreach (XElement f in root.Descendants())
{
System.Windows.MessageBox.Show(f.Name.ToString() +" ; "+f.Value.ToString());
}
Output: {http://www.fundsxml.org/XMLSchema/3.0.5}Date ; 2015-02-27
The second part would be to read ISIN of each Asset node.
But I hadn't time to do this, because I'm failing at the first part.
EDIT:
Solution was to search for namespace+name:
foreach (XElement f in root.Descendants("{http://www.fundsxml.org/XMLSchema/3.0.5}Asset"))
Best solution in my opinion:
foreach (XElement f in root.Descendants(xmlTree.Root.GetDefaultNamespace()+"Asset"))
As your XML is in a namespace, you need to add the namespace information to the Descendants query.
You can see an example here
You can try to get the
roots.Descendants()
Without filtering and check the nodes that it returns to confirm this.
Based on the sample data you've provided
<Asset></Asset>
doesn't appear to have any data in it. You would need to get
foreach (XElement f in root.Descendants("ISIN"))
I think anyway. If there's no actual text then you will get a blank or empty value?? So it sounds like it's returning what you're asking for??
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
I have included an XML file in my InfoPath form as a secondary data source. The data connection is named Divisions. Below is the structure and content of the file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Divisions>
<Division>
<Name>Business Application Services</Name>
<Abbr>BAS</Abbr>
</Division>
<Division>
<Name>Network Security Services</Name>
<Abbr>NSS</Abbr>
</Division>
<Division>
<Name>Back Office</Name>
<Abbr>BO</Abbr>
</Division>
</Divisions>
What I need to do is query this file using the Abbr of the division to get the Name.
I have used the following code to create a XPathNodeIterator:
XPathNodeIterator XPNIDivisions = this.DataSources["Divisions"].CreateNavigator().Select("/Divisions/Division/Name");
So now, how do I search for the name of the division whose Abbr is 'NSS'? Or is the code I'm using wrong (I'm new to XML manipulation)?
Please note that these abbreviations and names could change (more could be added, or some could be deleted); so I need it to be dynamic, meaning that if I have the Abbr in a variable MyAbbr, I have to look for its Name.
Thanks in advance,
Yusuf
Can you use Linq to Xml?
I was able to get this to work
string name = XDocument.Load("XMLFile1.xml")
.Descendants("Division")
.Where(x => x.Element("Abbr").Value == "NSS")
.First()
.Element("Name")
.Value;
Finally I've been able to find a solution, and I must say it's a bit more complex than Roger's solution; you have to know a bit about XPath expressions.
So what I had to do was just change the select from before
XPathNodeIterator XPNIDivisions = this.DataSources["Divisions"].CreateNavigator().Select("/Divisions/Division/Name");
to
XPathNodeIterator XPNIDivisions = this.DataSources["Divisions"].CreateNavigator().Select("/Divisions/Division[Abbr=\"thevalue\"]");
where thevalue is of course the value you're looking for.
In fact what I did was define a string variable
StrXPathDiv = "/Divisions/Division[Abbr=\"" + thevalue + "\"]";
and then pass it to the Select() method.
Then use the following code to get the value:
if (XPNIDivisions.MoveNext()) //If any record found
{
XPNIDivisions.Current.MoveToChild(XPathNodeType.Element);
XPNavMyDivision.SetValue(XPNIDivisions.Current.Value);
}
else { XPNavMyDivision.SetValue(""); }
where XPNavMyDivision is the navigator for where I need to set the value.
Thank you very much for your time and help Roger; I would have tried your solution if I were sure everyone had .NET 3.5 installed; however I'm quite sure of the opposite, so I had to stick with this.
I have the following set of data
<ids>
<id1 attr1="value1" attr2="value2" />
<id2 attr3="value3" attr4="value4" />
<id3 attr2="value6" attr5="value7" />
</ids>
Basically, it's an XML that can have any node name with any attribute name with any attribute value.
After parsing the XML, I store the attribute data in a Dictionary.
Then I store that same Dictionary as a value with the node name as a key.
So my data structure would be a Dictionary<string, Dictionary<string, string>> (let's give this a variable name called "dict")
So if I wanted to get the value for attr2 in the id1 node, I would do:
string value = dict["id1"]["attr2"];
// value will be value2
I think this is a pretty simple and workable solution for my needs, but there just seems to be this voice at the back of my head telling me that there is a different data structure or simpler solution that I'm missing out on. What does everyone think?
I think your solution is a good one. It will provide very fast lookups, and matches exactly to your domain.
Is your main problem with the nested dictionaries? If so, I would suggest that you not worry about it - using collections of collections is often a very useful tool.
My only complaint would be this: If you're not using this frequently, you're going to be loading a lot of information into a data structure that may be unncessary. If this is for one time lookups, leaving it in XML and using XPath queries may be a more optimal solution than pre-parsing and loading the entire thing into memory. If you're querying this frequently, though, this is a more optimal solution.
How about a class?
public class YourId
{
public string Id { get; set; }
public string Attribute1 { get; set; }
public string Value { get; set; }
}
Then you could create a List and populate it via your xml...
It would be easy to work with and you could use linq with it:
List<YourId> items = GetIdsFromXml();
var query = from i in items
where i.Id == "id1"
select i;
// or...
items.Where(i => i.Attribute == "blah").ToList();
// ect.
Just for grins - what if you kept the XML DOM and found your attributes with XPath queries? That way if you had duplicate node names you could accomodate that.
That XML doesn't look very good. It's not semantic XML at all. Semantic XML would be:
<data>
<item id="id1">
<value name="attr1">value1</value>
<!-- ... -->
</item>
<!-- ... -->
</data>
I know it's bigger, but that's XML for you. The reason I'm even saying this is that if you're not ready to go with semantic XML, you're probably looking for another data format. XML is a little bit bloated by nature. If you're looking for a compact format, have a look at JSON.
Anyways, using semantic XML, I would recommend XPath. Have a look in the MSDN documentation and look at the SelectNodes methods in the DOM objects.
Short example:
XmlDocument doc = new XmlDocument();
doc.Load("data.xml");
// Get a single item.
XmlNode item = doc.SelectSingleNode("//item[#id=myid]");
As long as all of the nodes have unique names, you should be OK. Note that it won't really work for XML like this:
<nodes>
<node id="id1" attr1="value1" attr2="value2" />
<node id="id2" attr3="value3" attr4="value4" />
<node id="id3" attr2="value6" attr5="value7" />
</nodes>
Given that the XML can have any node name and any attribute name I think your current solution is optimal.
Why not to use something that already exists?
Like Simple XML Parser in C#
If you need an XML grammar then create one for your needs. If you need a parser then use one of the many excellent ones provided in the .Net library. If you need to store the document in memory and access it use the DOM and XPath to select nodes. If you don't need any of this, then I would recommend against using XML and instead using something simpler like JSON.
If you need to keep the whole thing in memory, but just the values, then I suggest using the DataSets and loading them with the the XML loaders.