Confused about linq this query - c#

I have an xml file like this.
<Accounts>
<Account Id="">
<UserName/>
<Password/>
<AddingDate/>
<AccountType/>
</Account>
</Accounts>
What I want to do is that if the first element's attaribute value is not empty return true otherwise false
private bool IsListEmpty(){
XDocument doc = XDocument.Load("UserAccounts.xml");
var c = from p in doc.Descendants("Account")
where p.Element("Id").Value == ""
select p.Value;
if(c==null)return......
}
But I am stuck.How can I do this

Just use FirstOrDefault() on your LINQ and then check if Id attribute is empty:
var account = doc.Descendants("Account").FirstOrDefault();
return account != null && !string.IsNullOrEmpty(account.Attribute("Id").Value)

Not sure if I understand correctly but I think that .Any would do the trick.
XElement xelement = XElement.Load("UserAccounts.xml");
return xelement.Elements("Account").Where(x => string.IsNullOrEmpty((string)x.Element("Id"))).Any();
Any will return true if one element is found that is ""

Related

IEnumerable<XAttribute> is returning Null, Needs to Return Attribute Value

This is an example xml I just need the Nid attribute
<Server>
<Network Nid="43d5377-0dcd-40e6-b95c-8ee980b1e248">
<Client_Group id="20">963440d0-96dc-46a4-a54d-7251a65f585f</Client_Group>
<ClientID id="20">3fc8ffa1-c16b-4d7b-9e55-1e88dfe15277</ClientID>
<Server>
This is the IEnumerable for the XAttributes so that we can use Linq to query attributes in a XML Document, using the XElement to access an XML File. for some reason this is returning Null and it needs to be returning the Attributes for attribute names "Nid".
XElement main = XElement.Load(fi.FullName);
IEnumerable<XAttribute> successAttributes =
from attribute in main.Attributes()
where attribute.Name.LocalName == "Nid"
select attribute;
this is where I execute the Linq Query to get the attributes in place them in the array
foreach (string attribute in successAttributes)
{
for (int i = 0; i < IntializedPorts.Count(); i++)
{
//running Intialization
IntializedNetworks[i] = attribute.ToString();
}
}
This may help you. Everything you wrote that code was right, you just rather use main.Element("Network").Attributes() than main.Attributes()
IEnumerable<XAttribute> successAttributes =
from attribute in main.Element("Network").Attributes()
where attribute.Name.LocalName == "Nid"
select attribute;
On behalf of this issue I wrote the below sample program which will yield the expected NID value
string strVal = "<Server><Network Nid=\"43d5377-0dcd-40e6-b95c-8ee980b1e248\"/><Client_Group id=\"20\">963440d0-96dc-46a4-a54d-7251a65f585f</Client_Group><ClientID id=\"20\">3fc8ffa1-c16b-4d7b-9e55-1e88dfe15277</ClientID></Server>";
XElement main = XElement.Load(new StringReader(strVal));
IEnumerable<XAttribute> successAttributes =
from attribute in main.Element("Network").Attributes()
where attribute.Name.LocalName == "Nid"
select attribute;
main here is the root element: <Server> - which does not have a Nid attribute.
You want something like:
Guid nid = (Guid)main.Element("Network").Attribute("Nid");
Or for multiple:
Guid[] arr = (from attr in main.DescendentsOrSelf().Attributes("Nid")
select (Guid)attr).ToArray();

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)

Why can't I get the specific node under some condition?

Why doesn't this xpath work with me? I want the current title if the language _id =2
./title[language_id=2]
<news pnumber="1" id="1"><event_id>578</event_id><event_type_id>1</event_type_id><language_id>2</language_id><title>meeting</title></news>
<news pnumber="1" id="1"><event_id>578</event_id><event_type_id>1</event_type_id><language_id>1</language_id><title>meeting</title></news>
The correct XPath expression is
//title[../language_id=2]
To avoid the reverse axis, use:
self::*[language_id=2]/title.
First add root to your XML then:
XDocument doc = XDocument.Load(xmlFilePath);
var result= doc.Descendants("news")
.Where(x=>x.Attribute("id") != null && x.Attribute("id").Value = "1")
.Select(x=>x.Descendants("title").First().Value);
Here is description of this linq2xml:
First Load XML file (Also you can parse xml string):
XDocument.Load(xmlFilePath);
Find your news elements :
doc.Descendants("news")
Between news elements select elements which has id=1:
Where(x=>x.Attribute("id") != null && x.Attribute("id").Value = "1")
From each filtered item select first title:
x.Descendants("title").First().Value

