I'm trying to work out why my xpath won't select the nodes I specificy
My xpath expression is //DefaultValue, so I expect all elements of name DefaultValue to be selected
My test file (cut down) is :
<?xml version="1.0" encoding="utf-8"?>
<SharedDataSet xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner" xmlns="http://schemas.microsoft.com/sqlserver/reporting/2010/01/shareddatasetdefinition">
<Description />
<DataSet Name="ddd">
<Query>
<DataSourceReference>xxx</DataSourceReference>
<DataSetParameters>
<DataSetParameter Name="p1">
<DefaultValue>baaaah</DefaultValue> <!-- this node should be selected eh? -->
</DataSetParameter>
<DataSetParameter Name="p2">
<DefaultValue>fooo</DefaultValue> <!-- this node should be selected too eh? -->
</DataSetParameter>
</DataSetParameters>
</Query>
</SharedDataSet>
Code is :
XmlNamespaceManager xn = new XmlNamespaceManager(new NameTable());
xn.AddNamespace("ns", "http://schemas.microsoft.com/sqlserver/reporting/2010/01/shareddatasetdefinition");
xn.AddNamespace("rd", "http://schemas.microsoft.com/SQLServer/reporting/reportdesigner");
xn.AddNamespace("cl", "http://schemas.microsoft.com/sqlserver/reporting/2010/01/componentdefinition");
XDocument document = XDocument.Parse(reportBuffer, LoadOptions.PreserveWhitespace);
IEnumerable<XElement> elements = document.XPathSelectElements(xpath, xn);
at this point elements.Count() equals 0
Can anyone see what foolishness I present?
I can't seem to make the xml demons happy... :-(
The <DefaultValue> element is actually bound to the namespace http://schemas.microsoft.com/sqlserver/reporting/2010/01/shareddatasetdefinition.
The <SharedDataSet> document element has it declared without a prefix, so it is easy to miss. Since <DefaultValue> is a descendant, it inherits the namespace.
If you want to select the <DefaultValue> element you need to adjust your XPath:
//ns:DefaultValue
Related
I have this snippt of XML
<unit class="xxx.xxx.xxx" id="382">
<customId>000</customId>
<description>kg</description>
<key>22452</key>
<Description>Kilogramm</Description>
</unit>
how to get the node 'unit' or parnet of the key element using the value of an element. For instance
i have the value of key element above [22452] and it's Uniqe inside the xml-File.
what i am trying to do getting value of customid [000] of that specific tag.
what i did:
var doc = new XmlDocument();
doc.Load(stream); // stream from xml-file
var key = doc.SelectSingleNode(//key/[text()='" + 22452+ "']"); // that i am not sure about it.
var customId = key.InnerText("customId");
For this kind of query you could either find the node and than navigate to the parent.
Or use XPath:
var unitElemnt = doc.SelectSingleNode("//unit[key = '22452']");
(Assuming I've remembered the XPath to match an element's text content correctly.)
This gets a reference to the <unit> element, by using a relative path to the <key> element in the predicate of the XPath expression.
Generally better to avoid // in XPath for performance, but would need full document structure to do that.
For this you can use Linq to Xml queries.
XElement units = XElement.Load("./Test.xml");
XElement unit = (from item in units.Descendants("unit")
where item.Element("key").Value == "22455"
select item).FirstOrDefault();
string customId = unit.Element("customId").Value;
supposing your xml file look like :
<?xml version="1.0" encoding="utf-8"?>
<units>
<unit class="xxx.xxx.xxx" id="385">
<customId>003</customId>
<description>kg</description>
<key>22455</key>
<Description>Kilogramm</Description>
</unit>
<unit class="xxx.xxx.xxx" id="386">
<customId>004</customId>
<description>kg</description>
<key>22456</key>
<Description>Kilogramm</Description>
</unit>
</units>
for more reading check Microsoft Linq to Xml Docs
Have document loaded to XmlDocument with next srtucture
<?xml version="1.0" encoding="UTF-8"?>
<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink">
<stylesheet type="text/css"></stylesheet>
<description>...</description>
<body>...</body>
<binary id="19317.jpg" content-type="image/jpeg">...</binary>
</FictionBook>
Next metods return me null (or empty collection if i use SelectNodes):
doc.SelectSingleNode("body");
doc.SelectSingleNode("//body");
doc.LastChild.SelectSingleNode("body");
doc.LastChild.SelectSingleNode("//body");
But this one works correctly
doc.LastChild["body"];
Why XPath don't give me any results?
doc.SelectSingleNode("//body"); doesn't work because body is declared in a specific namespace "http://www.gribuser.ru/xml/fictionbook/2.0", so to query for it you could code it like this:
var mgr = new XmlNamespaceManager(new NameTable());
mgr.AddNamespace("whatever", "http://www.gribuser.ru/xml/fictionbook/2.0");
var node = doc.SelectSingleNode("//whatever:body", mgr);
doc.LastChild["body"]; works because the implementation supports it, but you could use it like this to avoid ambiguities:
doc.LastChild["body", "http://www.gribuser.ru/xml/fictionbook/2.0"]
First I want to find and select the PID"5678" from the <Tool>. With help of this PID, i want to find and select the ID"5678" from the <Parent>. The PID and the ID are the same value, but I have to find it from the <Tool> first.
At the moment I have following Code, to select the first PID. How can I "copy" this value and search with them the Attribute "ID"?
List<string> urls = xmldoc2.Descendants("PID").Select(x => x.Attribute("5678").Value).ToList();
<Tools>
<Tools>
<Tool>
<ID>1234</ID>
<PID>5678</PID>
<Name>Test</Name>
</Tool>
</Tools>
<Type>
<Parent>
<ID>5678</ID>
<PID>9999</PID>
<Name>Test2</Name>
</Parent>
</Type>
</Tools>
Notice that your Xml has multiple Root nodes - which does not work well.
So wrap it into single parent node (i.e. "Root" in below example)
Something of this sort should help you.
string xmlData = #"... Your Xml here....";
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlData);
var pidNodes = xmlDoc.SelectNodes("//Root/Tools/Tools/Tool/PID");
foreach(XmlNode node in pidNodes)
{
var typeNodeForPid = xmlDoc.SelectSingleNode(string.Format("//Root/Type/Parent[ID = '{0}']", node.InnerText));
}
I want to add an XML fragment to the last element to an XML document and I having problems i.e. the error I get is:
"The reference node is not a child of
this node".
So my existing XML document looks like this:
<MAP>
<LAYER name ="My first Layer">
<DATASET name="foo dataset" />
<SYMBOLOGY>
<SYMBOL colour="red" />
</SYMBOLOGY>
</LAYER>
<LAYER name="My second Layer">
<DATASET name="bar dataset" />
<SYMBOLOGY>
<SYMBOL colour="blue" />
</SYMBOLOGY>
</LAYER>
</MAP>
The XML fragment I want to insert after the last LAYER element is:
<LAYER name="My third Layer">
<DATASET name="whatever dataset" />
<SYMBOLOGY>
<SYMBOL colour="yellow" />
</SYMBOLOGY>
</LAYER>
The code I am using is:
XmlDocumentFragment xmlDocFrag = xmlDocument.CreateDocumentFragment();
xmlDocFrag.InnerXml = inputXML; //which is basically the third layer example - see above.
XmlElement rootElement = xmlDocument.DocumentElement;
XmlNode lastLayerNode = rootElement.SelectSingleNode(#"//LAYER[last()]");
rootElement.InsertAfter(xmlDocFrag, lastLayerNode); //error raised here.
Any ideas on what I'm doing wrong here would be much appreciated. My XPath query seems find and it seems to select the correct last layer it just won't insert after it for some bizarre reason.
UPDATE/SOLUTION - How to do this with XPATH
Finally figured it out in XPath - see the code below, I think it was down to basically not selecting the correct parent node in the first place, it's incorrect to select the last LAYER then try and InsertAfter() on this node. Better to select the level above i.e. MAP then AppendChild(). See below:
XmlDocumentFragment xmlDocFrag = xmlDocument.CreateDocumentFragment();
xmlDocFrag.InnerXml = inputXML;
XmlElement mapElement = (XmlElement)xmlDocument.SelectSingleNode(#"//MAP[last()]");
mapElement.AppendChild(xmlDocFrag);
Thanks to all the replies and help too :)
Taking into consideration that you need this to work with Framework 2.0, here's another solution:
string xml = "<map><layer>1</layer><layer>2</layer></map>";
string addMe = "<layer>3</layer>";
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
XmlDocumentFragment xmlDocFrag = xmlDocument.CreateDocumentFragment();
xmlDocFrag.InnerXml = addMe;
XmlElement rootElement = xmlDocument.DocumentElement;
rootElement.AppendChild(xmlDocFrag);
This results in:
<map><layer>1</layer><layer>2</layer><layer>3</layer></map>
Things look pretty good, but I would first try to avoid the xpath selection for the last node, and instead just use this:
rootElement.InsertAfter(xmlDocFrag, rootElement.LastChild);
I had similar issue, I used the ImportNode method to solve it
Here is a small example how you can use it to add node from different xml (stored in string) to your example at desired node in xml tree
string xmlstring =#"<tag>.....</tag>"; // holds xml tree to be appended
XmlDocument xml2 = new XmlDocument();
xml2.Load(#"path_of_main_xml");
XmlDocument xml1 = new XmlDocument();
xml1.Load(new StringReader(xmlString));
// get the node you want to import which in this icase is string
XmlNode elem = xml1.DocumentElement;
// use importNode to import it
XmlNode impnode = xml2.ImportNode(elem,true);
// get the node list of all node of particular tag name
XmlNodeList eNode = xml2.GetElementsByTagName("tag_name_of_parent");
eNode[0].AppendChild(impnode); // append new node
// write back the updates to same file
XmlWriter writer = XmlWriter.Create(#"path_of_main_xml");
xml2.Save(writer);
Why does running this code...
XmlDocument doc = new XmlDocument();
string xml = #"<?xml version=""1.0"" encoding=""utf-8"" ?>
<BaaBaa>
<BlackSheep HaveYouAny=""Wool"" />
</BaaBaa>";
doc.LoadXml(xml);
XmlNodeList nodes = doc.SelectNodes("//BaaBaa");
foreach (XmlElement element in nodes)
{
Console.WriteLine(element.InnerXml);
XmlAttributeCollection attributes = element.Attributes;
Console.WriteLine(attributes.Count);
}
Produce the following output in the command prompt?
<BlackSheep HaveYouAny="Wool" />
0
That is, shouldn't attributes.Count return 1?
When you call SelectNodes with "//BaaBaa" it returns all elements of "BaaBaa".
As you can see from your own document, BaaBaa has no attributes, it's the "BlackSheep" element that has the single attribute "HaveYouAny".
If you want to get the attribute count of child elements, you have to navigate to that from the node you are on when iterating through the nodes.
element.Attributes contains the attributes of the element itself, not its children.
Since the BaaBaa element doesn't have any attributes, it is empty.
The InnerXml property returns the XML of the element's contents, not of the element itself. Therefore, it does have an attribute.
<BlackSheep HaveYouAny=""Wool"" /> // innerXml that includes children
<BaaBaa> // is the only node Loaded, which has '0' attributes
solution
XmlAttributeCollection attributes = element.FirstChild.Attributes;
Will produce the following, required output
<BlackSheep HaveYouAny="Wool" />
1