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.
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
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.
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.
The main problem is this one.
I have 2 XMLs containing information about what my company does. One is considered the template XML where you can find the general information and the other one is the Catalog containing information about each individual equipment, containing a reference to the template XML.
They look like this
Catalog XML
<list>
<A>
<B>
<c>reference to template</c>
<d>info 2</d>
<e>info 3</e>
<f>info 4</f>
<g>
<h>info5</h>
<i>info5</i>
</g>
</B>
<B>
<c>reference to template</c>
<d>info a</d>
<e>info s</e>
<f>info d</f>
<g>
<h>infof</h>
<i>infog</i>
</g>
</B>
<B>
<c>reference to template</c>
<d>info h</d>
<e>info j</e>
<f>info k</f>
<g>
<h>infot</h>
<i>infoy</i>
</g>
</B>
</A>
</list>
Template
<list>
<R>
<S>
<t>info 7</t>
<u>info 8</u>
<v>info 9</v>
<w>info 10</w>
</S>
</R>
</list>
What I need to do is to display all of the equipment catalogued in a listView, which will lits information from both XMLs.
I've tried that and had no succes, all I can display is one equipment, weel it actually isn'y displayed, all that appears is invisible information.
I run through both XMLs using this:
xDocument load = xDocument.load("Myxml.xml");
var run = (from x in load.Descendants("A")
where x.Element("c").Value == comboBox1.SelectedItems.ToString()
select new
{
a = x.Element("d").Valuye.ToString(),
//here I gather the rest of the information
}).ToList();
listView.Items.Add(run);
//after that I tried listview.Items.Add(run.a) ... but the code which I use to run through
//ends with FirstorDefault(), instead of ToList() and I try adding all the components manually
The only thing that Appears is an invisible Equipment, which means that when I click on it I can see there's something there, but I just can't see the information.
So I tried adding strings using the same methodology, but got the same result.
Can anyone please tell me where's my mistake? I can't see it.
PS: After I manage to do this, I'm gonna Implement a function, that by double clicking on the information, the client will be able to alter the information. If someone knows where to start with this one, please point me in the right direction
I believe your linq query needs a bit of a touch up, something like:
xDocument load = xDocument.load("Myxml.xml");
var run = (from x in load.Descendants("B")
where x.Element("c") == comboBox1.SelectedItems.ToString()
select new
{
a = x.Element("d").Valuye.ToString(),
//here I gather the rest of the information
}).ToList();
Also, you should try using a for loop on the list and adding the strings one by one
foreach (var item in run)
listView.Items.Add(item.a);
You can take a look at the different overloads of the Add method on this MSDN page:
http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.listviewitemcollection.aspx
To be honest, I'm not entirely sure what the question is. I'll take a shot at the XDocument query, though.
When doing comparisons, you need to compare the correct types. The comparison from the example won't even compile because there is a comparison of an XElement with a String:
where x.Element("c") == comboBox1.SelectedItems.ToString()
Should be:
where x.Element("c").Value == comboBox1.SelectedItems.ToString()
If the overall goal is to get a list of strings from the query, then take a look at the following:
string match = comboBox1.SelectedItems.ToString();
var doc = XDocument.Load( "MyXml.xml" );
var query = doc.Descendants( "B" )
.Where( x => x.Element( "c" ).Value == match )
.Select( x => x.Element( "d" ).Value )
.ToList();
Notice that the query starts with the "B" element. Starting at the "A" element will result in 0 elements matched in the where clause.
Also, it is easier to break these types of problems down by using additional variables to break down the statement, i.e., variables for the matching criteria, for the XDocument, for the query, etc... Even the query could be broken down into further sub-queries if needed.
This should get you started.
If you are using the Detail mode of the list view, you need to add columns to the list and subitems in the items corresponding to each column, else your items will be "invisible".
See the ListView.Columns and ListViewItem.SubItems members for more details.
I found out what was wrong, I needed to add an observable collection, to which I added the content I wanted to put on the viewlist, and then I put the information in the observablecollection in the viewlist.
Thank you all for helping me.
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.