Xpath path not working - c#

I'm trying to get an element and its siblings via XpathNavigator.
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE document SYSTEM 'xmlschemas/domino_6_5_5.dtd'>
<document xmlns='http://www.lotus.com/dxl' version='6.5' maintenanceversion='5.0'>
<item name='Keywords'><text/></item>
<item name='Version'><number>1</number></item>
<item name='UPDATEDISTRIBUTION'><text>1</text></item>
<item name='$FILE' summary='true' sign='true' seal='true'>
<object>
<file hosttype='cdstorage' compression='none' flags='storedindoc' name='STG08828'>
<created><datetime>20110119T230442,22+01</datetime></created>
</file>
</object>
</item>
</document>
I want to navigate to the file-element, with the following XPath:
//item/name/object/file[#name='STG08828']
Why this path is wrong?
EDIT: Thanks for the hint with my "name"-mistake.
When I try to run it, I get nothing.
XmlElement rootNode = xmlDoc.DocumentElement;
// select the file Element
String query = "//file[#name='" + name + "']";
XmlNodeList fileElement = rootNode.SelectNodes(query);

I think you want:
//item/object/file[#name='STG08828']
Or maybe just:
//file[#name='STG08828']

I think you are missing your names space for xmlns='http://www.lotus.com/dxl'
Example:
XNamespace myMs = "http://www.lotus.com/dxl";

Related

insert xml tag with c#

I'm trying to insert a XML tag in an existing xml-file. To insert an XML-Tag in C# is not really a problem, but my Problem is, that i need the new beginning-tag and the ending-tag on a certain place in that file ... can anybody help me ...
here an Example:
this is the xml-file before:
<?xml version="1.0" standalone="yes"?>
<DocumentElement>
<Item>
<ItemID>2711</ItemID>
<ItemTypeID>P</ItemTypeID>
<ColorID>1</ColorID>
</Item>
<Item>
<ItemID>64566</ItemID>
<ItemTypeID>P</ItemTypeID>
<ColorID>3</ColorID>
</Item>
</DocumentElement>
... this should be the new XML-file -> with the new tag "Inventory":
<?xml version="1.0" standalone="yes"?>
<DocumentElement>
<Inventory>
<Item>
<ItemID>2711</ItemID>
<ItemTypeID>P</ItemTypeID>
<ColorID>1</ColorID>
</Item>
<Item>
<ItemID>64566</ItemID>
<ItemTypeID>P</ItemTypeID>
<ColorID>3</ColorID>
</Item>
</Inventory>
</DocumentElement>
can anybody help me?
You can wrap the existing Items in a new XElement then replace the nodes on the original document:
XDocument doc = XDocument.Parse("<DocumentElement><Item><ItemID>2711</ItemID><ItemTypeID>P</ItemTypeID><ColorID>1</ColorID></Item><Item><ItemID>64566</ItemID><ItemTypeID>P</ItemTypeID><ColorID>3</ColorID></Item></DocumentElement>");
var items = doc.Descendants("Item");
XElement inventory = new XElement("Inventory");
inventory.Add(items);
doc.Root.ReplaceNodes(inventory);
//now you can save the XDocument to a file or whatever
Creates this:
<DocumentElement>
<Inventory>
<Item>
<ItemID>2711</ItemID>
<ItemTypeID>P</ItemTypeID>
<ColorID>1</ColorID>
</Item>
<Item>
<ItemID>64566</ItemID>
<ItemTypeID>P</ItemTypeID>
<ColorID>3</ColorID>
</Item>
</Inventory>
</DocumentElement>
If you have LINQPad you can run this script: http://share.linqpad.net/7c636x.linq
You can try this
var doc = XDocument.Load(file);
var characters = doc.Descendants("DocumentElement").FirstOrDefault();
if (characters != null)
{
XElement xe = new XElement("Inventory");
characters.Add(xe);
var oColl = doc.Descendants("Item");
xe.Add(oColl);
}
doc.Save(file);

Remove all nodes in a specified namespace from XML

