Check if XElement(Record) exists? - c#

How can I check if XElement has Record & INVID attributes ?
My function returns only single XElement. i.e,
<INVENTORY>
<Record>
<INVID>1315</INVID>
<INVNAME>TEST LOCATIONTEST</INVNAME>
<HOSPNAME>TEST LOCATION</HOSPNAME>
<INVTYPE>CLINICAL</INVTYPE>
<INVDT>2013-09-30T09:30:00</INVDT>
<INVDEF>YES</INVDEF>
<INVACT>YES</INVACT>
<UPDDTTM />
<UPDUSR />
<ENBREF>true</ENBREF>
<INVPWD>101315</INVPWD>
</Record>
</INVENTORY>
XElement xInventory = GetDefaultInventory();
bool hasInventory = xInventory.Elements("INVID").Any(); //What to do here ?
if (hasInventory)
{
//TO DO Some action
}

If you want to check that inventory element has Record element with INVID child element, then you can use XPath:
XElement inventory = GetDefaultInventory();
XElement invid = inventory.XPathSelectElement("Record/INVID");
if (invid == null)
// not exist
Or LINQ way:
bool exists = inventory.Elements("Record").Elements("INVID").Any();

How about this :
bool hasInventory = xInventory.Elements("Record").Any() && xInventory.Descendants("INVID").Any();
That will check if xInventory element has direct child Record, and if it has then check if xInventory has child element INVID.

Related

Nested descendants not found Linq XElement

I'm trying to receive all elements with a given tag name, no matter where they are.
I have used the Descendants() method on my base element like this:
public static XElement GetModifiedDataSource(XElement rechnung, string parentElement, string newElementTag, string value = null)
{
foreach (var element in rechnung.Descendants(parentElement))
{
XElement newElement = new XElement(newElementTag);
if (value != null)
{
newElement.SetValue(value);
}
element.Add(newElement);
}
return rechnung;
}
For examlpe with string parentElement = "Produkt" I should receive multiple.
Sample:
<Schlussrechnung xmlns="http://someurl">
<Parameter>
<Version></Version>
</Parameter>
<Uebersicht>
<Kopf>
<Rechnungsempfaenger>
</Rechnungsempfaenger>
</Kopf>
<Detail>
</Detail>
</Uebersicht>
<AbrechnungsDetail>
<Messpunkt>
<Produktgruppe>
<Produkt>
HERE
</Produkt>
<Produkt>
AND HERE
</Produkt>
</Produktgruppe>
</Messpunkt>
</Schlussrechnung>
Steps to accomplish this :
Get all descendant elements in the element under rechnung XElement like
var descendants = rechnung.Descendants().
Get all Descendants with "LocalName" = "Produkt" using linq like
var getAllProdukt = descendants.ToList().Where(desc => desc.Name.LocalName == "Produkt").ToList();
This way you get a List of XElements with the tag "Produkt"
Your XML has default namespace which your target element inherits from the root element:
xmlns="http://someurl"
You can use combination of XNamespace and element's local-name to reference element in namespace:
// you can make `ns` as additional parameter of `GetModifiedDataSource`
XNamespace ns = "http://someurl";
foreach (var element in rechnung.Descendants(ns+parentElement))
{
....
}
Or, with the risk of getting element from the wrong namespace if any, you can ignore the namespace by looking only at the element's local-name:
foreach (var element in rechnung.Descendants().Where(o => o.Name.LocalName == parentElement)
{
....
}

Extracting XML values from two nodes

I want to extract the value in the moduleId attibute and the value from the Field node. For example, in this first node I want to extract the 447 in the moduleId and the 124694 from the Field node. I have the XML loaded in an XDocument. The end result will be a Tuple where the first item is the value from the moduleId attribute and the second item is the value from the Field node. Is there a way I can do this using one XLinq statement?
As a bonus...I only want to do it for nodes where the guid = "07a188d3-3f8c-4832-8118-f3353cdd1b73". This part I can probably figure out if someone can tell me how to extract information from two nodes, but a bonus would be to add the WHERE clause in there for me :)
<Records>
<Record moduleId="447">
<Field guid="07a188d3-3f8c-4832-8118-f3353cdd1b73">124694</Field>
</Record>
<Record moduleId="447">
<Field guid="07a188d3-3f8c-4832-8118-f3353cdd1b73">124699</Field>
</Record>
<Records>
I have gotten as far as extracting the Field value using this...
IEnumerable<string> c = from p in sourceDocument.Descendants("Field")
where p.Attribute("guid").Value == "07a188d3-3f8c-4832-8118-f3353cdd1b73"
select p.Value;
But I have no idea how to get information from both the Record node and the Field node.
Give this a try:
var doc = XDocument.Parse(xml);
var r = doc.Descendants("Record")
.Where(n => n.Element("Field").Attribute("guid").Value == "07a188d3-3f8c-4832-8118-f3353cdd1b73")
.Select(n => new { ModuleId = n.Attribute("moduleId").Value, Field = n.Element("Field").Value });
var a = r.ToArray();
Here is a solution that uses LINQ Query Syntax:
XDocument document = XDocument.Parse(xml);
var query =
from el in document.Root.Elements("Record")
where (string)el.Element("Field").Attribute("guid") ==
"07a188d3-3f8c-4832-8118-f3353cdd1b73"
select new
{
ModuleId = (string)el.Attribute("moduleId"),
Field = (string)el.Element("Field")
};
foreach (var item in query)
{
Console.WriteLine
("ModuleId:[{0}]\nField:[{1}]\n--",
item.ModuleId,
item.Field);
}

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.

