Select a subset of childnodes by name - c#

Given this xml doc
<listOfItem>
<Item id="1">
<attribute1 type="foo"/>
<attribute2 type="bar"/>
<property type="x"/>
<property type="y"/>
<attribute3 type="z"/>
</Item>
<Item>
//... same child nodes
</Item>
//.... other Items
</listOfItems>
Given this xml document, I would like to select, for each "Item" node, just the "property" child nodes. How can I do it in c# directly? With "directly" I mean without selecting all the child nodes of Item and then check one by one. So far:
XmlNodeList nodes = xmldoc.GetElementsByTagName("Item");
foreach(XmlNode node in nodes)
{
doSomething()
foreach(XmlNode child in node.ChildNodes)
{
if(child.Name == "property")
{
doSomethingElse()
}
}
}

You can use SelectNodes(xpath) method instead of ChildNodes property:
foreach(XmlNode child in node.SelectNodes("property"))
{
doSomethingElse()
}
Demo.

Try using LINQ to XML instead of XML DOM as it's much simpler syntax for what you want to do.
XDocument doc = XDocument.Load(filename);
foreach (var itemElement in doc.Element("listOfItems").Elements("Item"))
{
var properties = itemElement.Elements("property").ToList();
}

Related

Empty Child and another child attribute vlaues