I have an XML document that contains some content in a namespace. Here is an example:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:test="urn:my-test-urn">
<Item name="Item one">
<test:AlternativeName>Another name</test:AlternativeName>
<Price test:Currency="GBP">124.00</Price>
</Item>
</root>
I want to remove all of the content that is within the test namespace - not just remove the namespace prefix from the tags, but actually remove all nodes (elements and attributes) from the document that (in this example) are in the test namespace. My required output is:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:test="urn:my-test-urn">
<Item name="Item one">
<Price>124.00</Price>
</Item>
</root>
I'm currently not overly concerned if the namespace declaration is still present, for now I'd be happy with removing just the content within the specified namespace. Note that there may be multiple namespaces in the document to be modified, so I'd like to be able to specify which one I want to have the content removed.
I've tried doing it using .Descendants().Where(e => e.Name.Namespace == "test") but that is only for returning an IEnumerable<XElement> so it doesn't help me with finding the attributes, and if I use .DescendantNodes() I can't see a way of querying the namespace prefix as that doesn't seem to be a property on XNode.
I can iterate through each element and then through each attribute on the element checking each one's Name.Namespace but that seems inelegant and hard to read.
Is there a way of achieving this using LINQ to Xml?
Iterating through elements then through attributes seems not too hard to read :
var xml = #"<?xml version='1.0' encoding='UTF-8'?>
<root xmlns:test='urn:my-test-urn'>
<Item name='Item one'>
<test:AlternativeName>Another name</test:AlternativeName>
<Price test:Currency='GBP'>124.00</Price>
</Item>
</root>";
var doc = XDocument.Parse(xml);
XNamespace test = "urn:my-test-urn";
//get all elements in specific namespace and remove
doc.Descendants()
.Where(o => o.Name.Namespace == test)
.Remove();
//get all attributes in specific namespace and remove
doc.Descendants()
.Attributes()
.Where(o => o.Name.Namespace == test)
.Remove();
//print result
Console.WriteLine(doc.ToString());
output :
<root xmlns:test="urn:my-test-urn">
<Item name="Item one">
<Price>124.00</Price>
</Item>
</root>
Give this a try. I had to pull the namespace from the root element then run two separate Linqs:
Removes elements with the namespace
Removes attributes with the namespace
Code:
string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<root xmlns:test=\"urn:my-test-urn\">" +
"<Item name=\"Item one\">" +
"<test:AlternativeName>Another name</test:AlternativeName>" +
"<Price test:Currency=\"GBP\">124.00</Price>" +
"</Item>" +
"</root>";
XDocument xDocument = XDocument.Parse(xml);
if (xDocument.Root != null)
{
string namespaceValue = xDocument.Root.Attributes().Where(a => a.IsNamespaceDeclaration).FirstOrDefault().Value;
// Removes elements with the namespace
xDocument.Root.Descendants().Where(d => d.Name.Namespace == namespaceValue).Remove();
// Removes attributes with the namespace
xDocument.Root.Descendants().ToList().ForEach(d => d.Attributes().Where(a => a.Name.Namespace == namespaceValue).Remove());
Console.WriteLine(xDocument.ToString());
}
Results:
<root xmlns:test="urn:my-test-urn">
<Item name="Item one">
<Price>124.00</Price>
</Item>
</root>
If you want to remove the namespace from the root element add the this line in the if statement after you get the namespaceValue
xDocument.Root.Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
Results:
<root>
<Item name="Item one">
<Price>124.00</Price>
</Item>
</root>

Get entire node XML rather than InnerXml

Given the following XML:
<Root>
<Item id="1">
<name>Foo</name>
<status>Active</status>
</Item>
<Item id="2">
<name>Bar</name>
<status>Inactive</status>
</Item>
</Root>
Let's say I have this XML in an XmlDocument object and then have the following code:
var nodes = xmlDocumentObject.GetElementsByTagName("Item");
foreach (var node in nodes)
{
var nodeXml = ??
}
I can easily get the InnerXml of each node, which for the first node would be:
<name>Foo</name>
<status>Active</status>
But how can I get the XML for the node including the containing tag and its attributes, such as this:
<Item id="1">
<name>Foo</name>
<status>Active</status>
</Item>
Try using XmlNode.OuterXml instead of InnerXml :
foreach (XmlNode node in nodes)
{
var nodeXml = node.OuterXml;
}

XMLdocument Sort

