LINQ query to get XML elements when an attribute is not present - c#

I have this XML:
<Config>
<EmpFieldsMap>
<Employee>
<Field>
<Name update = "false">EmpNumber</Name>
</Field>
<Field>
<Name insert = "true">EmpName</Name>
</Field>
<Field>
<Name insert = "true">EmpDesignation</Name>
</Field>
</Employee>
</EmpFieldsMap>
</Config>
My application will do an an INSERT or UPDATE for which the fields will come from this xml.
Each tag will have either the insert or the update attribute as shown in the snippet above.
For Insert all the tags that have the attribute
insert = "true"
and the tags that don't have this attribute, in this case the 'EmpNumber', have to be considered.
The same applies for update.
This code gives me all the tags with the insert attribute set to true:
insertTags = from p in xml.Element("Config").Element("EmpFieldsMap").Elements("Field")
where p.Element("Name").Attribute("insert") != null
&& p.Element("Name").Attribute("insert").Value == "true"
select p.Element("Name").Value;
Removing the check for null
insertTags = from p in xml.Element("Config").Element("EmpFieldsMap").Elements("Field")
where p.Element("Name").Attribute("insert").Value == "true"
select p.Element("Name").Value;
gives
Object Reference not set to an instance
error.
I am having trouble composing a query that will also include the tags where the attribute is not present.
Can someone please help me with this?
Regards.

insertTags = from p in xml.Element("Config").Element("EmpFieldsMap").Elements("Field")
where (p.Element("Name").Attribute("insert") ?? "true") == "true"
select p.Element("Name").Value;

With XPath and Linq it even simpler:
XPathSelectElements(#"Config/EmpFieldsMap/Employee/Field/Name[#insert='true']")
Also for this particular xml you can use global search for name elements:
var insertTags = xdoc.XPathSelectElements(#"//Name[#insert='true']")
.Select(n => (string)n);
Or with Linq query syntax:
var insertTags = from n in xdoc.Descendants("Name")
where (string)n.Attribute("insert") == "true"
select (string)n;
When you cast node value to string, it will not throw exception if node is missing. Simply null will be returned. So, you don't need all that stuff (which is btw even not compilable):
(p.Element("Name").Attribute("insert") ?? "true") == "true"
One more edit. If you are dealing with booleans, then use booleans instead of strings:
var insertTags = from n in xdoc.Descendants("Name")
where (bool?)n.Attribute("insert") == true
select (string)n;
How it works? Nullable boolean will have null value for missing attributes. Comparing bool? which do not have value with any boolean value produces false. So, you will get only those elements, which have required attribute, and which have true for that attribute.

Related

Find one attribute in XML File with linq.XML

i want to find the value of an Attribute in our XML-FIle.
For Example, here our XML Document:
<PROJECT_DOCUMENTS>
<DOCUMENT isFile="YES" isLink="YES" type="Risk Action List (combined)" path="path" showFile="" showFolder="YES" FilePath="" FolderPath="" />
<DOCUMENT isFile="YES" isLink="NO" type="ASPICE-Action List" path="path" showFile="" showFolder="YES" FilePath="" FolderPath="path" />
</PROJECT_DOCUMENTS>
I want the value of path = " .... bla ..." were the type is ASPICE-Action List.
Here my code with i generated:
XElement elementToChange = (from c in getFileFromXML.Element("PROJECT_DOCUMENTS")
.Elements("DOCUMENT")
where("type"== "ASPICE-Action List")
select c).Single().Element("path");
But i dont get the infomation. I think the type == Aspice-Action list doesn´t work.
Can anyone help me to solve my Problem ;)
Thanks
If you want to get path attribute values you can use next code which manipulate with Attribute() method for attributes of element:
var elementsToChange = from c in getFileFromXML.Element("PROJECT_DOCUMENTS").Elements("DOCUMENT")
where c.Attribute("type")?.Value == "ASPICE-Action List" )
select c.Attribute("path").Value;
To retrieve whole elements use next code or add SingleOrDefault() or FirstOrDefault() to the end for your tasks:
var elementsToChange = from c in getFileFromXML.Element("PROJECT_DOCUMENTS").Elements("DOCUMENT")
where c.Attribute("type")?.Value == "ASPICE-Action List" )
select c;
You can try getting the same using the code below:
from c in getFileFromXML.Elements("DOCUMENT")
where "ASPICE-Action List" == c.Attribute("type").Value
select c;

Duplicate values in Xml database

I need to insert only unique values in my XML database based on the "userName" value. For example there can't be two players which has same name in the database. The implementation of my xml database which is named as "MyXML.xml" is like that :
<?xml version="1.0" encoding="utf-8"?>
<Players>
<player name="cag" score="5555" />
<player name="cihan" score="1222" />
<player name="can" score="333" />
</Players>
and my related code in order to detetect duplication is:
public bool insertUserDetails(string userName,float userScore){
XDocument doc = Document.Load(HttpContext.Current.Server.MapPath("MyXML.xml"));
var duplicate = doc.Element("Players").Elements("player").Where(x =>(string)x.Value == userName).SingleOrDefault();
if (duplicate != null){
return false;
}
else{
return true;
}
}
when I try to insert a duplicate value for example "can" and "333", var duplicate value turns out to be null. How can I fix this problem ?
"can" is defined as a attribute in your XML; It is not a value. Value is something which comes in between open and closing tags of xml.
For example, if you have a xml element like this
<SomeTag name="somename">Hello world</SomeTag>
Then, SomeTag is the element name, somename is the attribute value of attribute "name" and "Hello world" is the value of the xml element itself.
So, You need to find the attribute using Attribute method to access its value.
var duplicate = doc.Element("Players")
.Elements("player")
.Where(x=>x.Attribute("name").Value == userName)
.SingleOrDefault();
You're iterating over the Value of each player element, rather than the Attributes of those elements. Since there's no text inside the player element, the value is always an empty string, and thus never matched the name you're looking for. Instead, select the .GetAttribute("name") for each element
var duplicate = doc
.Element("Players")
.Elements("player")
.Select(ele => ele.GetAttribute("name"))
.Where(att =>(string)att.Value == userName)
.SingleOrDefault();
you're comparing the value of an XElement, "x", to userName.
you should compare the "name" attribute of x to it.
var duplicate = doc.Element("Players").Elements("player").Where(x => (string)x.Attribute("name").Value == userName).SingleOrDefault();

