LINQ complex delete and search query - c#

//assume that I have the following XML file.
<warehouse>
<cat id="computer">
<item>
<SN>1</SN>
<name>Toshiba</name>
<quantity>12</quantity>
<description>CPU: CORE I5 RAM: 3 GB HD: 512 GB</description>
<price>400 USD</price>
</item>
<item>
<SN>22</SN>
<name>Toshiba</name>
<quantity>12</quantity>
<description>CPU: CORE I5 RAM: 3 GB HD: 512 GB</description>
<price>400 USD</price>
</item>
</cat>
<cat id="Stationery">
<item>
<SN> 33 </SN>
<name>note books</name>
<quantity>250</quantity>
<description>Caterpiller</description>
<price>5 USD</price>
</item>
</cat>
<cat id="Furniture">
<item>
<SN> 1 </SN>
<name>dasd</name>
<quantity>asdasd</quantity>
<description>das</description>
<price>dasd</price>
</item>
<item>
<SN>44</SN>
<name>Toshiba</name>
<quantity>12</quantity>
<description>CPU: CORE I5 RAM: 3 GB HD: 512 GB</description>
<price>400 USD</price>
</item>
<item>
</cat>
</warehouse>
question 1 : I want to Delete <item> element and its child's using linq , where <cat id="computer"> and <SN> has a specific value like 44 .
question 2 : I want to make a query using TextBox and Literal1 which return a specic <item> and its child's . this query should be in linq.
for example
XDocument xmlDoc = XDocument.Load(Server.MapPath("/XML/Cat1.xml"));
var persons = from person in xmlDoc.Descendants("item")
where person.Element("SN").Value.Equals(DropDownList1.Text)
select person;
persons.Remove();
foreach (XElement person in persons.ToList())
{
person.Remove();
}

