how to use condition in linq while reading node from xml file - c#

how to add value of address and HomeAddress based on condition for Rule1 and Rule2.
Xml File :
<?xml version="1.0" encoding="utf-8"?>
<File>
<Data>
<id></id>
<name></name>
<address></address>
<HomeAddress></HomeAddress>
</Data>
</File>
C# code
[in reality xml is complex file]
XDocument xmlDocFinal = XDocument.Load(#"E:/Test.xml"); string[]
myCondition = {"Rule1","Rule2"};
foreach(string rule in myCondition)
{
var dataList = (from root in xmlDoc.Descendants("File")
from Data in root.Elements("Data")
select new
{
// For
Name = Data.Element("name").Value,
ID = Data.Element("id").Value,
// if rule1 then add Address otherwise ingnor it
Address =Data.Element("address").Value
// if rule2 then add HomeAddress otherwise ingnor it
HomeAddress = Data.Element("HomeAddress").Value
})
.ToList();
}

You can make in-line conditions with the conditional operator. Something like this:
Address = someCondition ? Data.Element("address").Value : string.Empty,
HomeAddress = anotherCondition ? Data.Element("HomeAddress").Value : string.Empty
So if someCondition is true then Address is set to the value, otherwise it's set to a default value (here I use string.Empty, you can use whatever you like). The same is true for setting HomeAddress.
Edit: Based on comments below, if you want to create two separate types then you're going to need two separate queries. Something like this:
var dataListAddress = (from root in xmlDoc.Descendants("File")
from Data in root.Elements("Data")
where someCondition // first condition
select new
{
Name = Data.Element("name").Value,
ID = Data.Element("id").Value,
Address = Data.Element("address").Value
}).ToList();
var dataListHomeAddress = (from root in xmlDoc.Descendants("File")
from Data in root.Elements("Data")
where anotherCondition // second condition
select new
{
Name = Data.Element("name").Value,
ID = Data.Element("id").Value,
HomeAddress = Data.Element("HomeAddress").Value
}).ToList();
This is because the anonymous types in the select clause are different. You could perhaps merge them into the same collection if it's a collection of something like object or maybe dynamic, but that could be unwieldy downstream when you need to use that collection.

Related

Search for attribute value in xaml file using linq in C#?

I know basic about getting node values from a xml file, but in a complicated xaml file I don't know how to write my linq query.
This is a sample of xaml I'm using:
<Activity something:something = " 2010" x:class = "example"..........>
<x:Members>
<x:Property Name= "aaaaa" Type = "Inargument" />
</x:Members>
<sap:abcdef>1322,2222</sap:abcdef>
<prwab:work sap2010:Annotation.AnnotationText = " I need this value" Active = "False" CreatedOn = "2014-07-09" ID = "123456" DisplayName = "theNameIneed">
<prwab:work.activity>
<prwais:collectingActivity sap2010:Annotation.AnnotationText = "I also need this value" > CreatedOn = "2014-07-09" ID = "1234232" DisplayName = "Ineed2">
<prwais .....>
</prwais>
</prwis:collectingActivity>
</prwab:work.activity>
</prwab:work>
</Activity>
I need to get the data in the lines that contain attribute "sap2010:Annotation.AnnotationText", and the value is going to be the content of sap2010:Annotation.AnnotationText, and key is going to be the attribute "ID" and attribute "displayName" in that line.
Here is the query I have right now, I know it's wrong but I don't know the proper way to write it:
var dataNodes = XElement.Load(file, LoadOptions.None);
Console.WriteLine("Loaded xaml file: " + file);
var dataNodesDictionary = from dataRecord in (dataNodes.Elements("prwab") && dataNodes.Elements("prwais"))//this line is wrong, but I dont know how to write it, since annotation may appears in different elements, and even if I only use "prwab" for testing, i still get nothing
where dataRecord.Attributes("Annotation.AnnotationText") != null
select new DictionaryEntry
{
Key = dataRecord.Attribute("DisplayName").Value.ToString() + "|" + dataRecord.Attribute("ID").Value.ToString(),
Value = dataRecord.Attribute("Annotation.AnnotationText").Value.ToString(),
};
Can some one help me please, thanks.
First find all elements that have the attribute. Then pull the attribute values.
// Name of the attributes we are looking
string ns = "http://schemas.microsoft.com/netfx/2010/xaml/activities/presentation";
XName name = XName.Get("Annotation.AnnotationText", ns);
XDocument doc = XDocument.Load("XMLFile1.xml");
var q = doc.Descendants().Where(e => e.Attribute(name) != null)
.Select(e => new DictionaryEntry { Key = e.Attribute("ID").Value, Value = e.Attribute(name).Value });
SIDE NOTE: In the future please paste in valid XML so it is easier for others to reproduce the issue.
Since this is XAML, you'll have a problem:
The attribute you're looking for can be written using a tag:
<prwab:work sap2010:Annotation.AnnotationText="I need this value">
...
</prwab:work>
This is equivalent to:
<prwab:work>
<sap2010:Annotation.AnnotationText>
I need this value
</sap2010:Annotation.AnnotationText>
...
</prwab:work>
So if you need a reliable way to read that, you should use the XamlReader class (the one from the System.Xaml namespace - not the one from System.Windows.Markup). It works in a similar way to XmlReader, but normalizes the XAML it presents to you.
It won't be as straightforward as a linq query, but will be more reliable.

Check against multiple elements with one query?

Is it possible to check multiple elements inside of a root using where? I have a XML sheet setup in such a way that there are multiple elements with the same name (but only sometimes), like so:
<person>
<name>Joe</name>
<food>orange</food>
<food>apple</food>
</person>
<person>
<name>Roger</name>
<food>apple</food>
</person>
I want to be able to check whether or not a person has a specific type of food and then output them into console. Using this method to grab them from the XML sheet:
var query = from c in xml.Root.Descendants("person")
where (string)c.Element("food") == "apple"
select new c.Element("food").Value;
It will only add Roger to the query. I believe this is because apple is second, since when I switch it to being first on the list I get Joe to show up. Is there a way to check to see if the second element meets the where statement as well?
You can try this out.It will return a projection with the name and food just to prove it has picked both:
var query = from c in xml.Root.Elements("person") //Descendants("person")
from f in c.Descendants("food")
where (string)f == "apple"
select new { Food = f.Value,Name = c.Element("name").Value };
Output of query:
{Food = "apple", Name = "joe"}
{Food = "apple", Name = "Roger"}
Your code won't add "Roger", but "apple" to the resulting IEnumerable. I assume you want to select the name element of your person.
As an alternative to the previous answer, you could also search for all apples and then get the name element of their parents.
var result = xml.Root.Descendants("food")
.Where(x=> (string)x == "apple")
.Select(y=> (string)y.Parent.Element("name"));
should do the trick
Using Lambda (short cutting since apple is hard coded):
var doc = XDocument.Parse(#"<root><person><name>Joe</name><food>orange</food><food>apple</food></person><person><name>Roger</name><food>apple</food></person></root>");
var results = doc.Root.Descendants("person")
.Where(p => p.Elements("food").Any(f => f.Value == "apple"))
.Select(p => "apple");
DotNetFiddle Example

LINQ TO XML attribute tag give no object reference error

Hi i am doing as follow
XDocument xmlDoc = XDocument.Load(#"F:\test2.xml");
var q = from c in xmlDoc.Descendants("autoivr.ok")
where c.Element("LS_CZIP4").Value == "1234"
select new
{
name = c.Element("LS_LIN").Value,
state = c.Element("LS_STATE").Value
};
When i use
where c.attribute("LS_CZIP4").Value == "1234"
i get error of object reference not set but when i use c.element there is no such error.
Following is the xml i made which is actually a table in sql converted to xml file
<?xml version="1.0" standalone="yes"?>
<DocumentElement>
<autoivr.ok>
<LS_LIN>abc</LS_LIN>
<LS_STATE>def</LS_STATE>
<LS_TYPE>5</LS_TYPE>
<LS_CZIP4>1234</LS_CZIP4>
<priority>0</priority>
</autoivr.ok>
Can someone let me know the problem and how can i resolve and can i work with element tag only instead of attribute . Thank You
Use casting instead of accessing Value property. Casting to string will return null for non-existing elements. Getting Value will throw an exception
XDocument xmlDoc = XDocument.Load(#"F:\test2.xml");
var q = from c in xmlDoc.Descendants("autoivr.ok")
where (string)c.Element("LS_CZIP4") == "1234"
select new
{
name = (string)c.Element("LS_LIN"),
state = (string)c.Element("LS_STATE")
};
BTW you need closing tag for <DocumentElement>. Also LS_CZIP4 is element, not attribute. See the difference here XML Elements vs. Attributes.
Element: <LS_LIN>abc</LS_LIN>
Attribute: <autoivr.ok LS_LIN="abc">

Problems with looping over XML in C# MVC

Hi there I'm trying to parse an XML file into a list of contacts, but having problems:
public List<ContactModel> GetContacts()
{
var doc = XDocument.Load(HttpContext.Current
.Server
.MapPath(#"..\App_Data\Contacts.xml"));
var result = from items in doc.Descendants("Directory")
select new ContactModel()
{
Id = items.Attribute("ID").Value,
FirstName = items.Attribute("FirstName").Value,
LastName = items.Attribute("LastName").Value,
Telephone = items.Attribute("Telephone").Value,
Email = items.Attribute("Email").Value,
Room = items.Attribute("Room").Value,
Building = items.Attribute("Building").Value,
Location = items.Attribute("Location").Value
};
List<ContactModel> contactList = new List<ContactModel>();
foreach (var item in result)
{
contactList.Add(item);
}
return contactList;
}
I get a null exception when it's trying to loop, what am I doing wrong?
This is my XML
<?xml version="1.0" standalone="yes"?>
<ContactDirectory>
<Directory>
<ID>1</ID>
<FirstName>Peter</FirstName>
<LastName>Sutt</LastName>
<Telephone>777888</Telephone>
<Email>pett#gmail.com</Email>
<Room>3.44</Room>
<Building>Westside</Building>
<Location>Leeds</Location>
</Directory>
<Directory>
<ID>2</ID>
<FirstName>Fred</FirstName>
<LastName>West</LastName>
<Telephone>1234</Telephone>
<Email>fred#west.com</Email>
<Room>1.23</Room>
<Building>Cromwell St</Building>
<Location>Gloster</Location>
</Directory>
<Directory>
</ContactDirectory>
Beebul, They are Elements not Attributes
var contactList = (from items in doc.Descendants("Directory")
select new ContactModel()
{
Id = items.Element("ID").Value,
FirstName = items.Element("FirstName").Value,
LastName = items.Element("LastName").Value,
Telephone = items.Element("Telephone").Value,
Email = items.Element("Email").Value,
Room = items.Element("Room").Value,
Building = items.Element("Building").Value,
Location = items.Element("Location").Value
})
.ToList();
PS: you don't need to loop over your result to get a list. You can use ToList()
It looks like one or more of your attributes are missing. items.Attribute(...) returns null, and calling Value on it causes the NPE*.
Since the execution is deferred, the call does not happen until you start looping through the result.
To find the attribute that causes the problem remove all calls to Attribute(...) except for ID, verify that the crash does not happen, and start adding back the attributes one by one until the crash is back.
* After seeing the XML that you added to your question it appears that all attributes are missing! Here is a link to a short article that discusses the differences.
The Linq to XML API has implicit conversions to help with possible null references returned from .Value. Try something like (string) items.Attribute("Room").

Linq to XML retrieve single node

I have the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<game>
<name>Space Blaster</name>
<description></description>
<version>1</version>
<fullscreen>false</fullscreen>
<width>640</width>
<height>640</height>
<c2release>6900</c2release>
</game>
It's guaranteed to only have 1 game record in it. I'd like to retrieve each property value, this is what I've tried:
string ArcadeXMLLocation = Settings.GamePergatoryLocation + UserID + "/unzipped/arcade.xml";
XDocument loaded = XDocument.Load(ArcadeXMLLocation);
var q = (from c in loaded.Descendants("game") select (string)c.Element("name")).SingleOrDefault();
Response.Write(q.name);
But I can't seem to take any values like this (intellisense hates it!) Can someone show me how it's done?
I think you just need to get the value of the element:
string val = doc.Descendants("game")
.Select(x => x.Element("name").Value).FirstOrDefault();
As a prerequisite to the above, and so intellisense picks it up, make sure that you import the System.Linq and System.Xml.Linq namespaces.
This will get you all the descendants with the tag "game"
You just need the FirstOrDefault() to get the only record if it exists.
var q = from c in loaded.Descendants("game")
select new { Name = c.Element("name").Value
Description = c.Element("description").Value
};
q.FirstOrDefault();
You query is actually correct (tested and worked for me) for extracting the value of the name node - the (string) cast that you are doing will extract the value of the name node as string and not give you the node object itself, this is one of the shortcuts built into Linq to Xml. All that is left is to print out the name:
Response.Write(q);
var q = (from c in loaded.Descendants("game") select (string)c.Element("name")).SingleOrDefault();
Console.WriteLine(q);
is enough. or to avoid the cast
var q = (from c in loaded.Descendants("game") select c.Element("name").Value).SingleOrDefault();
Console.WriteLine(q);

Categories