I am new to XPath. I read the entire W3Schools tutorial. I would like to get all the <schedule> nodes of my document. I can get all the child elements of my document with child::* but as soon as I add <schedule> like the following, I get zero results:
XmlDocument dom = new XmlDocument();
dom.Load(textBoxFilePath.Text);
XmlNodeList jobElements = dom.DocumentElement.SelectNodes("child::schedule");
This is my xml:
<?xml version="1.0" encoding="UTF-8"?>
<!-- This file contains job definitions in schema version 2.0 format -->
<job-scheduling-data version="2.0" xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<processing-directives>
<overwrite-existing-data>true</overwrite-existing-data>
</processing-directives>
<schedule>
<job>
<name>receiverjob</name>
<group>receivergroup</group>
<job-type>Quartz.Server.ArgumentReceiverJob, Quartz.Server</job-type>
<job-data-map>
<entry>
<key>receivedargument</key>
<value>hamburger</value>
</entry>
</job-data-map>
</job>
<trigger>
<simple>
<name>argumentreceiverJobTrigger</name>
<group>argumentreceiverGroup</group>
<description>Simple trigger to simply fire sample job</description>
<job-name>receiverjob</job-name>
<job-group>receivergroup</job-group>
<misfire-instruction>SmartPolicy</misfire-instruction>
<repeat-count>-1</repeat-count>
<repeat-interval>10000</repeat-interval>
</simple>
</trigger>
<job>
<name>batchjob</name>
<group>batchGroup</group>
<job-type>Quartz.Server.BatchJob, Quartz.Server</job-type>
<durable>true</durable>
<recover>false</recover>
</job>
<trigger>
<cron>
<name>Trigger2</name>
<group>DEFAULT</group>
<job-name>batchjob</job-name>
<job-group>batchGroup</job-group>
<cron-expression>0/15 * * * * ?</cron-expression>
</cron>
</trigger>
</schedule>
</job-scheduling-data>
What I would ultimately like to achieve is to get all the <name>s of the <job>s that match a string.
That's because your XML has default namespace :
xmlns="http://quartznet.sourceforge.net/JobSchedulingData"
Register a prefix that points to default namespace, then use that prefix along with the element's local name to reference an element in namespace :
XmlDocument dom = new XmlDocument();
dom.Load(textBoxFilePath.Text);
XmlNamespaceManager nsManager = new XmlNamespaceManager(dom.NameTable);
nsManager.AddNamespace("d", dom.DocumentElement.NamespaceURI);
XmlNodeList jobElements = dom.DocumentElement.SelectNodes("child::d:schedule", nsManager);
.NET fiddle demo
You could use the following code to find all schedule elements.
XmlDocument dom = new XmlDocument();
dom.Load(textBoxFilePath.Text);
XmlNodeList jobElements = dom.GetElementsByTagName("schedule");
Related
How can I read the content of the childnotes using Xpath?
I have already tried this:
var xml = new XmlDocument();
xml.Load("server-status.xml");
var ns = new XmlNamespaceManager(xml.NameTable);
ns.AddNamespace("ns", "namespace");
var node = xml.SelectSingleNode("descendant::ns:server[ns:ip-address]", ns)
Console.WriteLine(node.InnerXml)
But I only get a string like this:
<ip-address>127.0.0.50</ip-address><name>Server 1</name><group>DATA</group>
How can I get the values individually?
Xml file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<server-status xmlns="namespace">
<server>
<ip-address>127.0.0.50</ip-address>
<name>Server 1</name>
<group>DATA</group>
</server>
</server-status>
You're using XML namespaces in XPath correctly.
However, your original XPath,
descendant::ns:server[ns:ip-address]
says to select all ns:server elements with ns:ip-address children.
If you wish to select the ns:ip-address children themselves, instead use
descendant::ns:server/ns:ip-address
Similarly, you could select ns:name or ns:group elements.
I have been unable to parse an Xml which contains multiple namespaces as below:
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:ServerVersionInfo xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" MajorVersion="14" MinorVersion="3" MajorBuildNumber="352" MinorBuildNumber="0" Version="Exchange2010_SP2" />
</s:Header>
<s:Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<GetMailTipsResponse xmlns="http://schemas.microsoft.com/exchange/services/2006/messages" ResponseClass="Success">
<ResponseCode>NoError</ResponseCode>
<ResponseMessages>
<MailTipsResponseMessageType ResponseClass="Success">
<ResponseCode>NoError</ResponseCode>
<m:MailTips xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<t:RecipientAddress xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<t:Name />
<t:EmailAddress>emailaddress#organization.com</t:EmailAddress>
<t:RoutingType>SMTP</t:RoutingType>
</t:RecipientAddress>
<t:PendingMailTips xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" />
<t:OutOfOffice xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<t:ReplyBody>
<t:Message />
</t:ReplyBody>
</t:OutOfOffice>
</m:MailTips>
</MailTipsResponseMessageType>
</ResponseMessages>
</GetMailTipsResponse>
</s:Body>
</s:Envelope>
I tried the following code but as you can see the first node with Soap Name Space works fine but thereafter I am unable to retrieve the node information that I need which is the node -
/s:Envelope/s:Body/GetMailTipsResponse/ResponseMessages/MailTipsResponseMessageType/m:MailTips/t:RecipientAddress/t:Message
Here is the code that I tried:
string getXmlInfo (string resultXml)
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(resultXml);
XmlNamespaceManager soapNsManager = new XmlNamespaceManager(xmlDoc.NameTable);
soapNsManager.AddNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
XmlNode xmlNode = xmlDoc.SelectSingleNode("//s:Envelope/s:Body", soapNsManager); //works fine - the node now contains the Xml starting with the node
XmlNode xmlNode1 = xmlDoc.SelectSingleNode("//s:Envelope/s:Body/GetMailTipsResponse/ResponseMessages/MailTipsResponseMessageType", soapNsManager); //returns NULL
XmlNode innerNode1 = xmlNode.SelectSingleNode("//GetMailTipsResponse/ResponseMessages/MailTipsResponseMessageType"); // returns NULL
XmlNode innerNode2 = xmlNode.SelectSingleNode("//GetMailTipsResponse", soapNsManager); //returns NULL
// the next line throws an exception
//XmlNode messageNode = xmlDoc.SelectSingleNode("/s:Envelope/s:Body/GetMailTipsResponse/ResponseMessages/MailTipsResponseMessageType/m:MailTips/t:RecipientAddress/t:Message", manager);
}
Here is what I tried based on response from #LocEngineer:
XmlNamespaceManager manager = new XmlNamespaceManager(xmlDoc.NameTable);
manager.AddNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
manager.AddNamespace("blank", "http://schemas.microsoft.com/exchange/services/2006/types");
manager.AddNamespace("m", "http://schemas.microsoft.com/exchange/services/2006/types");
manager.AddNamespace("t", "http://schemas.microsoft.com/exchange/services/2006/types");
XmlNode messageNode = xmlDoc.SelectSingleNode("/s:Envelope/s:Body/blank:GetMailTipsResponse/blank:ResponseMessages/blank:MailTipsResponseMessageType/m:MailTips/t:OutOfOffice/t:ReplyBody/t:Message", manager);
The messageNode shows up as NULL
You also need an XmlNamespaceManager prefix for the prefix-less namespace introduced in GetMailTipsResponse node, which contains the nodes without prefix in XML:
GetMailTipsResponse xmlns="http://schemas.microsoft.com/exchange/services/2006/messages"
And then use that in your XPath appropriately, using "m" as prefix for the prefix-less nodes:
XmlNamespaceManager soapNsManager = new XmlNamespaceManager(xmlDoc.NameTable);
soapNsManager.AddNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
soapNsManager.AddNamespace("m", "http://schemas.microsoft.com/exchange/services/2006/messages");
XmlNode xmlNode1 = xmlDoc.SelectSingleNode("/s:Envelope/s:Body/m:GetMailTipsResponse/m:ResponseMessages/m:MailTipsResponseMessageType", soapNsManager);
I am trying to make a generic XmlParsing method. Take the Xml as such:
<body>
<section>
<subsection1>
...
</subsection1>
<subsection2>
...
</subsection2>
</section>
<section>
<subsection1>
...
</subsection1>
<subsection2>
...
</subsection2>
</section>
</body>
I am trying to grab all "section" nodes without knowing how deep they are or their parent nodes names.
So far I have (my XML is in string format)
XmlDocument xml = new XmlDocument();
xml.LoadXml(XMLtoRead);
XmlNodeList nodes = xml.DocumentElement.SelectNodes("//section");
However the node count is always 0. I was under the impression the "//" prefix recursivly searches through the document for the nodes named.
My real XML is a SOAP reply:
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Response xmlns="http://tempuri.org/">
In that case it is not generic but specific to your kind of SOAP replies. ;-)
Try this:
var ns = new XmlNamespaceManager(xml.NameTable);
ns.AddNamespace("ns", "http://tempuri.org/");
XmlNodeList nodes = xml.DocumentElement.SelectNodes("//ns:section", ns);
I have some xml (in a file, but can be a string) which I need to parse, e.g.:
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xmlText);
Given the following XML:
<foo>
<cat>...</cat>
<cat>...</cat>
<dog>...</dog>
<cat>...</cat>
<dog>...</dog>
</foo>
I'm not sure how I can extract all the cat and dog elements and put them into the following output :-
<foo>
<cat>...</cat>
<cat>...</cat>
....
</foo>
and the same with dogs.
What's the trick to extract those nodes and put them into separate XMLDocuments.
Use Linq to XML as it has a much nicer API.
var doc = XElement.Parse(
#"<foo>
<cat>...</cat>
<cat>...</cat>
<dog>...</dog>
<cat>...</cat>
<dog>...</dog>
</foo>");
doc.Descendants("dog").Remove();
doc now contains this:
<foo>
<cat>...</cat>
<cat>...</cat>
<cat>...</cat>
</foo>
Edit:
While Linq to XML itself provides a nice API to work with XML, the power of Linq and its projection capabilities enables you to shape your data as you see fit.
Consider this, for example. Here the descendant elements are grouped by name and projected into a new root element which is then wrapped into a XDocument. Note that this creates an enumerable of XDocument.
var docs=
from d in doc.Descendants()
group d by d.Name into g
select new XDocument(
new XElement("root", g)
);
docs now contains:
<root>
<cat>...</cat>
<cat>...</cat>
<cat>...</cat>
</root>
---
<root>
<dog>...</dog>
<dog>...</dog>
</root>
Oh, by the way. The Descendants method goes through all descendant elements, use Elements if you only want the immediate child elements.
Here are the Linq to XML docs on MSDN
The easiest way will be to use XSLT and apply it on you XMLDocument in such way you won't modify your source and have as much outputs as you need.
The code for applying transform is
XslCompiledTransform xslTransform = new XslCompiledTransform();
StringWriter writer = new StringWriter();
xslTransform.Load("cat.xslt");
xslTransform.Transform(doc.CreateNavigator(),null, writer);
return writer.ToString();
And the simple cat.xslt is
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="foo">
<xsl:copy>
<xsl:copy-of select = "cat" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Since you are using XmlDocument: Load it twice from the same file and remove the unwanted nodes. Here is a link that shows you how: Removing nodes from an XmlDocument.
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xmlText);
XmlNode root = doc.DocumentElement;
nodeList = root.SelectNodes("//cat");
foreach (XmlNode node on nodeList)
{
root.RemoveChild(node);
}
How do I add a new node to the end of an existing XML file?
I understand how, but how in the end?
For example, I have the following XML file and need to add a new node "entry" to the end of the file:
<?xml version="1.0" encoding="utf-8" ?>
- <entries>
- <entry type="debit">
<amount>100</amount>
<date>11.11.2010</date>
- <description>
- <![CDATA[ Описание записи]]>
</description>
<category>Продукты</category>
</entry>
- <entry type="credit">
<amount>50</amount>
<date>11.11.2010</date>
- <description>
- <![CDATA[ Описание записи]]>
</description>
<category>Продукты</category>
</entry>
- <entry type="debit">
<amount>100</amount>
<date>11.11.2010</date>
- <description>
- <![CDATA[ Описание записи]]>
</description>
<category>Продукты</category>
</entry>
</entries>
The simplest way is to load the XML into memory, append the child node, then write out the whole document again. For example:
XDocument doc = XDocument.Load("before.xml");
doc.Root.Add(new XElement("extra"));
doc.Save("after.xml");
If that isn't exactly what you were after, please clarify your question.
XmlDocument doc = new XmlDocument();
doc.LoadXml("before.xml");
//XmlNode root = doc.DocumentElement;
//Create a new node.
XmlElement elem = doc.CreateElement("entry");
elem.InnerText="";
//Add the node to the document.
//root.AppendChild(elem);
//Console.WriteLine("Display the modified XML...");
doc.LastChild.AppendChild(elem);
doc.Save("before.xml");'