Xml file reading using XML to LINQ in C# - c#

I've an xml file (Sample.xml) which has the following structure
<RootElement>
<Children>
<Child Name="FirstChild" Start="0" End="2">
<Sibling Name="Test1" />
<Sibling Name="Test2" />
<AdditionalSibling Name="Add_Test_1" />
<AdditionalSibling Name="Add_Test_2" />
<MissingSibling Name="Miss_Test_1" />
<MissingSibling Name="Miss_Test_2" /
</Child>
<Child Name="SecondChild" Start="0" End="2">
<Sibling Name="Test3" />
<Sibling Name="Test4" />
</Child>
<Child Name="ThirdChild" Start="0" End="2">
<Sibling Name="Test5" />
<Sibling Name="Test6" />
</Child>
<Child Name="FourthChild" Start="0" End="2">
<Sibling Name="Test7" />
<Sibling Name="Test8" />
</Child>
<Child Name="FifthChild" Start="0" End="2">
<Sibling Name="Test9" />
<Sibling Name="Test10" />
</Child>
<Child Name="SixthChild" Start="0" End="2">
<Sibling Name="Test11" />
<Sibling Name="Test12" />
</Child>
<MatchedChilds>
<Child Name="FirstChild" />
<Child Name="SecondChild" />
<Child Name="ThirdChild" />
<Child Name="FourthChild" />
<Child Name="FifthChild" />
<Child Name="SixthChild" />
</MatchedChilds>
</Children>
</RootElement>
i need to read the attribute values of the element" Child" which is directly under "RootElement"
Now i'm using the LINQ query like
List<string> lst = (from element in XDocument.Load(Application.StartupPath + "\\Sample.xml")
.Descendants("Child")
group element by element.Attribute("Name").Value into KeyGroup select KeyGroup.Key )
.ToList();
but it returns the attribute value of "Name" of all the "Child" element in the file.
Please give me a better solution to do this using XML to LINQ
Thanks in advance

Use something like this:
XDocument doc = XDocument.Load(Application.StartupPath + "\\Sample.xml");
List<string> lst = doc.Root.Elements("Child")
.Select(x => (string) x.Attribute("Name"))
.ToList();
Note the use of Elements() instead of Descendants(). That will get you all the name attributes. It's unclear to me why you were grouping in your original sample code, but hopefully this will get you going.

You grouped by name, so your list would have name elements. You may query and iterate your results as follows:
var query = from element in XDocument.Load(Application.StartupPath + "\\Sample.xml")
.Descendants("Child")
group element by element.Attribute("Name").Value into KeyGroup
select new { Grouping = KeyGroup.Key, Children = KeyGroup };
and the iteration
foreach(var namekey in query)
{
Console.WriteLine(namekey.Grouping);
foreach(var child in namekey.Children)
{
Console.WriteLine(child.whatever);
}
}

Related

Why my code with XDocument in .NET not return any elements? (Problem with XML and XDocument)

I have a problem with the following XML:
<?xml version="1.0" encoding="utf-8"?>
<Report xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ReportName="SearchQueryPerformanceReport_05_24_2022_11_19_51" ReportTime="3/24/2022,5/23/2022" TimeZone="Various" ReportAggregation="Daily" LastCompletedAvailableDay="5/24/2022 10:20:00 AM (GMT)" LastCompletedAvailableHour="5/24/2022 10:20:00 AM (GMT)" PotentialIncompleteData="true" xmlns="http://adcenter.microsoft.com/advertiser/reporting/v5/XMLSchema">
<Table>
<Row>
<AccountName value="fffffff" />
<CampaignName value="dddddddddddddd" />
<CampaignId value="424769594" />
<Clicks value="1" />
<CostPerConversion value="" />
<Ctr value="100.00%" />
<AccountId value="1848" />
<DeviceType value="Computer" />
<AverageCpc value="0.25" />
<AveragePosition value="0.00" />
<BidMatchType value="Exact" />
<AdGroupName value="eeeeeeeeee" />
<AdGroupId value="135899347066" />
<AdId value="84933705" />
<ConversionRate value="0.00%" />
<Keyword value="zusammen at" />
<KeywordId value="84937818" />
<Language value="German" />
<Conversions value="0" />
<Impressions value="1" />
<Spend value="0.25" />
<TimePeriod value="2022-04-13" />
<SearchQuery value="zusammen.at" />
</Row>
</Table>
<Copyright>©2022 Microsoft Corporation. All rights reserved. </Copyright>
</Report>
When I use this code the XML does not return any element.
XDocument doc = XDocument.Parse(LstSearchTermV.XmlContent); var result2 = from s in doc.Descendants("Table");
or
var result2 = from s in doc.Element("Table");
or
var result2 = from s in doc.Elements("Row");
I have tried different ways but nothing.
I test with other XML and have same problem.
Does anyone know why, where is the problem?
Update:
I try it: XDocument Elements return null
It work and I get elements but, when I try to get values is very long code, example:
var ValueElement = el.Element(XName.Get("CampaignId", doc.Root.GetDefaultNamespace().NamespaceName)).Attributes().Where(w => w.Name == "value");
¿Is any code more short or more optimized to get data of XDocument?
Yes, you can definitely write simpler code than that, using the XNamespace type and the various operators and conversions available to create an XName, and using the Attribute method that looks for an attribute with a given name:
// Or XNamespace ns = doc.Root.GetDefaultNamespace()
XNamespace ns = "http://adcenter.microsoft.com/advertiser/reporting/v5/XMLSchema";
var value = el.Element(ns + "CampaignId").Attribute("value");
value will be null if there's no such attribute.