How do I programmatically determine if an XML node is a leaf node?

I have a big XML tree like the following:
<CategoryArray>
<Category Name="Antiques" ID="20081">
<Category Name="Antiquities" ID="37903">
<Category Name="The Americas" ID="37908" />
<Category Name="Byzantine" ID="162922" />
<Category Name="Celtic" ID="162923" />
<Category Name="Egyptian" ID="37905" />
...
I'd like to iterate through all nodes to populate a control and, when doing so, check to see: is this node a leaft of a parent node? What is the easiest way to do this?
A leaf node is one that has no children so you can simply perform a check if it has children. There are various ways of doing this depending on how you're loading the XML document. For example, you can use the HasChildNodes property.
if (myXmlNode.HasChildNodes)
//is not a leaf
else
//is a leaf
Number of child nodes will give you the answer - 0 child nodes (or only text child node, depending on classes/queries you use) means it is leaf.
I.e. XElement sample form MSDN: Find a List of Child Elements
XDocument cpo = XDocument.Load("PurchaseOrders.xml");
XElement po = cpo.Root.Element("PurchaseOrder").Element("Address");
// list1 contains all children of PurchaseOrder using LINQ to XML query
IEnumerable<XElement> list1 = po.Elements();
// list2 contains all children of PurchaseOrder using XPath expression
IEnumerable<XElement> list2 = po.XPathSelectElements("./*");
I would first flatten the hierarchy - e.g. using the code from this post
How do I select recursive nested entities using LINQ to Entity
And then something like this...
using (XmlReader reader = XmlReader.Create(new StringReader(this.XML)))
{
XElement xml = XElement.Load(reader);
var all = xml.Elements("Category").Flatten(x => x.Elements("Category"));
var leafs = from cat in all
where cat.Elements("Category").Any() == false
select cat;
// or go through all...
var categories =
from cat in all
select new
{
Name = cat.Attribute("Name"),
ID = cat.Attribute("ID"),
IsLeaf = cat.Elements("Category").Any() == false,
SubCount = cat.Elements("Category").Count(),
// Subs = cat.Elements("Category").Select(x => x.Attribute("Name").ToString()).ToArray(),
};
// or put into dictionary etc.
var hash = categories.ToDictionary(x => x.Name);
}

Find element with specific attribute in xml?