Hi All: Could you please tell me what is the mistake in gettting the child & child2 attribute values?
I'm getting the valu of the node attribute but not for the childs nodes.
Overall i need to get Name value from products, Visibile value from the element Visibilities, and Price value from elment Prices.
Code:
XmlDocument doc = new XmlDocument();
doc.Load("Prices.txt");
XmlNodeList nodes = doc.GetElementsByTagName("RetroPrintProduct");
foreach (XmlNode node in nodes)
{
Console.WriteLine("Name={0} ", node.Attributes["Name"].Value);
foreach (XmlNode child in node.SelectNodes("ProductVisibility "))
{
Console.WriteLine("Visible={0}", child.Attributes["Visible"].Value);
foreach (XmlNode child2 in child.SelectNodes("Prices"))
{
Console.WriteLine("Price={0}", child2.Attributes["Price"].Value);
}
}
}
XML file:
<Item>
<RetroPrintProduct Nino123="d89e280b-c8d5-4da2-87da-36e4a57e2867" Nino1="2022ProfileProducts.RetroPrintProduct" Sys_Index="UpdateOnly" DefaultIconName="RetroPrints_10x8x2_R_Metallic.png" DefaultOutputProfileName="Nino" DefaultOutputProfileTypeFullName="2022Profile" Name="Item Retro">
<ShippingMethodPrices Index="Replace" />
<Visibilities Index="Replace" />
</RetroPrintProduct>
<RetroPrintProduct Nino123="67d1577d-7baf-4b3f-a9fb-9d52404e45f4" Nino1="2022ProfileProducts.RetroPrintProduct" Sys_Index="UpdateOnly" DefaultIconName="RetroPrints_10x8x2_S_Normal.png" DefaultOutputProfileName="Nino" DefaultOutputProfileTypeFullName="2022Profile" Name="Item 2 Retro">
<ShippingMethodPrices Index="Replace" />
<Visibilities Index="Replace">
<ProductVisibility Nino123="cc0096e0-d964-4e45-93f7-9258ddee148d" Sys_GlobalUniqueId="cc0096e0-d964-4e45-93f7-9258ddee148d" Sys_ReplicationId="39f43856-a16f-4555-b519-ccf71b97ee58" Nino1="2022.ProductVisibility" Activated="True" Noprices="False" PhotoSource="EndUserPhotos" BackgroundColor="Default" Icon="" Image="" IsUnusableByLicense="False" MaxDate="2999-12-31" MinDate="1900-01-01" Name="" Object="Visible" Visible="True" ProductLibrary="" ReplicationId="39f43856-a16f-4555-b519-ccf71b97ee58" SysCode="">
<Prices Index="Replace">
<ProductPrice Nino123="4ca2658e-3e07-4636-b33f-d87fb021288a" Sys_GlobalUniqueId="4ca2658e-3e07-4636-b33f-d87fb021288a" Sys_ReplicationId="3e75b8f4-d1b6-41fe-be9e-d2858caf6eb9" Nino1="2022.ProductPrice" FixFee="0" ServiceFee="0" Mode="Replace" FromQuantity="1" Price="0.5" ProductPriceType="PerPageQuantity" ProductLibrary="" ReplicationId="3e75b8f4-d1b6-41fe-be9e-d2858caf6eb9" SysCode="" />
</Prices>
</ProductVisibility>
</Visibilities>
</RetroPrintProduct>
</Item>
It looks like you're missing some nesting when searching through the nodes. ProductVisibility is under Visibilites, not RetroPrintProduct, and Price is an attribute of ProductPrice, not Prices.
Something like this should do the trick (note that I named the nodes in code to match the xml names to make it easier to remember which child is which):
XmlDocument doc = new XmlDocument();
doc.Load(#"c:\temp\Prices.xml");
XmlNodeList retroPrintProducts = doc.GetElementsByTagName("RetroPrintProduct");
foreach (XmlNode retroPrintProduct in retroPrintProducts)
{
Console.WriteLine("Name={0} ", retroPrintProduct.Attributes["Name"].Value);
foreach (XmlNode visibility in retroPrintProduct.SelectNodes("Visibilities"))
{
foreach (XmlNode productVisibilty in visibility.SelectNodes("ProductVisibility"))
{
Console.WriteLine("Visible={0}", productVisibilty.Attributes["Visible"].Value);
foreach (XmlNode price in productVisibilty.SelectNodes("Prices"))
{
foreach (XmlNode productPrice in price.SelectNodes("ProductPrice"))
{
Console.WriteLine("Price={0}", productPrice.Attributes["Price"].Value);
}
}
}
}
}

C# XML to dropdownBox

Returned XML from URL:
<root>
<APIVersion>0.1</APIVersion>
<resource>persons</resource>
<search>givenname</search>
<query>andreas</query>
<limit>400</limit>
<results>
<item>
<persons>
<personId>21168</personId>
<givenName>Andreas</givenName>
<familyName>Garpe</familyName>
<email>andreas.garpe#t-fk.no</email>
<mobilePhone/>
<workPhone/>
<positions>...</positions>
</persons>
</item>
<item>...</item>
<item>...</item>
<item>...</item>
</results>
</root>
(Keep in mind that "item" is the objects with personnel info.)
I have a textbox defined as bunifuTextbox1.
I input a name, and it returns the names from the returned XML result and put all the names returned into a dropdown box.
private void button1_Click(object sender, EventArgs e)
{
string address = "http://ws.t-fk.no/?resource=persons&search=givenname&string=" + bunifuTextbox1.text;
XmlDocument doc1 = new XmlDocument();
doc1.Load(address);
XmlElement root = doc1.DocumentElement;
XmlNodeList nodes = root.SelectNodes("/results/item");
foreach (XmlNode node in nodes)
{
string tempf = node["persons"]["givenName"].InnerText;
bunifuDropdown1.AddItem(tempf);
}
}
I'm not sure why this doesen't work. Any help?
Your XPath is incorrect.
Instead of
XmlNodeList nodes = root.SelectNodes("/results/item");
try
XmlNodeList nodes = root.SelectNodes("results/item");
or
XmlNodeList nodes = root.SelectNodes("./results/item");
or
XmlNodeList nodes = root.SelectNodes("//results/item");
Use "results/item" or "./results/item" for an item element that is a child of a results element that is a child of the root node.
Using "//results/item" will select item elements that are children of results elements where the results element is anywhere in the XML.

read child nodes xml

Here is my xml which i am trying to read.
<VacancyList xmlns="urn:abc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" generated="2016-04-20T11:42:47" xsi:schemaLocation="http://www.abc.in/dtd/vacancy-list.xsd">
<Vacancy id="1619993" date_start="2016-04-15" date_end="2016-04-22" reference_number="">
<Versions>
<Version language="nb">
<Title>Marketing Specialist</Title>
<TitleHeading/>
<Location>CXCXC</Location>
<Engagement/>
<DailyHours/>
<Region>
<County id="11">sds</County>
<County id="1">zxzx</County>
</Country>
</Region>
<Categories>
<Item type="position-type" id="3909">sER</Item>
<Item type="duration" id="contract">ss</Item>
<Item type="extent" id="fulltime">sd</Item>
<Item type="operating-time" id="day">s</Item>
</Categories>
</Version>
</Versions>
</Vacancy>
</VacancyList>
I want to read node location so wrote below code
XmlDocument xd = new XmlDocument();
xd.Load("https://abc.in/list.xml");
XmlNamespaceManager ns = new XmlNamespaceManager(xd.NameTable);
ns.AddNamespace("msbld", "urn:abc");
XmlNodeList nodelist = xd.SelectNodes("//msbld:VacancyList", ns);
if (nodelist != null)
foreach (XmlNode node in nodelist)
{
XmlNode nodelist1 = node.SelectSingleNode("Vacancy");
if (nodelist1 != null)
foreach (XmlNode node1 in nodelist1)
{
var k = node1.Attributes.GetNamedItem("Location").Value;
}
}
But i dont get anything in variable "node1". How to fix this?
Also is there any better solution for this?
Update1
i modified code but i only get node Title. cant get others inside Version node like Location.
if (nodelist != null)
foreach (XmlNode node in nodelist)
{
XmlNode nodelist1 = node.SelectSingleNode("//msbld:Vacancy/msbld:Versions",ns);
if (nodelist1 != null) {
XmlNode nodelist2 = nodelist1.SelectSingleNode("//msbld:Version", ns);
foreach (XmlNode node3Node in nodelist2)
{
var k = node3Node.Attributes.GetNamedItem("Location").Value;
}
}
}
xmlns="urn:abc" is a default namespace. Notice that descendant elements without prefix inherits ancestor's default namespace implicitly. You need to use the same prefix that references default namespace URI for acessing Vacancy and Location as well :
XmlNode nodelist1 = node.SelectSingleNode("msbld:Vacancy", ns);
Your updated code introduces an entirely different problem; / at the beginning of a path expression will always reference document element, unless you explicitly set the context to current active context by using . before /, for example :
XmlNode nodelist1 = node.SelectSingleNode(".//msbld:Vacancy/msbld:Versions",ns);
If you only need the Location element then you can do it like this:
var doc = XElement.Load("path/to/file");
var location = doc.Descendants
.FirstOrDefault(e => e.Name.LocalName == "Location"));