I've figured out how to append nodes to my rss document in the right structyre. I now need to sort it in the pubDate order and then output to screen. Looking at the examples online, I've found lots of XDocument and Linq stuff but nothing with XmlDocument. Scratching my head whether to scrap what code I have and work out how to do it in XDocument with advice from here or continue with XMLDocument and figure out a way to sort.
With XMLDocument I've got the code working exactly as I want, just need my feed to be sorted in pubDate order when it spits it out to the screen. So I think I will stick with this for the timebeing. I've found this article http://support.microsoft.com/kb/555060 and an xslt someone posted in Stack Overflow, but I dont know how to call the "XmlHelperFunctions" from my code. Is XSLT the easiest option I have, or is there something easier out there?
This is my code:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(rssFeed.ToString());
XmlNodeList nl = xmlDoc.SelectNodes("/rss/channel/item");
foreach (XmlNode xn in nl)
{
string title = xn["title"].InnerText;
string link = xn["link"].InnerText;
string desc = xn["description"].InnerText;
string auth = xn["author"].InnerText;
string pdate = xn["pubDate"].InnerText;
XmlElement itemnode = xmlDoc.CreateElement("item");
itemnode.InnerXml = "<title></title><link></link><description></description><author></author><pubDate></pubDate>";
itemnode["title"].InnerText = title;
itemnode["link"].InnerText = link;
itemnode["description"].InnerText = desc;
itemnode["author"].InnerText = auth;
itemnode["pubDate"].InnerText = pdate;
xmlDoc.DocumentElement.SelectNodes("/rss/channel")[0].AppendChild(itemnode);
}
// Output to screen
xmlDoc.Save(Response.Output);
my rss feed
<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0">
<channel>
<title>My RSS Feed</title>
<link>http://www.mylink.aspx</link>
<description>
</description>
<item>
<title>Top marks</title>
<link>http://www.mymarks.aspx</link>
<description>
<p>description field here</p>
</description>
<author>Viv</author>
<pubDate>Thu, 16 Aug 2012 12:10:54 GMT</pubDate>
</item>
<item>
<title>Costa Coffee</title>
<link>http://www.Costa.aspx</link>
<description>
<p>Costa Coffee have special offers.</p>
</description>
<author>Mike</author>
<pubDate>Thu, 23 Aug 2012 15:55:53 GMT</pubDate>
</item>
<item>
<title>Celebrate success</title>
<link>http://www.Celebrate.aspx</link>
<description>
<p>Lets all celebrate </p>
</description>
<author>Viv</author>
<pubDate>Thu, 22 Aug 2012 09:10:21 GMT</pubDate>
</item>
</channel>
</rss>
You can do this fairly quickly and painlessly using Linq to XML.
If you parse your XML using XElement.Parse(...) you can then use OrderBy or OrderByDescending functions and alter the content pretty easily.
Here is a simplified example:
XElement element = XElement.Parse(#"
<rss>
<channel>
<item title='something' pubDate='22/11/2012'/>
<item title='something2' pubDate='24/03/2012'/>
<item title='something3' pubDate='10/02/2010'/>
<item title='something4' pubDate='22/01/2011'/>
</channel>
</rss>");
var elements = element.Element("channel")
.Elements("item")
.OrderBy<XElement, DateTime>(e => DateTime.ParseExact(e.Attribute("pubDate").Value, "dd/MM/yyyy", null))
.Select(e => new XElement("item",
new XElement("title", e.Attribute("title").Value),
new XElement("pubDate", e.Attribute("pubDate").Value))); // Do transform here.
element.Element("channel").ReplaceAll(elements);
Console.Write(element.ToString());
The XML is not going to be the same as yours, but hopefully it gives you an idea of what you could do. You can just specify XElement and XAttribute objects as content for your new XML, this outputs the following:
<rss>
<channel>
<item>
<title>something3</title>
<pubDate>10/02/2010</pubDate>
</item>
<item>
<title>something4</title>
<pubDate>22/01/2011</pubDate>
</item>
<item>
<title>something2</title>
<pubDate>24/03/2012</pubDate>
</item>
<item>
<title>something</title>
<pubDate>22/11/2012</pubDate>
</item>
</channel>
</rss>
I hope this is useful.

XML Namespaces are confounding me

I have an XML document which is confounding me. I'd like to (to start) pull all of the document nodes (/database/document), but it only works if I remove all of the attributes on the database element. Specifically the xmlns tag causes an xpath query for /database/document to return nothing - remove it, and it works.
xmlns="http://www.lotus.com/dxl"
I take it this has to do with XML namespaces. What is it doing, and more to the point, how do I make it stop? I just want to parse the document for data.
<?xml version="1.0" encoding="utf-8"?>
<database xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.lotus.com/dxl xmlschemas/domino_7_0_3.xsd"
xmlns="http://www.lotus.com/dxl"
version="7.0"
maintenanceversion="3.0"
path="C:\LotusXML\test1.nsf"
title="test1">
<databaseinfo numberofdocuments="3">
<datamodified>
<datetime dst="true">20090812T142141,48-04</datetime>
</datamodified>
<designmodified>
<datetime dst="true">20090812T154850,91-04</datetime>
</designmodified>
</databaseinfo>
<document form="NameAddress">
<noteinfo noteid="8fa" unid="x" sequence="2">
<created>
<datetime dst="true">20090812T130308,71-04</datetime>
</created>
<modified>
<datetime dst="true">20090812T142049,36-04</datetime>
</modified>
<revised>
<datetime dst="true">20090812T142049,35-04</datetime>
</revised>
<lastaccessed>
<datetime dst="true">20090812T142049,35-04</datetime>
</lastaccessed>
<addedtofile>
<datetime dst="true">20090812T130321,57-04</datetime>
</addedtofile>
</noteinfo>
<updatedby>
<name>MOOSE</name>
</updatedby>
<revisions>
<datetime dst="true">20090812T130321,57-04</datetime>
</revisions>
<item name="Name">
<text>joe</text>
</item>
<item name="OtherName">
<text>dave</text>
</item>
<item name="Address">
<text>here at home</text>
</item>
<item name="PictureHere">
<richtext>
<pardef id="1" />
<par def="1">
</par>
<par def="1" />
</richtext>
</item>
</document>
<document form="NameAddress">
<noteinfo noteid="8fe" unid="x" sequence="2">
<created>
<datetime dst="true">20090812T130324,59-04</datetime>
</created>
<modified>
<datetime dst="true">20090812T142116,95-04</datetime>
</modified>
<revised>
<datetime dst="true">20090812T142116,94-04</datetime>
</revised>
<lastaccessed>
<datetime dst="true">20090812T142116,94-04</datetime>
</lastaccessed>
<addedtofile>
<datetime dst="true">20090812T130333,90-04</datetime>
</addedtofile>
</noteinfo>
<updatedby>
<name>MOOSE</name>
</updatedby>
<revisions>
<datetime dst="true">20090812T130333,90-04</datetime>
</revisions>
<item name="Name">
<text>fred</text>
</item>
<item name="OtherName">
<text>wilma</text>
</item>
<item name="Address">
<text>bedrock</text>
</item>
<item name="PictureHere">
<richtext>
<pardef id="1" />
<par def="1">
</par>
<par def="1" />
</richtext>
</item>
</document>
<document form="NameAddress">
<noteinfo noteid="902" unid="x" sequence="2">
<created>
<datetime dst="true">20090812T130337,09-04</datetime>
</created>
<modified>
<datetime dst="true">20090812T142141,48-04</datetime>
</modified>
<revised>
<datetime dst="true">20090812T142141,47-04</datetime>
</revised>
<lastaccessed>
<datetime dst="true">20090812T142141,47-04</datetime>
</lastaccessed>
<addedtofile>
<datetime dst="true">20090812T130350,20-04</datetime>
</addedtofile>
</noteinfo>
<updatedby>
<name>MOOSE</name>
</updatedby>
<revisions>
<datetime dst="true">20090812T130350,20-04</datetime>
</revisions>
<item name="Name">
<text>julie</text>
</item>
<item name="OtherName">
<text>mccarthy</text>
</item>
<item name="Address">
<text>the pen</text>
</item>
<item name="PictureHere">
<richtext>
<pardef id="1" />
<par def="1">
</par>
<par def="1" />
</richtext>
</item>
</document>
</database>
The xmlns="http://www.lotus.com/dxl" sets a default namespace for contained nodes. It means that /database/document is really /{http://www.lotus.com/dxl}:database/{http://www.lotus.com/dxl}:document. Your XPath query will need to include the namespace:
XmlDocument doc = new XmlDocument();
doc.Load(fileName);
XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("tns", "http://www.lotus.com/dxl");
var documents = doc.SelectNodes("/tns:database/tns:document", ns);
When there is an XML namespace defined, each element needs to preceded by it for it to be correctly recognized.
If you were to use LINQ to XML to read in this data it would look something like this:
XDocument xdoc = XDocument.Load("file.xml");
XNamespace ns = "http://www.lotus.com/dxl";
var documents = xdoc.Descendants(ns + "document");
XML namespaces are similar in concept to C# namespaces (or any other language that supports it). If you define a class inside a namespace, you wouldn't be able to access it without first specifying the namespace (this is what using statements do for you).
You need to specify the element by their full name, including the namespace. The easy way to do this is to define the appropriate XNamespace and prepend it to the element name.
XDocument myDoc;
XNamespace ns = "http://www.lotus.com/dxl";
XElement myElem = myDoc.Element(ns + "ElementName");
See MSDN for more information.

Categories