I have the following xml:
<?xml version="1.0" encoding="utf-8" ?>
<layout>
<menu name="Employees" url="Employees.aspx" admin="0">
</menu>
<menu name="Projects" url="Projects.aspx" admin="1">
</menu>
<menu name="Cases" url="Cases.aspx" admin="1">
</menu>
<menu name="CaseView" url="CaseView.aspx" admin="1" hidden="1" parent="Projects">
</menu>
<menu name="Management" url="" admin="1">
<item name="Groups" url="Groups.aspx" admin="1" parent="Management"/>
<item name="Statuses" url="Statuses.aspx" admin="1"/>
</menu>
</layout>
Here I have CaseView and Groups that both have a 'parent' attribute.
Currently I iterate like this:
IEnumerable<XElement> menus =
doc.Element("layout").Elements();
foreach (var menu in menus)
{
string name = menu.Attribute("name").Value;
string active = "";
string url = menu.Attribute("url").Value;
if(activePage == url)
{
active = "class=\"active\"";
}
...
What I want is:
if(activePage == url || ActiveIsChildOf(name, activePage))
{
active = "class=\"active\"";
}
Essentially this method needs to find if an element with activePage as its url attribute exists. If it does, see if it has a parent attribute; if it does, check if the parent == name.
Is there some way to find an element by attribute or something?
ex:
XElement e = doc.GetByAttribute("url",activePage)
Thanks
Since you are using Linq to XML, you can use Descendants method - it returns all child elements, not just the direct children. After that, you can use LINQ to filter the results.
XDocument doc;
string activePage;
var activeMenu = doc.Descendants("menu")
.FirstOrDefault(o => o.Attribute("url").Value == activePage);
You might need to check if o.Attribute("url") does not return null (it does when the attribute does not exist) if you cannot guarantee that the source XML does not have such attribute for all menu elements.
You can also skip the argument to Descendants() to check all elements - in your sample data that would allow you to check both menu and item elements. For example:
var activeMenu = doc.Descendants()
.Where(o => o.Name == "menu" || o.Name == "item")
.FirstOrDefault(o => o.Attribute("url").Value == activePage);
If xpath is too cryptic, you can use LINQ:
IEnumerable<XElement> hits =
(from el in XMLDoc.root.Elements("item")
where (string)el.Attribute("url") == activePage
select el);
or like this:
XElement xml = XElement.Load(file);
XElement xele = xml.Elements("item").FirstOrDefault(e => ((string)e.Attribute("url")) == activePage);
if(null != xele )
{
// do something with it
}
And you probably want it case-insensitive:
XElement xml = XElement.Load(file);
XElement xele = xml.Elements("item").FirstOrDefault(e => StringComparer.OrdinalIgnoreCase.Equals((string)e.Attribute("url") , activePage));
if(null != xele )
{
// do something with it
}
If you want both menu and item, use this:
XElement xml = XElement.Load(file);
XElement xele = xml.Elements().FirstOrDefault(e => StringComparer.OrdinalIgnoreCase.Equals((string)e.Attribute("url") , activePage));
if(null != xele )
{
// do something with it
}
You can simply use xPath. It's a query language for XML.
You can formulate something like this :
var xDoc = new XmlDocument();
xDoc.Load("XmlFile.xml");
//Fetch your node here
XmlNode = xDoc.SelectSingleNode(/layout/menu[#url='activepage'][1]);
It returns a set of node and the index 1 is to get the first node of the given set.
You can always use xDoc.SelectNodes if you want all the matching nodes.
Since you are using LINQ you can simply include System.Xml.XPath and select nodes with XPathSelectElement or XPathSelectElements.
You can do that with XPath:
doc.SelectNodes("//*[#url='" + activePage + "']")
It will return all document items that have activePage as url attribute.
A case insensitive search example, converting xml to a dictionary:
Dim expandos = XDocument.Parse(Request("Xml")).Root.Elements.Select(
Function(e)
Dim expando As Object = New ExpandoObject,
dic = e.Attributes.ToDictionary(Function(a) a.Name.LocalName, Function(a) a.Value,
StringComparer.InvariantCultureIgnoreCase)
expando.PedidoId = dic("PedidoId")
expando.FichaTecnicaModeloId = dic("FichaTecnicaModeloId")
expando.Comodo = dic("Comodo")
expando.Cliente = dic("Cliente")
Return expando
End Function)

Categories

Resources