Select Parent XML(Entire Hierarchy) Elements based on Child element values LINQ

I have the following XML and query through the ID,how do get the Parent Hierarchy
<Child>
<Child1 Id="1">
<Child2 Id="2">
<Child3 Id="3">
<Child4 Id="4">
<Child5 Id="5"/>
<Child6 Id="6"/>
</Child4>
</Child3>
</Child2>
</Child1>
</Child>
In this if i query(Id = 4) and find out the Parent elements using Linq in the particular element how to get the following output with Hierarchy.
<Child>
<Child1 Id="1">
<Child2 Id="2">
<Child3 Id="3">
<Child4 Id="4"/>
</Child3>
</Child2>
</Child1>
</Child>
Thanks In Advance.
Assume you want just one node parent tree:
string xml = #"<Child>
<Child1 Id="1">
<Child2 Id="2">
<Child3 Id="3">
<Child4 Id="4">
<Child5 Id="5"/>
<Child6 Id="6"/>
</Child4>
</Child3>
</Child2>
</Child1>
</Child>";
TextReader tr = new StringReader(xml);
XDocument doc = XDocument.Load(tr);
IEnumerable<XElement> myList =
from el in doc.Descendants()
where (string)el.Attribute("Id") == "4" // here whatever you want
select el;
// select your hero element in some way
XElement hero = myList.FirstOrDefault();
foreach (XElement ancestor in hero.Ancestors())
{
Console.WriteLine(ancestor.Name); // rebuild your tree in a separate document, I print ;)
}
To search for every element of your tree iterate retrieve the node with the select query without the where clause and call the foreach for every element.
Based on the sample XML provided, you could walk up the tree to find the parent node once you've found the node in question:
string xml =
#"<Child>
<Child1 Id='1'>
<Child2 Id='2'>
<Child3 Id='3'>
<Child4 Id='4'>
<Child5 Id='5'/>
<Child6 Id='6'/>
</Child4>
</Child3>
</Child2>
</Child1>
</Child>";
var doc = XDocument.Parse( xml );
// assumes there will always be an Id attribute for each node
// and there will be an Id with a value of 4
// otherwise an exception will be thrown.
XElement el = doc.Root.Descendants().First( x => x.Attribute( "Id" ).Value == "4" );
// discared all child nodes
el.RemoveNodes();
// walk up the tree to find the parent; when the
// parent is null, then the current node is the
// top most parent.
while( true )
{
if( el.Parent == null )
{
break;
}
el = el.Parent;
}
In Linq to XML there is a method called AncestorsAndSelf on XElement that
Returns a collection of elements that contain this element, and the
ancestors of this element.
But it will not transform your XML tree the way you want it.
What you want is:
For a given element, find the parent
Remove all elements from parent but the given element
Remove all elements from the given element
Something like this in Linq (no error handling):
XDocument doc = XDocument.Parse("<xml content>");
//finding element having 4 as ID for example
XElement el = doc.Descendants().First(el => el.Attribute("Id").Value == "4");
el.RemoveNodes();
XElement parent = el.Parent;
parent.RemoveNodes();
parent.Add(el);
[Edit]
doc.ToString() must give you what you want as a string.
[Edit]
Using RemoveNodes instead of RemoveAll, the last one also removes attributes.
Removing nodes from the chosen element too.
I found the following way
XElement elementNode = element.Descendants()
.FirstOrDefault(id => id.Attribute("id").Value == "4");
elementNode.RemoveNodes();
while (elementNode.Parent != null)
{
XElement lastNode = new XElement(elementNode);
elementNode = elementNode.Parent;
elementNode.RemoveNodes();
elementNode.DescendantsAndSelf().Last().AddFirst(lastNode);
}
return or Print elementNode.