What is the best way to sort xml elements by attribute values in c#/.net 3.5

I have a XML file with data like:
<Details>
<TableDef>
<Column Name="Calldate" HeaderName="Calldate" SourceColumnName="Calldate" IsHidden = "false" Position="4" />
<Column Name="Issue" HeaderName="Issues" SourceColumnName="Issues" IsHidden = "false" Position="3" />
<Column Name="ParamortizedValue" HeaderName="paramortizedvalue" SourceColumnName="Value" IsHidden = "false" Position="1" />
<Column Name="PercentBondTotal" HeaderName="percentbondtotal" SourceColumnName="Percentage" IsHidden = "false" Position="2" />
</TableDef>
</Details>
I was wondering what would be the best and efficient way to filter and sort the elements by an attribute so that I can use that sorted elements further in my code?
In above example, I want to filter by "IsHidden = false" and then sort the elements by "Position" attribute.
I have the following code that will read all the elements without any sort:
var doc = XDocument.Load("data.xml");
var nodeTableDefInfo = doc.XPathSelectElements("/Details");
var elements = nodeTableDefInfo.Elements("TableDef").Elements();
foreach (var e in elements)
{
//want to work on the sorted elements
}
Guys, Thanks for all the solutions. before I could read your solution I came up with this:
var elements = nodeTableDefInfo.Elements("TableDef").Elements();
var sortedElements = (from element in elements
where element.Attribute("IsHidden").Value == "false"
orderby element.Attribute("Position").Value ascending
select element);
foreach (var e in sortedElements)
{
//work on the sorted elements
}
Now, after going through the solutions, I really liked the solution and explanation provided by GFK. I guess I need some handling for nulls. Thanks all.
Use some more LINQ!
var sortedElements =
from element in elements
let hiddenAttribute = element.Attribute("IsHidden")
let positionAttribute = element.Attribute("Position")
where hiddenAttribute != null
&& positionAttribute != null
&& hiddenAttribute.Value == "false"
let position = int.Parse(positionAttribute)
orderby position
select element;
foreach (var e in sortedElements)
{
// ...
}
Edit: also, what I do usually with LINQ-to-XML is create an anonymous type so I can parse once and for all the attributes into strongly typed values (and handle missing or invalid values), like:
int parsedInt;
var sortedElements =
from element in elements
let hiddenAttribute = element.Attribute("IsHidden")
let positionAttribute = element.Attribute("Position")
// add other attributes here
select new
{
IsHidden = hiddenAttribute == null || hiddenAttribute.Value != "false",
Position = positionAttribute == null || !int.TryParse(positionAttribute.Value, out parsedInt)
? default(int?)
: parsedInt,
// add other parsed attributes here
};
This way, all operations is made easier, like sorting or filtering:
foreach(var element in sortedElements.Where(e => !e.IsHidden).OrderBy(e => e.Position)
{
// ...
}
var tableDefs = nodeTableDefInfo.Elements("TableDef").Elements();
foreach(var column in tableDefs.Where(x=>
x.Attributes("IsHidden") != null &&
bool.Parse(x.Attributes("IsHidden").value) == false &&
x.Attributes("Position") != null)
.OrderBy(x=>int.Parse(x.Attributes("Position"))
linq..
string str = #"
<Details>
<TableDef>
<Column Name='Calldate' HeaderName='Calldate' SourceColumnName='Calldate' IsHidden='false' Position='4' />
<Column Name='Issue' HeaderName='Issues' SourceColumnName='Issues' IsHidden='false' Position='3' />
<Column Name='ParamortizedValue' HeaderName='paramortizedvalue' SourceColumnName='Value' IsHidden = 'false' Position='1' />
<Column Name='PercentBondTotal' HeaderName='percentbondtotal' SourceColumnName='Percentage' IsHidden = 'false' Position='2' />
</TableDef>
</Details>";
XDocument xml = XDocument.Parse(str);
List<XElement> columns = (from t in xml.Descendants("TableDef").Descendants("Column") where (bool)t.Attribute("IsHidden") == false orderby (int)t.Attribute("Position") ascending select t).ToList();
Try Linq2XML.
XDocument ip = XDocument.Load(Server.MapPath("~/data.xml"));
var op = ip.Descendants("Column")
.Where(node => node.Attribute("IsHidden").Value == "false")
.OrderBy(node => int.Parse(node.Attribute("Position").Value))
.ToList();
Now, op will be List<XElement>. Use for or foreach to manipulate it.
Sample approach:
var list = new List<KeyValuePair<string, XElement>>();
foreach(var element in elements)
{
list.Add(new KeyValuePair<string,XElement>(
element.Attribute("someAttr").Value,
element));
}
var sorted = from entry in list
orderby entry.Value.Attribute("someAttr").Value
select entry;
// just test:
foreach (var entry in sorted)
{
Debug.WriteLine(String.Format("attribute value: {0} - element: {1}", entry.Key, entry.Value.ToString()));
}
We're retrieving your chosen attribute values and treat it as an index.
We create a dictionary (a list of key-value pairs to be precise, as those values may not be unique) and then we just sort the list by the key.
I assume that this attribute will be present for every XElement - if that's not the case, you need to check for null of course.

How to get XElement's value and not value of all child-nodes?

Sample xml:
<parent>
<child>test1</child>
<child>test2</child>
</parent>
If I look for parent.Value where parent is XElement, I get "test1test2".
What I am expecting is "". (since there is no text/value for .
What property of XElement should I be looking for?
When looking for text data in the <parent> element you should look for child nodes that have NodeType properties equal to XmlNodeType.Text. These nodes will be of type XText. The following sample illustrates this:
var p = XElement
.Parse("<parent>Hello<child>test1</child>World<child>test2</child>!</parent>");
var textNodes = from c in p.Nodes()
where c.NodeType == XmlNodeType.Text
select (XText)c;
foreach (var t in textNodes)
{
Console.WriteLine(t.Value);
}
Update: if all you want is the first Text node, if any, here's an example using LINQ method calls instead of query comprehension syntax:
var firstTextNode = p.Nodes().OfType<XText>().FirstOrDefault();
if (firstTextNode != null)
{
var textValue = firstTextNode.Value;
...do something interesting with the value
}
Note: using First() or FirstOrDefault() will be more performant than Count() > 0 in this scenario. Count always enumerates the whole collection while FirstOrDefault() will only enumerate until a match is found.
It is amazing that a coder somewhere at Microsoft thought that returning all text values as a concatenated and undelimited string would be useful. Luckily, another MS developer wrote an XElement extension to return what they call the "Shallow Value" of the text node here. For those who get the willies from clicking on links, the function is below...
public static string ShallowValue(this XElement element)
{
return element
.Nodes()
.OfType<XText>()
.Aggregate(new StringBuilder(),
(s, c) => s.Append(c),
s => s.ToString());
}
And you call it like this, because it gives you all the whitespace too (or, come to think of it, you could trim it in the extension, whatever)
// element is a var in your code of type XElement ...
string myTextContent = element.ShallowValue().Trim();
You could concatenate the value of all XText nodes in parent:
XElement parent = XElement.Parse(
#"<parent>Hello<child>test1</child>World<child>test2</child>!</parent>");
string result = string.Concat(
parent.Nodes().OfType<XText>().Select(t => t.Value));
// result == "HelloWorld!"
For comparison:
// parent.Value == "Hellotest1Worldtest2!"
// (parent.HasElements ? "" : parent.Value) == ""
msdn says:
A String that contains all of the text content of this element. If there are multiple text nodes, they will be concatenated.
So the behaviour is to be expected.
You could solve your problem by doing:
string textContent = parent.HasElements ? "" : parent.Value;
// Create the XElement
XElement parent = XElement.Parse(
#"<parent>Hello<child>test1</child>World<child>test2</child>!</parent>");
// Make a copy
XElement temp=new XElement(parent);
// remove all elements but root
temp.RemoveNodes();
// now, do something with temp.value, e.g.
Console.WriteLine(temp.value);

Categories

Resources