How to read xml in c# with XElement?

I have this xml
<Folder.FolderStructure
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/FileSiteAPI.Models">
<a_attr i:nil="true" />
<checkedout>false</checkedout>
<children>
<Folder.FolderStructure>
<checkedout>false</checkedout>
<children />
<created>2018-11-29T13:58:57</created>
<edited>2018-11-29T13:58:57</edited>
<extension>MSG</extension>
<id>36878331</id>
<rootfolder>false</rootfolder>
<searchfolder>false</searchfolder>
<size>29696</size>
<state i:nil="true" />
<version>1</version>
</Folder.FolderStructure>
<Folder.FolderStructure>
<checkedout>false</checkedout>
<children />
<created>2019-01-15T10:18:03</created>
<edited>2019-01-15T10:18:03</edited>
<extension>DOCX</extension>
<id>37584622</id>
<rootfolder>false</rootfolder>
<searchfolder>false</searchfolder>
<size>42345</size>
<state i:nil="true" />
<version>1</version>
</Folder.FolderStructure>
<Folder.FolderStructure>
<a_attr i:nil="true" />
<checkedout>false</checkedout>
<children i:nil="true" />
<created>0001-01-01T00:00:00</created>
<edited>0001-01-01T00:00:00</edited>
<extension i:nil="true" />
<id>2478514</id>
<rootfolder>false</rootfolder>
<searchfolder>false</searchfolder>
<size>0</size>
<state i:nil="true" />
<version>0</version>
</Folder.FolderStructure>
</children>
<created>0001-01-01T00:00:00</created>
<edited>0001-01-01T00:00:00</edited>
<extension i:nil="true" />
<id>2469288</id>
<rootfolder>false</rootfolder>
<searchfolder>false</searchfolder>
<size>72041</size>
<state i:nil="true" />
<text>Public</text>
<version>0</version>
</Folder.FolderStructure>
but when I parse it and try to read the 3 Folder.FolderStructure elements, it comes back with empty set.
XElement folder_xml = XElement.Parse(xml_str);
List<XElement> elements = folder_xml.Elements("Folder.FolderStructure").ToList();
Console.WriteLine(elements.Count);
Anyone know what's wrong?
You need to define the namespace and use Descendants instead of Elements:
XElement folder_xml = XElement.Parse(xml_str);
XNamespace ns = "http://schemas.datacontract.org/2004/07/FileSiteAPI.Models";
var elements = folder_xml.Descendants(ns + "Folder.FolderStructure").ToList();
Console.WriteLine(elements.Count);

Delete data in XML with C#

I have the following XML File:
<order id="1234">
<users>
<user id="102030" nick="nickname" done="false" />
<user id="123456" nick="nickname" done="false" />
</users>
<machines>
<machine id="123" sd="123" ref="" done="false" />
<machine id="456" sd="456" ref="" done="false" />
<machine id="789" sd="789" ref="" done="false" />
</machines>
</order>
I want to delete the user with the id 102030, so the xml looks like this:
<users>
<user id="123456" nick="nickname" done="false" />
</users>
<machines>
<machine id="123" sd="123" ref="" done="false" />
<machine id="456" sd="456" ref="" done="false" />
<machine id="789" sd="789" ref="" done="false" />
</machines>
</order>
This is my code which doesn't work:
XmlDocument doc = XmlDocument.Load(path);
XmlNodeList nodes = doc.GetElementsByTagName("users");
foreach(XmlNode node in nodes){
foreach(XmlAttribute attribute in node.Attributes){
if(attribute.Name== "id" && attribute.Value == "102030"){
node.RemoveAll();
}
}
}
doc.Save(path);
I am a newbie in C# so I need every help!
Thanks in advance, geibi
XmlNode.RemoveAll() does not remove a node. Instead it:
Removes all the child nodes and/or attributes of the current node.
Thus, instead you need to remove the node from its parent:
node.ParentNode.RemoveChild(node);

Swapping nodes in xmlNodeList C#