Parsing XmlDocument with Variable Parent Tags

Assuming I have an XmlDocument like this:
<xmlFile>
<details>
<code1>ADJ</code1>
<code2>ADC </code2>
<Shipment>
<foo></foo>
<bar></bar>
</Shipment>
<Shipment>
<foo></foo>
<bar></bar>
</Shipment>
</details>
<details>
<code1>ADJ</code1>
<code2>SCC </code2>
<Shipment>
<foo></foo>
<bar></bar>
</Shipment>
</details>
</xmlFile>
I need to process each in an xml file but only shipments that fall under the tags with a child node with a value of "ADC". So far I have:
// Assume there is an XmlDocument named xml
XmlNodeList details= xml.GetElementsByTagName("details");
foreach (XmlNode node in details)
{
if (node["code2"].InnerText == "ADC ")
{
// Get each shipment and process it accordingly.
}
}
I can't figure out what to do next. Thanks.
Assuming Data\Sample.xml contains xml as mentioned in the question,
Following is the XLINQ query
XElement root = XElement.Parse(File.ReadAllText(#"Data\Sample.xml"));
var adcShipment = root.Descendants().Where(e=>String.Equals(e.Value, "ADC "));
//next query for nodes/elements inside/next to ADC shipments
XPath can simplify your search for matches:
foreach (XmlNode node in xml.SelectNodes("/xmlFile/details[normalize-space(code2)='ADC']"))
{
string foo = node.SelectSingleNode("foo").InnerText;
string bar = node.SelectSingleNode("bar").InnerText;
}
I'm in the process of adding XPath parsing to this library: https://github.com/ChuckSavage/XmlLib/
I modified it so you can do this:
XElement root = XElement.Load(file);
var shipments = root.XPath("details[starts-with(*,'ADC')]/Shipment");
Long-hand that looks like:
var shipments = root.Elements("details")
.Where(x => x.Elements().Any(xx => ((string)xx).StartsWith("ADC")))
.Elements("Shipment");
This is the sort of thing you're after
XmlNodeList details = xml.GetElementsByTagName("details");
foreach (XmlNode node in details)
{
if (node["code2"].InnerText.Trim() == "ADC")
{
// Get each shipment and process it accordingly.
foreach(XmlNode shipment in node.SelectNodes("Shipment"))
{
var foo = shipment.SelectSingleNode("foo");
var bar = shipment.SelectSingleNode("bar");
}
}
}

Categories

Resources