Confused about linq this query

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 ""

C# linq to xml, nested query with attributes

Im really struggling to get my head round this.
Im using c#.
I want to get back an IEnumerable of products from an xml file.
Below is a sample of the xml structure.
I need to get a list of products that have the productEnriched custom attribute set as true.
Some products wont have any custom attribute section at all
my head has strated to hurt just thinking about it!
<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="http://www.mynamespace.com" catalog-id="MvgCatalog">
<product>
<custom-attributes>
<custom-attribute attribute-id="productEnriched">true</custom-attribute>
</custom-attributes>
</product>
</category>
thanks for any help
To clear things up i have added a few more items to the example xml
I need to get a list of products
only products that have a custom-attribute element with the attribute productEnriched and value of true
some products in the xml wont have any custom-attribute or custom-attributes elements
some products will have it but with a value of false
i just need a list of products where it exists and has a value of true
<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="http://www.mynamespace.com" catalog-id="MvgCatalog">
<product>
<upc>000000000000</upc>
<productTitle>My product name</productTitle>
<custom-attributes>
<custom-attribute attribute-id="productEnriched">true</custom-attribute>
<custom-attribute attribute-id="somethingElse">4</custom-attribute>
<custom-attribute attribute-id="anotherThing">otherdata</custom-attribute>
</custom-attributes>
</product>
</category>
I need to get a list of products only products that have a
custom-attribute element with the attribute productEnriched and value
of true some products in the xml wont have any custom-attribute or
custom-attributes elements some products will have it but with a value
of false i just need a list of products where it exists and has a
value of true
var xml = XElement.Load(#"your file.xml");
XNamespace ns = "http://www.mynamespace.com";
var products = xml.Elements(ns + "product");
var filtered = products.Where(
product =>
product.Element(ns + "custom-attributes") != null &&
product.Element(ns + "custom-attributes").Elements(ns + "custom-attribute")
.Any(
ca =>
ca.Value == "true" &&
ca.Attribute("attribute-id") != null &&
ca.Attribute("attribute-id").Value == "productEnriched"));
By the way, your XMLs are not valid - your opening tag (catalog) does not match your closing tag (category).
The format by itself is strange - is it your idea?
<custom-attributes>
<custom-attribute attribute-id="productEnriched">true</custom-attribute>
<custom-attribute attribute-id="somethingElse">4</custom-attribute>
<custom-attribute attribute-id="anotherThing">otherdata</custom-attribute>
</custom-attributes>
Why put an attribute name as an attribute value and attribute value as an element value? It looks bloated and kind of "reinvents" XML with no clear purpose.
Why not:
<custom-attributes>
<custom-attribute productEnriched="true"/>
<custom-attribute somethingElse="4"/>
<custom-attribute anotherThing="otherdata"/>
</custom-attributes>
Or:
<custom-attributes productEnriched="true" somethingElse="4" anotherThing="otherdata"/>
Or perhaps just use elements:
<product-parameters>
<productEnriched>true</productEnriched>
<somethingElse>4</somethingElse>
<anotherThing>otherdata</anotherThing>
</product-parameters>

How to check the existence of Attribute in XDocument using LINQ to XML in C#

i'm having an xml file like
<Root>
<Child Name="A" />
</Root>
i need to check whether the "Child" element have "val" attribute.if yes , and if the value is greater than zero then need to change the value of a Boolean variable into true;
now i'm using like
bool bVal=false
bVal=XDocument.Load(Application.StartupPath+"\\foo.xml")
.Descendants("Child")
.Select(TEMP => (Int32)TEMP.Attribute("val")).ToList()[0]>0?true:false;
this expression is working fine if xml is like
<Root>
<Child Name="A" val ="2" />
</Root>
but its throwing an exception if the xml does not contain "val" attribute.
How to modify the above expression(Query) to check the existence of "val" attribute.
In this case, I'd rewrite the query as:
bool bVal = XDocument.Load(Application.StartupPath+"\\foo.xml")
.Descendants("Child")
.Select(x => (int?) x.Attribute("val"))
.FirstOrDefault(x => x != null) > 0;
This uses three features:
Converting XAttribute to int? instead of int will result in the null value if the attribute isn't present
Using FirstOrDefault instead of ToList()[0] is more efficient and works even if there are no values
The lifted > operator will return False when either operand is null
If you want to check whether there any positive values, it's even easier:
bool bVal = XDocument.Load(Application.StartupPath+"\\foo.xml")
.Descendants("Child")
.Select(x => (int?) x.Attribute("val"))
.Any(x => x > 0);

Categories

Resources