C# XmlDocument SelectNodes - c#

I have an xml document with a root element, two child elements, 'diagnostic' and 'results'. The 'results' element then has an arbitrary number of elements with the name 'result'
When this is loaded into an XmlDocument it is easy to navigate the structure and see that this is exactly how things operate. I can write a recursive function that picks out all the "result" elements. The XmlDocument.SelectNodes("//results") finds a node no problem.
However,
* XmlDocument.SelectNodes("//results/result") finds nothing.
* XmlDocument.SelectNodes("//result") finds nothing.
I've talked to a co-worker and he's had grief using Xpath in XmlDocument.SelectNodes. Anyone else run into this kind of problem? Any solutions?
XML FILE:
<?xml version="1.0" encoding="UTF-8"?>
<query xmlns:yahoo="http://www.yahooapis.com/v1/base.rng" yahoo:count="10" yahoo:created="2009-08-07T10:19:59Z" yahoo:lang="en-US" yahoo:updated="2009-08-07T10:19:59Z" yahoo:uri="http://query.yahooapis.com/v1/yql?q=select+*+from+search.news+where+query%3D%22Tanzania%22">
<diagnostics>
<publiclyCallable>true</publiclyCallable>
<url execution-time="47"><![CDATA[http://boss.yahooapis.com/ysearch/news/v1/Tanzania?format=xml&start=0&count=10]]></url>
<user-time>49</user-time>
<service-time>47</service-time>
<build-version>2579</build-version>
</diagnostics>
<results>
<result xmlns="http://www.inktomi.com/">
<abstract>Kakungulu Cup winners SC Villa face Tanzania’s Simba SC this afternoon at the National stadium in Dar es salaam. “We had a very tiresome journey. The road was so bad and the road blocks were so many. However, we finally reached but the boys were so tired,” said Kato.</abstract>
<clickurl>http://lrd.yahooapis.com/_ylc=X3oDMTQ4cXAxcnRoBF9TAzIwMjMxNTI3MDIEYXBwaWQDb0pfTWdwbklrWW5CMWhTZnFUZEd5TkouTXNxZlNMQmkEY2xpZW50A2Jvc3MEc2VydmljZQNCT1NTBHNsawN0aXRsZQRzcmNwdmlkA21VVGlta2dlQXUzeEYuM0xGQkQzR1pUU1FIS0dORXA4cUk4QUJJX1U-/SIG=12vhpskdd/**http%3A//www.monitor.co.ug/artman/publish/sports/SC_Villa_face_Simba_in_Tanzania_89289.shtml</clickurl>
<date>2009/08/07</date>
<language>english</language>
<source>The Monitor</source>
<sourceurl>http://www.monitor.co.ug/</sourceurl>
<time>20:22:32</time>
<title>SC Villa face Simba in Tanzania</title>
<url>http://www.monitor.co.ug/artman/publish/sports/SC_Villa_face_Simba_in_Tanzania_89289.shtml</url>
</result>
XPATH
doc.SelectNodes("//result") produces no hits.

Rob and Marc's answers are probably going in the right direction - XmlDocument + namespaces + XPath can be a bit of a pain.
If you're able to use .NET 3.5, I suggest you use LINQ to XML instead. That would make it really easy:
XDocument doc = XDocument.Load("foo.xml");
XNamespace ns = "bar";
var results = doc.Descendants(ns + "result");
foreach (var result in results)
{
...
}
Basically LINQ to XML is a superior API in almost every way, in my experience :) (I believe there are some capabilities it's missing, but if you have access to .NET 3.5 it's definitely worth at least trying.)

It sounds to me like namespaces are the issues; you generally need to enlist the help of an XmlNamespaceManager for this, and use an alias in your queries, i.e.
doc.SelectNodes("//x:results/x:result", nsmgr);
(where x is defined in nsmgr as an alias to the given namespace)

Related

Searching XML Elements by Attributes (C#)

I'm Trying to check XML elements for specific attributes so I can keep from saving duplicate element entries. the XML looks more or less like this:
<root>
<artist name="Coldplay">
<track name="yellow" artist="Coldplay" url="coldplay.com/yellow" playCount="123" />
<track name="fix you" artist="Coldplay" url="coldplay.com/fixyou" playCount="135" >
</artist>
//ect.
</root>
google and various search results suggest something like
[#id='foo']
but i don't know what that is and for reasons that might be more obvious to you than to me i can't "google" a collection of special characters like that without getting bizarre results. So If anyone can offer a suggestion for an if checking statement I'd be much obliged! or a name or link for how special characters are used in C#.
It's an XPath expression. You can use them along with a variety of XML-related objects in c#.
XmlDocument xd = new XmlDocument();
xd.LoadXml( xmlString );
XmlNodeList nodes = xd.SelectNodes( "//root/artist/track[#name='yellow']" );
General Reference: http://msdn.microsoft.com/en-us/library/ms256086.aspx
XPath with LINQ: http://msdn.microsoft.com/en-us/library/bb675183.aspx.
That's an XPath expression - but personally, I'd use LINQ to XML for the searching myself:
XDocument doc = XDocument.Load("test.xml");
var track = doc.Descendants("track")
.Where(t => (string) t.Attribute("id") == "foo")
.FirstOrDefault();
(Use Single, SingleOrDefault, First etc if you want to.)

XML namespaces and XPath

I have an application that has to load XML document and output nodes depending on XPath.
Suppose I start with a document like this:
<aaa>
...[many nodes here]...
<bbb>text</bbb>
...[many nodes here]...
<bbb>text</bbb>
...[many nodes here]...
</aaa>
With XPath //bbb
So far everything is nice.
And selection doc.SelectNodes("//bbb"); returns the list of required nodes.
Then someone uploads a document with one node like <myfancynamespace:foo/> and extra namespace in the root tag, and everything breaks.
Why? //bbb does not give a damn about myfancynamespace, theoretically it should even be good with //myfancynamespace:foo, as there is no ambiguity, but the expression returns 0 results and that's it.
Is there a workaround for this behavior?
I do have a namespace manager for the document, and I am passing it to the Xpath query. But the namespaces and the prefixes are unknown to me, so I can't add them before the query.
Do I have to pre-parse the document to fill the namespace manager before I do any selections? Why on earth such behavior, it just doesn't make sense.
EDIT:
I'm using:
XmlDocument and XmlNamespaceManager
EDIT2:
XmlDocument doc = new XmlDocument();
doc.XmlResolver = null;
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
//I wish I could:
//nsmgr.AddNamespace("magic", "http://magicnamespaceuri/
//...
doc.LoadXML(usersuppliedxml);
XmlNodeList nodes = doc.SelectNodes(usersuppliedxpath, nsmgr);//usersuppliedxpath -> "//bbb"
//nodes.Count should be > 0, but with namespaced document they are 0
EDIT3:
Found an article which describes the actual scenario of the issue with one workaround, but not very pretty workaround: http://codeclimber.net.nz/archive/2008/01/09/How-to-query-a-XPath-doc-that-has-a-default.aspx
Almost seems that stripping the xmlns is the way to go...
You're missing the whole point of XML namespaces.
But if you really need to perform XPath on documents that will use an unknown namespace, and you really don't care about it, you will need to strip it out and reload the document. XPath will not work in a namespace-agnostic way, unless you want to use the local-name() function at every point in your selectors.
private XmlDocument StripNamespace(XmlDocument doc)
{
if (doc.DocumentElement.NamespaceURI.Length > 0)
{
doc.DocumentElement.SetAttribute("xmlns", "");
// must serialize and reload for this to take effect
XmlDocument newDoc = new XmlDocument();
newDoc.LoadXml(doc.OuterXml);
return newDoc;
}
else
{
return doc;
}
}
<myfancynamespace:foo/> is not necessarily the same as <foo/>.
Namespaces do matter. But I can understand your frustration as they usually tend to breaks codes as various implementation (C#, Java, ...) tend to output it differently.
I suggest you change your XPath to allow for accepting all namespaces. For example instead of
//bbb
Define it as
//*[local-name()='bbb']
That should take care of it.
You should describe a bit more detailed what you want to do. The way you ask your question it make no sense at all. The namespace is just a part of the name. Nothing more, nothing less. So your question is the same as asking for an XPath query to get all tags ending with "x". That's not the idea behind XML, but if you have strange reasons to do so: Feel free to iterate over all nodes and implement it yourself. The same applies to functionality you are requesting.
You could use the LINQ XML classes like XDocument. They greatly simplify working with namespaces.

what's the fastest way to write XML

I need create XML files frequently and I choose XmlWrite to do the job, I found it spent much time on things like WriteAttributeString ( I need write lots of attributes in some cases), my question is are there some better way to create xml files? Thanks in advance.
Fastest way that I know is two write the document structure as a plain string and parse it into an XDocument object:
string str =
#"<?xml version=""1.0""?>
<!-- comment at the root level -->
<Root>
<Child>Content</Child>
</Root>";
XDocument doc = XDocument.Parse(str);
Console.WriteLine(doc);
Now you will have a structured and ready to use XDocument object where you can populate with your data. Also, you can even parse a fully structured and populated XML as string and start from there. Also you can always use structured XElements like this:
XElement doc =
new XElement("Inventory",
new XElement("Car", new XAttribute("ID", "1000"),
new XElement("PetName", "Jimbo"),
new XElement("Color", "Red"),
new XElement("Make", "Ford")
)
);
doc.Save("InventoryWithLINQ.xml");
Which will generate:
<Inventory>
<Car ID="1000">
<PetName>Jimbo</PetName>
<Color>Red</Color>
<Make>Ford</Make>
</Car>
</Inventory>
XmlSerializer
You only have to define hierarchy of classes you want to serialize, that is all. Additionally you can control the schema through some attributes applied to your properties.
Write it directly to a file via for example a FileStream (through manually created code). This can be made very fast, but also pretty hard to maintain. As always, optimizations comes with a prize tag.
Also, do not forget that "premature optimization is the root of all evil".
Using anonymous types and serializing to XML is an interesting approach as mentioned here
How much is much time...is it 10 ms, 10 sec or 10 min...and how much of the whole process that writes an Xml is it?
Not saying that you shouldn't optimize but imo it's a matter of how much time do you want to spend optimizing that slight bit of a process. In the end the faster you wanna go, the more complex it will be to maintain in this case (personal opinion).
I personally like to use XmlDocument type. It's still a bit heavy when writing nodes but attributes are one-liner, and all in all way simpler that using Xmlwrite.

XDocument or XmlDocument

I am now learning XmlDocument but I've just ran into XDocument and when I try to search the difference or benefits of them I can't find something useful, could you please tell me why you would use one over another ?
If you're using .NET version 3.0 or lower, you have to use XmlDocument aka the classic DOM API. Likewise you'll find there are some other APIs which will expect this.
If you get the choice, however, I would thoroughly recommend using XDocument aka LINQ to XML. It's much simpler to create documents and process them. For example, it's the difference between:
XmlDocument doc = new XmlDocument();
XmlElement root = doc.CreateElement("root");
root.SetAttribute("name", "value");
XmlElement child = doc.CreateElement("child");
child.InnerText = "text node";
root.AppendChild(child);
doc.AppendChild(root);
and
XDocument doc = new XDocument(
new XElement("root",
new XAttribute("name", "value"),
new XElement("child", "text node")));
Namespaces are pretty easy to work with in LINQ to XML, unlike any other XML API I've ever seen:
XNamespace ns = "http://somewhere.com";
XElement element = new XElement(ns + "elementName");
// etc
LINQ to XML also works really well with LINQ - its construction model allows you to build elements with sequences of sub-elements really easily:
// Customers is a List<Customer>
XElement customersElement = new XElement("customers",
customers.Select(c => new XElement("customer",
new XAttribute("name", c.Name),
new XAttribute("lastSeen", c.LastOrder)
new XElement("address",
new XAttribute("town", c.Town),
new XAttribute("firstline", c.Address1),
// etc
));
It's all a lot more declarative, which fits in with the general LINQ style.
Now as Brannon mentioned, these are in-memory APIs rather than streaming ones (although XStreamingElement supports lazy output). XmlReader and XmlWriter are the normal ways of streaming XML in .NET, but you can mix all the APIs to some extent. For example, you can stream a large document but use LINQ to XML by positioning an XmlReader at the start of an element, reading an XElement from it and processing it, then moving on to the next element etc. There are various blog posts about this technique, here's one I found with a quick search.
I am surprised none of the answers so far mentions the fact that XmlDocument provides no line information, while XDocument does (through the IXmlLineInfo interface).
This can be a critical feature in some cases (for example if you want to report errors in an XML, or keep track of where elements are defined in general) and you better be aware of this before you happily start to implement using XmlDocument, to later discover you have to change it all.
XmlDocument is great for developers who are familiar with the XML DOM object model. It's been around for a while, and more or less corresponds to a W3C standard. It supports manual navigation as well as XPath node selection.
XDocument powers the LINQ to XML feature in .NET 3.5. It makes heavy use of IEnumerable<> and can be easier to work with in straight C#.
Both document models require you to load the entire document into memory (unlike XmlReader for example).
As mentioned elsewhere, undoubtedly, Linq to Xml makes creation and alteration of xml documents a breeze in comparison to XmlDocument, and the XNamespace ns + "elementName" syntax makes for pleasurable reading when dealing with namespaces.
One thing worth mentioning for xsl and xpath die hards to note is that it IS possible to still execute arbitrary xpath 1.0 expressions on Linq 2 Xml XNodes by including:
using System.Xml.XPath;
and then we can navigate and project data using xpath via these extension methods:
XPathSelectElement - Single Element
XPathSelectElements - Node Set
XPathEvaluate - Scalars and others
For instance, given the Xml document:
<xml>
<foo>
<baz id="1">10</baz>
<bar id="2" special="1">baa baa</bar>
<baz id="3">20</baz>
<bar id="4" />
<bar id="5" />
</foo>
<foo id="123">Text 1<moo />Text 2
</foo>
</xml>
We can evaluate:
var node = xele.XPathSelectElement("/xml/foo[#id='123']");
var nodes = xele.XPathSelectElements(
"//moo/ancestor::xml/descendant::baz[#id='1']/following-sibling::bar[not(#special='1')]");
var sum = xele.XPathEvaluate("sum(//foo[not(moo)]/baz)");
XDocument is from the LINQ to XML API, and XmlDocument is the standard DOM-style API for XML. If you know DOM well, and don't want to learn LINQ to XML, go with XmlDocument. If you're new to both, check out this page that compares the two, and pick which one you like the looks of better.
I've just started using LINQ to XML, and I love the way you create an XML document using functional construction. It's really nice. DOM is clunky in comparison.
Also, note that XDocument is supported in Xbox 360 and Windows Phone OS 7.0.
If you target them, develop for XDocument or migrate from XmlDocument.
I believe that XDocument makes a lot more object creation calls. I suspect that for when you're handling a lot of XML documents, XMLDocument will be faster.
One place this happens is in managing scan data. Many scan tools output their data in XML (for obvious reasons). If you have to process a lot of these scan files, I think you'll have better performance with XMLDocument.

Using C# Regular expression to replace XML element content

I'm writing some code that handles logging xml data and I would like to be able to replace the content of certain elements (eg passwords) in the document. I'd rather not serialize and parse the document as my code will be handling a variety of schemas.
Sample input documents:
doc #1:
<user>
<userid>jsmith</userid>
<password>myPword</password>
</user>
doc #2:
<secinfo>
<ns:username>jsmith</ns:username>
<ns:password>myPword</ns:password>
</secinfo>
What I'd like my output to be:
output doc #1:
<user>
<userid>jsmith</userid>
<password>XXXXX</password>
</user>
output doc #2:
<secinfo>
<ns:username>jsmith</ns:username>
<ns:password>XXXXX</ns:password>
</secinfo>
Since the documents I'll be processing could have a variety of schemas, I was hoping to come up with a nice generic regular expression solution that could find elements with password in them and mask the content accordingly.
Can I solve this using regular expressions and C# or is there a more efficient way?
This problem is best solved with XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//password">
<xsl:copy>
<xsl:text>XXXXX</xsl:text>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This will work for both inputs as long as you handle the namespaces properly.
Edit : Clarification of what I mean by "handle namespaces properly"
Make sure your source document that has the ns name prefix has as namespace defined for the document like so:
<?xml version="1.0" encoding="utf-8"?>
<secinfo xmlns:ns="urn:foo">
<ns:username>jsmith</ns:username>
<ns:password>XXXXX</ns:password>
</secinfo>
I'd say you're better off parsing the content with a .NET XmlDocument object and finding password elements using XPath, then changing their innerXML properties. It has the advantage of being more correct (since XML isn't regular in the first place), and it's conceptually easy to understand.
From experience with systems that try to parse and/or modify XML without proper parsers, let me say: DON'T DO IT. Use an XML parser (There are other answers here that have ways to do that quickly and easily).
Using non-xml methods to parse and/or modify an XML stream will ALWAYS lead you to pain at some point in the future. I know, because I have felt that pain.
I know that it seems like it would be quicker-at-runtime/simpler-to-code/easier-to-understand/whatever if you use the regex solution. But you're just going to make someone's life miserable later.
You can use regular expressions if you know enough about what you are trying to match. For example if you are looking for any tag that has the word "password" in it with no inner tags this regex expression would work:
(<([^>]*?password[^>]*?)>)([^<]*?)(<\/\2>)
You could use the same C# replace statement in zowat's answer as well but for the replace string you would want to use "$1XXXXX$4" instead.
Regex is the wrong approach for this, I've seen it go so badly wrong when you least expect it.
XDocument is way more fun anyway:
XDocument doc = XDocument.Parse(#"
<user>
<userid>jsmith</userid>
<password>password</password>
</user>");
doc.Element("user").Element("password").Value = "XXXX";
// Temp namespace just for the purposes of the example -
XDocument doc2 = XDocument.Parse(#"
<secinfo xmlns:ns='http://tempuru.org/users'>
<ns:userid>jsmith</ns:userid>
<ns:password>password</ns:password>
</secinfo>");
doc2.Element("secinfo").Element("{http://tempuru.org/users}password").Value = "XXXXX";
Here is what I came up with when I went with XMLDocument, it may not be as slick as XSLT, but should be generic enough to handle a variety of documents:
//input is a String with some valid XML
XmlDocument doc = new XmlDocument();
doc.LoadXml(input);
XmlNodeList nodeList = doc.SelectNodes("//*");
foreach (XmlNode node in nodeList)
{
if (node.Name.ToUpper().Contains("PASSWORD"))
{
node.InnerText = "XXXX";
}
else if (node.Attributes.Count > 0)
{
foreach (XmlAttribute a in node.Attributes)
{
if (a.LocalName.ToUpper().Contains("PASSWORD"))
{
a.InnerText = "XXXXX";
}
}
}
}
The main reason that XSLT exist is to be able to transform XML-structures, this means that an XSLT is a type of stylesheet that can be used to alter the order of elements och change content of elements. Therefore this is a typical situation where it´s highly recommended to use XSLT instead of parsing as Andrew Hare said in a previous post.

Categories

Resources