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);
Related
When I save a new Question element, I need it to be inside the questions element, which in turn is inside the QuestionCollection, but the way I'm saving the new elements are outside the questions and are not read later.
How XML is getting: (ABA IS NEW ELEMENT)
<?xml version="1.0" encoding="UTF-8"?>
<QuestionCollection>
<Question Titulo="ABA">
<Enunciado>ABB</Enunciado>
<Resposta1>ABC</Resposta1>
<Resposta2>ABD</Resposta2>
<Resposta3>ABE</Resposta3>
<Resposta4>ABF</Resposta4>
<RespostaC>ABC</RespostaC>
</Question>
<Questions>
<START>
</START>
<Question Titulo="AAA">
<Enunciado>AAB</Enunciado>
<Resposta1>AAC</Resposta1>
<Resposta2>AAD</Resposta2>
<Resposta3>AAE</Resposta3>
<Resposta4>AAF</Resposta4>
<RespostaC>AAF</RespostaC>
</Question>
</Questions>
</QuestionCollection>
How should I stay:
<?xml version="1.0" encoding="UTF-8"?>
<QuestionCollection>
<Questions>
<START>
</START>
<Question Titulo="AAA">
<Enunciado>AAB</Enunciado>
<Resposta1>AAC</Resposta1>
<Resposta2>AAD</Resposta2>
<Resposta3>AAE</Resposta3>
<Resposta4>AAF</Resposta4>
<RespostaC>AAF</RespostaC>
</Question>
<Question Titulo="ABA">
<Enunciado>ABB</Enunciado>
<Resposta1>ABC</Resposta1>
<Resposta2>ABD</Resposta2>
<Resposta3>ABE</Resposta3>
<Resposta4>ABF</Resposta4>
<RespostaC>ABC</RespostaC>
</Question>
</Questions>
</QuestionCollection>
Part of Code
XmlDocument doc = new XmlDocument ();
doc.Load ("Assets/Resources/Questions.xml");
XmlNode root = doc.DocumentElement;
XmlElement q = doc.CreateElement ("Question");
q.InnerText = Question;
XmlNode qa = doc.SelectSingleNode ("QuestionCollection/Questions/START");
root.InsertAfter(q,qa);
doc.Save ("Assets/Resources/Questions.xml");
Instance.CloseWindow ();
ERROR: ArgumentException: The reference node is not a child of this node.
#Marshall Tigerus is correct, but there's more;
Change this:
XmlElement q = doc.CreateElement("Question");
q.InnerText = Question;
XmlNode qa = doc.SelectSingleNode("QuestionCollection/Questions");
XmlNode start = qa.SelectSingleNode("START");
qa.InsertAfter(q,start);
Assuming you haven't put a typo in your code above, I think I know what is happening.
Your XML structure has QuestionCollection as an element, while your SelectSingleNode call has QuestionsCollection. This will cause the qa node to be null.
The InsertAfter method has logic in it that will handle the reference node being null. It will instead insert the new element as a child of the root element at the beginning of the list (https://msdn.microsoft.com/en-us/library/system.xml.xmlnode.insertafter(v=vs.110).aspx)
This appears to be what is happening here. Remove that extra s from your SelectSingleNode parameters and you should be good.
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");
<?xml version="1.0" encoding="utf-8"?>
<response list="true">
<audio>
<aid>253663595</aid>
<artist>Example</artist>
<duration>389</duration>
<lyrics_id>57485771</lyrics_id>
<genre>18</genre>
</audio>
<audio>
<aid>253663595</aid>
<artist>Example1</artist>
<duration>400</duration>
<lyrics_id>57485772</lyrics_id>
<genre>20</genre>
</audio>
</response>
Source code
XmlDocument allAudio = new XmlDocument();
allAudio.Load(#"e:\Audio.xml");
StreamWriter write_text = File.CreateText(#"e:\Audio.txt");
XmlNodeList audioNodes = allAudio.GetElementsByTagName("audio");
foreach (XmlNode audioNode in audioNodes)
{
XmlNode artistNode = audioNode["artist"];
write_text.WriteLine(String.Format("{0}", artistNode.Value));
}
write_text.Close();
Hi, I can't parse xml. I want write some node values in file, but as result I get an empty txt file.
You want: artistNode.InnerText
Not: artistNode.value
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);
}
I have the following XML.
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://localhost/gsainis/GsaInisWebService">
<string>
<gsafeed>
<group action="add">
<record>
......
......
</record>
</group>
</gsafeed>
</string>
</ArrayOfString>
I am using C# code (.NET 4.0) to parse this XML. I am using the code below to select all <record> nodes in the above XML.
XmlNamespaceManager xmlnsmgr = new XmlNamespaceManager(INISRecordXMLdoc.NameTable);
xmlnsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
xmlnsmgr.AddNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
xmlnsmgr.AddNamespace(string.Empty, "http://localhost/gsainis/GsaInisWebService");
foreach (XmlNode node in INISRecordXMLdoc.SelectNodes("//ArrayOfString/string/gsafeed/group/record",xmlnsmgr))
{
//Do something
}
The problem is the foreach loop is never executed. What should be correct XPath to be used such that I get all the <record> nodes?
Try this - I've had trouble in the past with having an "empty" XML prefix:
XmlNamespaceManager xmlnsmgr = new XmlNamespaceManager(INISRecordXMLdoc.NameTable);
xmlnsmgr.AddNamespace("ns", "http://localhost/gsainis/GsaInisWebService");
foreach (XmlNode node in INISRecordXMLdoc.SelectNodes("//ns:ArrayOfString/ns:string/ns:gsafeed/ns:group/ns:record", xmlnsmgr))
{
// Do something
}
Use something other than an empty string - and use that XML namespace prefix in your XPath.