Assuming having the following xml
<test>
<step>
<var name="name1" />
<var name="name2" />
</step>
<step>
<var name="name3" />
<var name="name4" />
</step>
<step>
<var name="name5" />
<var name="name6" />
</step>
</test>
I m using XmlNodeList, seperated by "step". Is there a way to swap or replace a step directly in the xmlnodelist?
Need to make it like this:
<test>
<step>
<var name="name3" />
<var name="name4" />
</step>
<step>
<var name="name1" />
<var name="name2" />
</step>
<step>
<var name="name5" />
<var name="name6" />
</step>
</test>
You can use XDocument class instead of XMLDocument. This will swap the var nodes name3 with name6.
using System.Linq;
using System.Xml.Linq;
class Test
{
static void Main()
{
XDocument document = XDocument.Load("test.xml");
Swap("name3", "name6", document);
document.Save("test.xml");
}
static void Swap(string nameOne, string nameTwo, XDocument document)
{
var nameOneNode = document.Descendants("var").FirstOrDefault(p => p.Attribute("name").Value == nameOne);
var nameTwoNode = document.Descendants("var").FirstOrDefault(p => p.Attribute("name").Value == nameTwo);
nameOneNode.Attribute("name").Value = nameTwo;
nameTwoNode.Attribute("name").Value = nameOne;
}
}
The order of the nodes in an XML file does not necessarily have to be kept when the XML file is read. For example, if your file looks like this:
<xmlcontent>
<node value="Hello" />
<node value="World" />
</xmlcontent>
The XML read may return the nodes like this:
<xmlcontent>
<node value="World" />
<node value="Hello" />
</xmlcontent>
To apply something like an "order" to XML nodes, you need to add an attribute you can sort by, like
<xmlcontent>
<node index="1" value="Hello" />
<node index="2" value="World" />
</xmlcontent>
In that case, "swapping" two elements would come down to swapping the index values.
Finally managed to do it, here is the code:
XmlDocument xml;
XmlNodeList xmlList;
xml = new XmlDocument();
xml.Load(path);
xmlList = xml.GetElementsByTagName("step");
XmlNode refNode = xmlList[1];
XmlNode newNode = xmlList[0];
xml.DocumentElement.InsertAfter(newNode, refNode);

Xpath and regex for autocompletion filter

I have got a huge xml document.
something like that
<?xml version="1.0" encoding="utf-8"?>
<elements>
<element id="1" name="france" />
<element id="2" name="usa" />
<element id="3" name="Spaïn" />
<element id="4" name="spain and africa" />
<element id="5" name="italie and Spâin" />
</elements>
I want to have something like this :
string str = "spain";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(myXML);
// Xpath with regex or something very veloce
XmlNodeList xmlNodeList = xmlDoc.SelectNodes("//element"+ something);
And the xmlNodeList will contains :
<element id="3" name="Spaïn" />
<element id="4" name="france with spâin and africa" />
<element id="5" name="italie and Spain" />
it must ignore the case
And accent
for the moment I've
XmlNodeList xmlNodeList = xmlDoc.SelectNodes("/*/*[contains(concat(' ',translate(translate(#n,translate(#n, 'aaabcdefghiiijklmnopqrstuvwxyzâÂABCDEFGHïÏIJKLMNOPQRSTUVWXYZ', ''),''), 'âÂABCDEFGHïÏIJKLMNOPQRSTUVWXYZ','aaabcdefghiiijklmnopqrstuvwxyz'),' '),' "+prefix+" ')]");
where #n = #name and prefix is maybe : "spain" or "Spain" or "Spaïn" and it give me 0 solution
Use
//element[contains(concat(' ',translate(#name,'SPAIN','spain'),' '),' spain ')]
Edit: Now, the question has changed, but the answer remains...
Just add these changes in the translation pattern like:
//element[contains(concat(' ',
translate(#name,
'SPAÂâIÏïN',
'spaaaiiin'),
' '),
' spain ')]
Note: Of course, a more general expression would need a more general translation pattern.
string str = "spain";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(myXML);
// Xpath with regex or something very veloce
XmlNodeList xmlNodeList = xmlDoc.SelectNodes("//element[contains(#name,'spain')]");
UPDATE:
As the original problem was changed adding the requirement to recognize the word "Spain" not only in all possible capitalizations but also including accented characters, I have updated the solution below so that now "Spain" with â and/or ïÏ is correctly recognized.
Here is a more generic solution than that of #Alejandro:
If we want to select all elements, whose name attribute contains the word "Spain" in any capitalization and if the possible word delimiters are all non-alphabetic characters, then
This XPath expression:
/*/*[contains(
concat(' ',
translate(translate(#name,
translate(#name, $vAlpha, ''),
' '),
$vUpper,
$vLower),
' '
),
' spain '
)
]
when applied on this XML document:
<elements>
<element id="1" name="france" />
<element id="2" name="usa" />
<element id="3" name="Spaïn" />
<element id="4" name="france with spâin and africa" />
<element id="5" name="-Spain!" />
<element id="6" name="spain and africa" />
<element id="7" name="italie and Spain." />
</elements>
selects the following elements:
<element id="3" name="Spaïn"/>
<element id="4" name="france with spâin and africa"/>
<element id="5" name="-Spain!"/>
<element id="6" name="spain and africa"/>
<element id="7" name="italie and Spain."/>
In the above XPath expression $vLower, $vUpper must be substituted with (respectively):
'aaabcdefghiiijklmnopqrstuvwxyz'
and
'âÂABCDEFGHïÏIJKLMNOPQRSTUVWXYZ'
$vAlpha must be substituted by the concatenation of $vLower and $vUpper .

Categories