I am learning C# and trying to flatten XML following this code from this post
var doc = XDocument.Load("test.xml");
XNamespace ns = "mynamespace";
var member = doc.Root.Element(ns + "member");
// This will *sort* of flatten, but create copies...
var descendants = member.Descendants().ToList();
// So we need to strip child elements from everywhere...
// (but only elements, not text nodes). The ToList() call
// materializes the query, so we're not removing while we're iterating.
foreach (var nested in descendants.Elements().ToList())
{
nested.Remove();
}
member.ReplaceNodes(descendants);
This is my XML (sorry I don't know how to post fancy code style)
<ApplicationExtraction>
<IsCurrent>Yes</IsCurrent>
<ApplicationDate>10/06/2015</ApplicationDate>
<Status>Application Received</Status>
<EquipmentType>Equipment</EquipmentType>
<IsLoan>No</IsLoan>
</ApplicationExtraction>
There is namespace so I changed var member = doc.Root.Element(ns + "member"); to var member = doc.Root.Element("ApplicationExtraction"); but this returns NULL.
I also try XElement sRoot = doc.Root.Element("ApplicationExtraction"); from this post I still get the same result.
I read up Microsoft XElement document but don't see how I can fix this.
What could I have done wrong?
XElement sRoot = doc.Root.Element("ApplicationExtraction");
will look for an element 'ApplicationExtraction' inside the Root.
If you want the Root, just reference
doc.Root
In your XML, doc.Root is the root node i.e. ApplicationExtraction and there is no node ApplicationExtraction inside the root node thus you are getting null.
To fetch any specific node you need(for example):-
XElement member = doc.Root.Element("IsCurrent");
and to fetch the value inside the node:-
string member = (string)doc.Root.Element("IsCurrent");
Related
I have an XML document containing processing instructions. I know that, with the XmlDocument class, you can use
var node = xmlDoc.SelectSingleNode("processing-instruction('xml-stylesheet')") as XmlProcessingInstruction;
but I want to use XDocument. How can I do this?
This is how I access an XML file's nodes with the XDocument class.
However, you'll have to be more specific on what you want to do with it.
XDocument doc = XDocument.Load("filepath");
var node = doc.Nodes().OfType<XElement>().SingleOrDefault(n => n.Name == "node name");
var node_value = node.Value;
var node_descendants = node.Descendants();
UPDATE:
As you may have noticed there's no SelectSingleNode in XDocument, in fact, to retrieve the node you want you'll have to fetch it from the corresponding ienumerable collection, or alternatively from the predefined FirstNode, NextNode, PreviousNode, LastNode, but you cannot apply any filters to those. Therefore the only ways to retrieve ProcessingInstruction nodes are
var pI_nodes = doc.Nodes().OfType<XProcessingInstruction>();
And
var pI_nodes = (from node in doc.Nodes()
where node.NodeType == System.Xml.XmlNodeType.ProcessingInstruction
select node);
If you expect to retrieve several ProcessingInstructions and need to filter these as well, the equivalent to the node name would the Target property
var filtered_pIs = pI_nodes_1.Where(pI => pI.Target == "xml-stylesheet");
And as a final reminder the value of the processing instruction is stored in the Data property.
string pI_value = filtered_pIs.First().Data
Here is one way:
var node = xDoc.Root.Nodes().OfType<XProcessingInstruction>().First();
I cant get my Xpath query to work though on paper it should be right. I even tried to get a single node without the attribute and could not even get this ...
What am I doing wrong ?
var trxXml = new XmlDocument();
trxXml.Load(InputTrxFile);
XmlElement root = trxXml.DocumentElement;
var unitTestResult = trxXml.GetElementsByTagName("UnitTestResult");
foreach (XmlElement runinfo in unitTestResult)
{
// Find failed tests, works fine then...
string TestName = runinfo.GetAttribute("testName"); // works fine
// Want to find equivalent TestDefinitions/UnitTest
/* Tried
TestRun/TestDefinitions/UnitTest[#name='thetest']
/TestRun/TestDefinitions/UnitTest[#name='thetest']
TestDefinitions/UnitTest[#name='thetest']
/TestDefinitions/UnitTest[#name='thetest']
UnitTest[#name='thetest']
variations with no attribute test JUST to get a node
Example http://www.csharp-examples.net/xml-nodes-by-attribute-value/
*/
var xpath = string.Format(#"/TestRun/TestDefinitions/UnitTest[#name='{0}']", TestName);
XmlNode node = trxXml.SelectSingleNode(xpath);
XmlNode node2 = root.SelectSingleNode(xpath);
// These all return null
Any query that begins with a / represents an absolute path, i.e. it is from the root of the document. It seems your UnitTestResult element (at the very least) encloses your TestRun elements.
To have your query take into account the current context, you need to reference the current context. This can be selected using ..
Secondly, your XML elements all have a namespace, and this needs to make up part of your query. A prefix needs to be added to a namespace manager, used in your query and the manager passed into the query method.
So, taking these together, define the prefix:
var manager = new XmlNamespaceManager(new NameTable());
manager.AddNamespace("t", "http://microsoft.com/schemas/VisualStudio/TeamTest/2010");
Change your query:
./t:TestRun/t:TestDefinitions/t:UnitTest[#name='{0}']
And pass the namespace manager to the method:
trxXml.SelectSingleNode(xpath, manager);
XPath and XmlDocument are pain, though. This would all be a lot more straightforward in LINQ to XML:
var doc = XDocument.Load(InputTrxFile);
var unitTestQuery =
from result in doc.Descendants(ns + "UnitTestResult")
let name = (string)result.Attribute("testName")
from unitTest in result.Descendants(ns + "UnitTest")
where (string)unitTest.Attribute("name") == name
select unitTest;
var unitTest = unitTestQuery.Single();
I try to get some data from Google Geocoding API with C# and ASP.net. But I have trouble with XML responce. I get valid XML and I can get evry element besides "address_component" element. (XML looks like this : http://maps.googleapis.com/...)
/* data is a string with XML from Google server */
XDocument receivedXml = new XDocument();
receivedXml = XDocument.Parse(data);
XName address = XName.Get("address_component");
var root = reciviedXml.Root; //returns corect XElement
XElement result = root.Element("result"); //returns corect XElement
IEnumerable<XElement> components = result.Elements("address_component"); //returns empty collection
This is another way, I'have tried it to with the same result.
var results = reciviedXml.Descendants("address_component");
And when I try to get some descendant of like:
var types = receivedXml.Descendants("type");
It's the empty collection to. But another elements in "result" tag (like the "location" tag)I can get successfully.
Thanks for any advice.
The following:
var receivedXml = XDocument.Load("http://maps.googleapis.com/maps/api/geocode/xml?latlng=49.1962253,16.6071422&sensor=false");
Console.WriteLine(receivedXml.Root.Element("result").Elements("address_component").Count());
Console.WriteLine(receivedXml.Descendants("address_component").Count());
Writes 10 and 28 respectively. Make sure you are using the same instance - in your question you usereceivedXml as well as reciviedXml so you may have two instances XDocument and one of these may contain different data. Also you don't need to instantiate XDocument as XDocument.Parse() will do it for you (and the address variable seems to be unused)
I have a simple XML
<AllBands>
<Band>
<Beatles ID="1234" started="1962">greatest Band<![CDATA[lalala]]></Beatles>
<Last>1</Last>
<Salary>2</Salary>
</Band>
<Band>
<Doors ID="222" started="1968">regular Band<![CDATA[lalala]]></Doors>
<Last>1</Last>
<Salary>2</Salary>
</Band>
</AllBands>
However ,
when I want to reach the "Doors band" and to change its ID :
using (var stream = new StringReader(result))
{
XDocument xmlFile = XDocument.Load(stream);
var query = from c in xmlFile.Elements("Band")
select c;
...
query has no results
But
If I write xmlFile.Elements().Elements("Band") so it Does find it.
What is the problem ?
Is the full path from the Root needed ?
And if so , Why did it work without specify AllBands ?
Does the XDocument Navigation require me to know the full level structure down to the required element ?
Elements() will only check direct children - which in the first case is the root element, in the second case children of the root element, hence you get a match in the second case. If you just want any matching descendant use Descendants() instead:
var query = from c in xmlFile.Descendants("Band") select c;
Also I would suggest you re-structure your Xml: The band name should be an attribute or element value, not the element name itself - this makes querying (and schema validation for that matter) much harder, i.e. something like this:
<Band>
<BandProperties Name ="Doors" ID="222" started="1968" />
<Description>regular Band<![CDATA[lalala]]></Description>
<Last>1</Last>
<Salary>2</Salary>
</Band>
You can do it this way:
xml.Descendants().SingleOrDefault(p => p.Name.LocalName == "Name of the node to find")
where xml is a XDocument.
Be aware that the property Name returns an object that has a LocalName and a Namespace. That's why you have to use Name.LocalName if you want to compare by name.
You should use Root to refer to the root element:
xmlFile.Root.Elements("Band")
If you want to find elements anywhere in the document use Descendants instead:
xmlFile.Descendants("Band")
The problem is that Elements only takes the direct child elements of whatever you call it on. If you want all descendants, use the Descendants method:
var query = from c in xmlFile.Descendants("Band")
My experience when working with large & complicated XML files is that sometimes neither Elements nor Descendants seem to work in retrieving a specific Element (and I still do not know why).
In such cases, I found that a much safer option is to manually search for the Element, as described by the following MSDN post:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/3d457c3b-292c-49e1-9fd4-9b6a950f9010/how-to-get-tag-name-of-xml-by-using-xdocument?forum=csharpgeneral
In short, you can create a GetElement function:
private XElement GetElement(XDocument doc,string elementName)
{
foreach (XNode node in doc.DescendantNodes())
{
if (node is XElement)
{
XElement element = (XElement)node;
if (element.Name.LocalName.Equals(elementName))
return element;
}
}
return null;
}
Which you can then call like this:
XElement element = GetElement(doc,"Band");
Note that this will return null if no matching element is found.
The Elements() method returns an IEnumerable<XElement> containing all child elements of the current node. For an XDocument, that collection only contains the Root element. Therefore the following is required:
var query = from c in xmlFile.Root.Elements("Band")
select c;
Sebastian's answer was the only answer that worked for me while examining a xaml document. If, like me, you'd like a list of all the elements then the method would look a lot like Sebastian's answer above but just returning a list...
private static List<XElement> GetElements(XDocument doc, string elementName)
{
List<XElement> elements = new List<XElement>();
foreach (XNode node in doc.DescendantNodes())
{
if (node is XElement)
{
XElement element = (XElement)node;
if (element.Name.LocalName.Equals(elementName))
elements.Add(element);
}
}
return elements;
}
Call it thus:
var elements = GetElements(xamlFile, "Band");
or in the case of my xaml doc where I wanted all the TextBlocks, call it thus:
var elements = GetElements(xamlFile, "TextBlock");
How to get a value of XElement without getting child elements?
An example:
<?xml version="1.0" ?>
<someNode>
someValue
<child>1</child>
<child>2</child>
</someNode>
If i use XElement.Value for <someNode> I get "somevalue<child>1</child><child>2<child>" string but I want to get only "somevalue" without "<child>1</child><child>2<child>" substring.
You can do it slightly more simply than using Descendants - the Nodes method only returns the direct child nodes:
XElement element = XElement.Parse(
#"<someNode>somevalue<child>1</child><child>2</child></someNode>");
var firstTextValue = element.Nodes().OfType<XText>().First().Value;
Note that this will work even in the case where the child elements came before the text node, like this:
XElement element = XElement.Parse(
#"<someNode><child>1</child><child>2</child>some value</someNode>");
var firstTextValue = element.Nodes().OfType<XText>().First().Value;
There is no direct way. You'll have to iterate and select. For instance:
var doc = XDocument.Parse(
#"<someNode>somevalue<child>1</child><child>2</child></someNode>");
var textNodes = from node in doc.DescendantNodes()
where node is XText
select (XText)node;
foreach (var textNode in textNodes)
{
Console.WriteLine(textNode.Value);
}
I think what you want would be the first descendant node, so something like:
var value = XElement.Descendents.First().Value;
Where XElement is the element representing your <someNode> element.
You can specifically ask for the first text element (which is "somevalue"), so you could also do:
var value = XElement.Descendents.OfType<XText>().First().Value;