Try this:-
xmlDoc.Root.Descendants("cat").Where(x => x.Attribute("id").Value == "computer")
.Descendants("item").Where(x => x.Element("SN").Value.Trim() == Dropdownlist.Text)
.Remove();
xmlDoc.Save(#"YourXML.xml");
And for filtering you can replace the values at specific places you like. Also, I have used Trim directly, better check for empty strings first in your actual code.
Question 2:-
var result = xmlDoc.Descendants("item")
.Where(x => x.Element("SN").Value.Trim() == Dropdownlist.Text);

Regarding question one i would do something like this using method syntax:
doc.Descendants("cat")
.Where(x => String.Equals((string)x.Attribute("id"), "computer", StringComparison.OrdinalIgnoreCase))
.Elements("item")
.Where(x => (string)x.Element("SN") == "44")
.Remove();
Basically select all your cat elements and filter by attribute id = computer. Then select all items in each of those and filter through SN = 44.

Related

Load XML containing multiple lists to multiple DataTables

I have an XML that has the below structure:
<?xml version="1.0" encoding="utf-8"?>
<n0:Response xmlns:n0="urn:sap-com:document:sap:rfc:functions">
<Cars>
<item>
<ID>1</ID>
<TYPE>SUV</TYPE>
<YEAR>2022</YEAR>
</item>
<item>
<ID>2</ID>
<TYPE>Convertible</TYPE>
<YEAR>2021</YEAR>
</item>
</Cars>
<Employees>
<item>
<ID>1</ID>
<NAME>John</NAME>
<STATUS>1</STATUS>
<PHONE>000000000</PHONE>
</item>
<item>
<ID>2</ID>
<NAME>Sam</NAME>
<STATUS>2</STATUS>
<PHONE>000000000</PHONE>
</item>
<item>
<ID>3</ID>
<NAME>Jane</NAME>
<STATUS>1</STATUS>
<PHONE>000000000</PHONE>
</item>
</Employees>
</n0:Response>
(this is just a sample, the final file will have more than 10k items per row)
I need to load that file to two DataTables, each having the correct values.
I do this by creating DataSet, loading XML, and filtering the data table.
My DataSet has 3 tables:
but when I try to access the correct data table I get all the items instead of the correct ones (I get items from the entire XML):
var rel = ds.Relations.OfType<DataRelation>()
.FirstOrDefault(x => x.ParentTable.TableName == "Cars")?.ChildTable;
this is the result:
I managed to filter the data by adding a Select statement like below:
var ds = new DataSet();
using Stream stream = new FileStream("Sample.xml", FileMode.Open, FileAccess.Read);
ds.ReadXml(stream);
var table1 = ds.Relations.OfType<DataRelation>()
.FirstOrDefault(x => x.ParentTable.TableName == "Cars")?
.ChildTable.Select("Cars_Id = 0").CopyToDataTable();
var table2 = ds.Relations.OfType<DataRelation>()
.FirstOrDefault(x => x.ParentTable.TableName == "Employees")?
.ChildTable.Select("Employees_Id = 0").CopyToDataTable();
but this isn't generic and I'd like to be able to access the correct DataTable only by name (the node name from XML).
My question is similar to How to get multiple tables in Dataset from XML, but the provided answer didn't solve the issue.

Select an element who as a last child with property

<bus>
<port>
<req>
<item>
[...]
</item>
</req>
[...]
<req>
<item>
[...]
</item>
</req>
</port>
[...]
<port>
<req>
<item>
[...]
</item>
</req>
[...]
<req>
<item>
[...]
</item>
</req>
</port>
</bus>
<bus>
[...] (same as before)
</bus>
I have this structure; all the structures repeats themselves. I need to select the last port element of a bus which has a last child with property "mode"=="read".
It can exist a bus which has a last port element with a last child with property different from "read", so I need to choose the right port element.
I tried lots of tries, last one is this, but does not works:
var modbusportSelected = Elements("bus").Elements("port")
.Where( x => x.Elements("req")
.Any(y => y.Attribute("mode").Value.Contains("read")))
.Last();
Any help would be greatly appreciated; also, I'm totally new with LINQ to XML and I cannot find a single webpage where to get the exact meaning of "Any" and if there are other operators, and if so, what they are.
It may be significant that your XML snippet needs a top level element. If you wrap what you have above in an outer tag, then your code appears to work provided you trap null references from any port elements which don't have a mode attribute. eg.
using System;
using System.Linq;
using System.Xml.Linq;
namespace ConsoleApp1
{
class Program
{
public static string xml = #"<topLevel><bus>
<port isCorrectNode='no'>
<req>
<item>
</item>
</req>
<req mode='read'>
<item>
</item>
</req>
</port>
<port isCorrectNode='yes'>
<req mode='read'>
<item>
</item>
</req>
<req>
<item>
</item>
</req>
</port>
</bus>
<bus>
</bus>
</topLevel>";
static void Main(string[] args)
{
XElement root = XElement.Parse(xml);
var found = root.Elements("bus").Elements("port")
.Where(x => x.Elements("req").Any(y => y.Attribute("mode") != null && y.Attribute("mode").Value.Contains("read")))
.Last();
var isThisTheCorrectNode = found.Attribute("isCorrectNode").Value;
Console.WriteLine(isThisTheCorrectNode);
}
}
}
will write yes
Edit: I've noticed your code looks for the last port which has any child req whose mode is 'read'. but your question asked for the last such req. In that case:
var wanted = root.Elements("bus").Elements("port")
.Where(x => x.Elements("req").Any() && // make sure there is a req element
x.Elements("req").Last().Attribute("mode") != null && // and it has the attribute
x.Elements("req").Last().Attribute("mode").Value.Contains("read")) // and it has the right value
.Last();

Order frequent XML Data by group using LINQ

I have an XDocument with this data:
<item>
<name>Name 1</name>
<group>foo</group>
...
</item>
<item>
<name>Name 2</name>
<group>bar</group>
...
</item>
<item>
<name>Name 3</name>
<group>foo</group>
...
</item>
That means I have 2 items with group = foo and 1 item with group = bar. How can I order that data by the most frequent group? Result:
<item>
<name>Name 1</name>
<group>foo</group>
...
</item>
<item>
<name>Name 3</name>
<group>foo</group>
...
</item>
<item>
<name>Name 2</name>
<group>bar</group>
...
</item>
Data is loaded like this:
XDocument xml = XDocument.Load(#"\path\data.xml");
You can use a simple GroupBy and OrderByDescending:
var items = xml.Root.Elements()
.GroupBy(r => r.Element("group").Value)
.OrderByDescending(r => r.Count())
.ToList();
Then simply remove all nodes and add them ordered:
xml.Root.RemoveAll();
xml.Root.Add(items);
If you want to work with objects instead of the xml, you'll first need to deserialize the xml into a List<Item>, where each Item is a class of the same structure in the xml.
After that, when you have the list you can use GroupBy:
var newList = list.GroupBy(i => i.Group) //group together by "group" value
.OrderByDescending(i => i.Count()) //simple ordering
.SelectMany(i => i) //flatten the hierarchy
.ToList(); //back to list

Read xml with repeat elements using xdocument

i need to xml file repeated item. Inside "item" i have fields like title, pubdate, description, dc:creator and with repeatwp:comment... see the xml file below..
<channel>
<item>
<title>What Messed With My Head: Summit 2011</title>
<link>http://www.wcablog.com/2011/08/what-messed-with-my-head-summit-2011/</link>
<pubDate>Fri, 26 Aug 2011 09:10:04 +0000</pubDate>
<dc:creator>willowcreekassociation</dc:creator>
<guid isPermaLink="false">http://www.wcablog.com/?p=1706</guid>
<description></description>
<content:encoded>
<![CDATA[text here]]>
</content:encoded>
<wp:comment>
<wp:comment_id>1016</wp:comment_id>
<wp:comment_author><![CDATA[]]></wp:comment_author>
<wp:comment_author_email>thelmabowlen#gmail.com</wp:comment_author_email>
<wp:comment_author_url></wp:comment_author_url>
<wp:comment_author_IP></wp:comment_author_IP>
<wp:comment_date>2011-08-26 20:13:00</wp:comment_date>
<wp:comment_content><![CDATA[some text ]]></wp:comment_content>
</wp:comment>
<wp:comment>
<wp:comment_id>1016</wp:comment_id>
<wp:comment_author><![CDATA[]]></wp:comment_author>
<wp:comment_author_email>thelmabowlen#gmail.com</wp:comment_author_email>
<wp:comment_author_url></wp:comment_author_url>
<wp:comment_author_IP></wp:comment_author_IP>
<wp:comment_date>2011-08-26 20:13:00</wp:comment_date>
<wp:comment_content><![CDATA[some text ]]></wp:comment_content>
</wp:comment>
</item>
<item>
<title>What Messed With My Head: Summit 2011</title>
<link>http://www.wcablog.com/2011/08/what-messed-with-my-head-summit-2011/</link>
<pubDate>Fri, 26 Aug 2011 09:10:04 +0000</pubDate>
<dc:creator>willowcreekassociation</dc:creator>
<guid isPermaLink="false">http://www.wcablog.com/?p=1706</guid>
<description></description>
<content:encoded>
<![CDATA[text here]]>
</content:encoded>
</item>
<item>
<title>What Messed With My Head: Summit 2011</title>
<link>http://www.wcablog.com/2011/08/what-messed-with-my-head-summit-2011/</link>
<pubDate>Fri, 26 Aug 2011 09:10:04 +0000</pubDate>
<dc:creator>willowcreekassociation</dc:creator>
<guid isPermaLink="false">http://www.wcablog.com/?p=1706</guid>
<description></description>
<content:encoded>
<![CDATA[text here]]>
</content:encoded>
<wp:comment></wp:comment>
<wp:comment></wp:comment>
</item>
</channel>
I am using following code to read xml..
XDocument xDoc = XDocument.Load("willowcreekassociationblog.wordpress.xml");
var list = xDoc.Descendants("Occurrence")
.Select(o => new List.XMLList
{
title = (string)o.Element("title"),
URL = (string)o.Element("link"),
Descr = (string)o.Element("Description"),
StartDate = (DateTime)o.Element("pubdate"),
})
.ToList();
But i dont know how read the wp:comment with my above code...can anyone help me how to do this??
You haven't said what you want to do with the data, which makes the question hard to answer. You also haven't shown where the wp namespace alias is defined, but hey...
You want something like this, within your Select call:
Comments = o.Elements(wp + "comment")
.Select(comment => Comment.FromXElement(comment))
.ToList();
I find it helpful to give static methods within the domain objects which can perform the transformation from XElement - it keeps things clearer.
FromXElement would then be something like:
public static Comment FromXElement(XElement x)
{
return new Comment((int) x.Element(wp + "comment_id"),
(string) x.Element(wp + "author"),
...);
}

Need a Nested LINQ to XML query help

i have a XML file as follows:
<?xml version="1.0" encoding="utf-8" ?>
<publisher>
<name>abc</name>
<link>http://</link>
<description>xyz</description>
<category title="Top">
<item>
<title>abc</title>
<link>http://</link>
<pubDate>1</pubDate>
<description>abc</description>
</item>
<item>
<title>abc</title>
<link>http://</link>
<pubDate>2</pubDate>
<description>abc</description>
</item>
</category>
<category title="Top2">
<item>
<title>abc</title>
<link>http://</link>
<pubDate>1</pubDate>
<description>abc</description>
</item>
<item>
<title>abc</title>
<link>http://</link>
<pubDate>2</pubDate>
<description>abc</description>
</item>
</category>
</publisher>
I need to write a LINQ to XML query in C# which returns everything under a "category" tag based on the value of attribute provided. I have tried the following code but it gives me error. Any help will be appreciated:
System.Xml.Linq.XElement xml = System.Xml.Linq.XElement.Parse(e.Result);
IEnumerable<string> items = from category in xml.Elements("category")
where category.Attribute("title").Value == "Top"
select category.ToString();
IEnumerable<string> items = from category in xml.Descendants("category")
where category.Attribute("title").Value == "Top"
select category.ToString();
Of course, that's going to give you a list with one string in it. If you want just the string in it:
var items = (from category in xml.Descendants("category")
where category.Attribute("title").Value == "Top"
select category.ToString()).First();
But, if you want to continue processing the XML, you probably really want it as a XElement object:
var items = (from category in xml.Descendants("category")
where category.Attribute("title").Value == "Top"
select category).First();

Categories

Resources