C# XML LINQ searching - c#

I have a XML file like this:
<SoftwareComponent schemaVersion="1.0" packageID="Y75WC" releaseID="Y75WC" hashMD5="a190fdfa292276288df38507ea551a3b" path="FOLDER04650736M/1/OptiPlex_3050_1.7.9.exe" dateTime="2017-12-05T05:34:30+00:00" releaseDate="décembre 05, 2017" vendorVersion="1.7.9" dellVersion="1.7.9" packageType="LWXP" identifier="532f5a9e-c087-4499-b40c-cf7921ee06d3" rebootRequired="true">
<Name>
<Display lang="en"><![CDATA[Dell OptiPlex 3050 System BIOS,1.7.9]]></Display>
</Name>
<ComponentType value="BIOS">
<Display lang="en"><![CDATA[BIOS]]></Display>
</ComponentType>
<Description>
<Display lang="en"><![CDATA[This package provides the Dell System BIOS update and is supported on Dell OptiPlex 3050 Tower, OptiPlex 3050 Small Form Factor and OptiPlex 3050 Micro for Windows Operation System.]]></Display>
</Description>
<LUCategory value="NONE">
<Display lang="en"><![CDATA[None]]></Display>
</LUCategory>
<Category value="BI">
<Display lang="en"><![CDATA[FlashBIOS Updates]]></Display>
</Category>
<SupportedDevices>
<Device componentID="159" embedded="0">
<Display lang="en"><![CDATA[OptiPlex 3050 System BIOS]]></Display>
</Device>
</SupportedDevices>
<SupportedSystems>
<Brand key="1" prefix="OP">
<Display lang="en"><![CDATA[Optiplex]]></Display>
<Model systemID="07A3">
<Display lang="en"><![CDATA[3050]]></Display>
</Model>
</Brand>
</SupportedSystems>
<ImportantInfo URL="http://www.dell.com/support/home/us/en/19/Drivers/DriversDetails?driverId=Y75WC" />
<Criticality value="2">
<Display lang="en"><![CDATA[Urgent-Dell highly recommends applying this update as soon as possible. The update contains changes to improve the reliability and availability of your Dell system.]]></Display>
</Criticality>
There are multiple SoftwareComponent Elements inside.
I tried to get some attributes of SoftwareComponent ( dellVersion, hashMD5) based on descendants Elements ( ComponentType value, SupportedSystems->Device->Display value, Criticality value) but all my tests were not good.
See my actual code, I can get all the value in the XML file only:
XDocument doc = XDocument.Load("catalog.xml");
var els = from el in doc.Root.Elements("SoftwareComponent")
select new
{
dellVersion = (string)el.Attribute("dellVersion"),
hashMD5 = (string)el.Attribute("hashMD5"),
path = (string)el.Attribute("path"),
};
foreach (var el in els)
{
Console.WriteLine("dell BIOS: {0}, MD5: {1}, path: {2}", el.dellVersion, el.hashMD5, el.path);
}
Somebody can show me how to proceed please ?
Thanks

First of all, your XML document is missing a </SoftwareComponent> end tag. Maybe you didn't copy the contents OK here.
Then, SoftwareComponent is actually the root in your document, so you would need code like:
XDocument doc = XDocument.Load("catalog.xml");
var el = new
{
dellVersion = (string)doc.Root.Attribute("dellVersion"),
hashMD5 = (string)doc.Root.Attribute("hashMD5"),
path = (string)doc.Root.Attribute("path"),
};
Console.WriteLine("dell BIOS: {0}, MD5: {1}, path: {2}", el.dellVersion, el.hashMD5, el.path);
Your code would work OK as-is if the XML would have the format:
<Root>
<SoftwareComponent schemaVersion="1.0" packageID="Y75WC" releaseID="Y75WC" hashMD5="a190fdfa292276288df38507ea551a3b" path="FOLDER04650736M/1/OptiPlex_3050_1.7.9.exe" dateTime="2017-12-05T05:34:30+00:00" releaseDate="décembre 05, 2017" vendorVersion="1.7.9" dellVersion="1.7.9" packageType="LWXP" identifier="532f5a9e-c087-4499-b40c-cf7921ee06d3" rebootRequired="true">
</SoftwareComponent>
</Root>
XML documents can only have one root node, so you can't have multiple SoftwareComponent as root as you seem to imply.
If you want to get, for example, ComponentType value, you can do:
componentTypeValue = (string)el.Descendants("ComponentType").FirstOrDefault().Attribute("value")
I would actually change the query into a foreach and check that FirstOrDefault result for null.

Related

How do I find a specific XML attribute in a specific place in an XDocument

I have an example of my XML below (which i'm converting from SGML to XML prior to the check but that's besides the point). The XML IS valid at the point of checking.
I need check the XDocument and find out if any of the <version> elements contained within the <status> section at the top of the file contain the attriubute "RNWB". I need to be explicit about the fact that i'm only interested in checking <version> elements that are children of <status> elements because there might be other <version> elements within the document which i don't care about.
<dmodule>
<idstatus>
<dmaddres>
<dmc>Blah</dmc>
<dmtitle><techname>System</techname><infoname>Introduction</infoname></dmtitle>
<issno issno="006" type="revised"/>
<issdate year="2016" month="11" day="30"/>
</dmaddres>
<status>
<security class="2"/>
<rpc></rpc>
<orig></orig>
<applic>
<model model="2093">
<version version="BASE"></version>
<version version="RNWB"></version></model>
</applic>
<techstd>
<autandtp>
<authblk></authblk>
<tpbase></tpbase>
</autandtp>
<authex></authex>
<notes></notes>
</techstd>
<qa>
<firstver type="tabtop"/></qa>
<remarks></remarks>
</status>
</idstatus>
<content>
<refs><norefs></refs>
<descript>
<para0><title>Introduction</title>
<para>The system many new features included which are safe and accurate blah blah blah.</para>
</descript>
</content>
</dmodule>
I've tried all sorts but can't seem to get a result. Here's one example of what i've tried:
var result = (from ele in doc.Descendants("applic").Descendants("version").Attributes("RNWB")
select ele).ToList();
foreach (var v in result)
{
File.Move(file, Path.Combine(outputFolder, fileName)); // move the file to the new folder
}
If you have to be explicit that the version element is within a status element, then you need to be explicit about that in your query too. Your example doesn't include version anywhere.
You then need to find a version attribute with the value RNWB. You're currently looking for an RNWB attribute, which doesn't exist.
var hasFlag = doc
.Descendants("status")
.Descendants("version")
.Attributes("version")
.Any(x => x.Value == "RNWB");
See this fiddle for a demo.
Something like this should work:
// Load document
XDocument _doc = XDocument.Load("C://t//My File2.txt");
Select path to model
Select the elements version
Where the attribute == RNWB
Put them in a list for the result
// if you need to get the version element where the atrribute == something
List<XElement> model = _doc
.XPathSelectElement("dmodule/idstatus/status/applic/model")
.Elements("version")
.Where(x => x.Attribute("version").Value == "RNWB")
.ToList();
// if you only need to check if the attribute exists
bool contains = _doc
.XPathSelectElement("dmodule/idstatus/status/applic/model")
.Elements("version")
.Any(x => x.Attribute("version").Value == "RNWB");
The XML is not in valid format: I made some adjustment and got it working
<dmodule>
<idstatus>
<dmaddres>
<dmc>Blah</dmc>
<dmtitle><techname>System</techname><infoname>Introduction</infoname></dmtitle>
<issno issno="006" type="revised"/>
<issdate year="2016" month="11" day="30"/>
</dmaddres>
<status>
<security class="2"/>
<rpc></rpc>
<orig></orig>
<applic>
<model model="2093">
<version version="BASE"></version>
<version version="RNWB"></version>
</model>
</applic>
<techstd>
<autandtp>
<authblk></authblk>
<tpbase></tpbase>
</autandtp>
<authex></authex>
<notes></notes>
</techstd>
<qa>
<firstver type="tabtop"/></qa>
<remarks></remarks>
</status>
</idstatus>
<content>
<refs>
<norefs>
</norefs>
</refs>
<descript>
<para0>
<title>Introduction</title>
<para>The system many new features included which are safe and accurate blah blah blah.</para>
</para0>
</descript>
</content>
</dmodule>

Parsing Advanced XML File & Turning Information Into Class In C#

What needs to be done
Currently I have 1 Advanced XML file that needs to be parsed. I need to iterate through the file and read each "Entity" tag individually. Altough the issue I come across is reading & iterating through the Stats and Slots. Also, the amount of Stat & Slot tags will vary depending on the Entity. (Yes I have researched this topic but I still can't find a way without creating errors as I need some more guidance. The other posts haven't had the exact fix I've hoped for...)
XML File
<ROOT>
<Entity Type="Clothing" Name="Light Robe" ID="0">
<Armor>2</Armor>
<Weight>1</Weight>
<Usability>120</Usability>
<Rarity>0.1</Rarity>
<Stats>
<Stat Type="Health">10</Stat>
</Stats>
<Slots>
<Slot>Torso</Slot>
</Slots>
</Entity>
<Entity Type="Clothing" Name="Medium Robe" ID="1">
<Armor>4</Armor>
<Weight>2</Weight>
<Stats>
<Stat Type="Health">15</Stat>
</Stats>
<Usability>120</Usability>
<Rarity>0.1</Rarity>
<Slots>
<Slot>Torso</Slot>
</Slots>
</Entity>
<Entity Type="Clothing" Name="Heavy Robe" ID="2">
<Armor>6</Armor>
<Weight>4</Weight>
<Stats>
<Stat Type="Health">25</Stat>
</Stats>
<Usability>120</Usability>
<Rarity>0.1</Rarity>
<Slots>
<Slot>Torso</Slot>
</Slots>
</Entity>
</ROOT>
If anybody has any criticism of this post please say so as I'll edit accordingly.
Use XDocument for XML reading.
// Load Document
XDocument _doc = XDocument.Load("C:\\t\\My File2.txt");
// Get all Entity elements and put them into a list.
List<XElement> employees = _doc.XPathSelectElements("ROOT/Entity").ToList();
// Next you can loop thru the list to check the Entity's elements
foreach (var employee in employees)
{
// to get the armor element:
string armor = employee.Element("Armor").Value;
// to get the rarity element:
string rarity = employee.Element("Rarity").Value;
// to get the Stat element:
string stat = employee.Element("Stats").Element("Stat").Value;
// to get the Slot element:
string slot = employee.Element("Slots").Element("Slot").Value;
}
// To get one element specific by attribute use this (I check on attribute ID):
XElement emp = _doc.XPathSelectElements("ROOT/Entity").FirstOrDefault(c => c.Attribute("ID").Value == "0");
// Next you can extract information from this element just like in the foreach loop.

Linq query to Parse XML

I have the XML shown below. I am trying to parse using C# with LINQ.
<software>
<version>31.0.1</version>
<status>uptime 2d 22h 39m 26s</status>
<wPack>
<rv>
<total>0</total>
<qv>0</qv>
</rv>
<sv>
<total>0</total>
<qv>0</qv>
</sv>
</wPack>
<sPack>
<rv>
<total>242</total>
<qv>1</qv>
</rv>
<sv>
<total>69845</total>
<qv>145</qv>
</sv>
<size>146</size>
</sPack>
<dPack>
<rv>
<total>88560</total>
</rv>
<sv>
<total>0</total>
</sv>
<in>0.28,0.23,0.35</in>
<out>0.00,0.00,0.00</out>
<qv>216806</qv>
<db>mysql</db>
</dPack>
<bClients>
<bClient>
<type>sPackbClient</type>
<id>test1</id>
<IP>127.0.0.1</IP>
<queue>0</queue>
<status>on-line 2d 22h 39m 21s</status>
<ssl>no</ssl>
</bClient>
<bClient>
<type>sPackbClient</type>
<id>test2</id>
<IP>127.0.0.1</IP>
<queue>0</queue>
<status>on-line 2d 22h 39m 18s</status>
<ssl>no</ssl>
</bClient>
<bClient>
<type>sPackbClient</type>
<id>test3</id>
<IP>127.0.0.1</IP>
<queue>0</queue>
<status>on-line 0d 2h 33m 30s</status>
<ssl>no</ssl>
</bClient>
</bClients>
<servers>
<server>
<name>EC1</name>
<admin-id>EC1</admin-id>
<id>EC1</id>
<status>online 8901s</status>
<failed>0</failed>
<qv>0</qv>
<sPack>
<rv>0</rv>
<sv>0</sv>
<in>0.00,0.00,0.00</in>
<out>0.00,0.00,0.00</out>
</sPack>
<dPack>
<rv>0</rv>
<sv>0</sv>
<in>0.00,0.00,0.00</in>
<out>0.00,0.00,0.00</out>
</dPack>
</server>
<server>
<name>EC2</name>
<admin-id>EC2</admin-id>
<id>EC2</id>
<status>online 8918s</status>
<failed>2</failed>
<qv>0</qv>
<sPack>
<rv>79</rv>
<sv>20843</sv>
<in>0.00,0.00,0.00</in>
<out>0.06,0.05,0.08</out>
</sPack>
<dPack>
<rv>35050</rv>
<sv>0</sv>
<in>0.10,0.07,0.14</in>
<out>0.00,0.00,0.00</out>
</dPack>
</server>
<server>
<name>EC3</name>
<admin-id>EC3</admin-id>
<id>EC3</id>
<status>re-connecting</status>
<failed>0</failed>
<qv>0</qv>
<sPack>
<rv>4</rv>
<sv>1671</sv>
<in>0.00,0.00,0.00</in>
<out>0.00,0.00,0.00</out>
</sPack>
<dPack>
<rv>1664</rv>
<sv>0</sv>
<in>0.00,0.00,0.00</in>
<out>0.00,0.00,0.00</out>
</dPack>
</server>
</servers>
</software>
When I try to get <sPack> elements using following query.
var software = (from sw in xDoc.Descendants("software")
from sp in sw.Descendants("sPack")
select sp).ToList();
I get all instance of <sPack> which are under:
<servers>
<server>
<sPack>
What i want is to get <sPack> which comes under <software> and a separate query for parsing <servers>.
You could simply use xDoc.Root.Elements("sPack") to select the sPack child element(s) of the root element and then xDoc.Root.Elements("servers").Elements("server").Elements("sPack") to select the sPack descendants of the server element(s).
The sPack element you're getting as part of your query is a descendant ofsoftware. As software is the root element, all elements within are descendants. The example in the docs shows how this query works.
What you want is Elements, which only returns the child elements, it doesn't involve any recursion.
var software = xDoc.Elements("software").Elements("sPack");
For your second query, you do want to search recursively through all elements. So Descendants is appropriate here:
var servers = xDoc.Descendants("servers").Descendants("sPack");

Using Linq and XDocument, can I get all the child elements under parent tag?

I have an XML
<data>
<summary>
<account curr_desc='USD' acct_nbr='123' net='1000.00' />
<account curr_desc='USD' acct_nbr='456' net='2000.00' />
</summary>
<details>
<accounts>
<account acct_nbr="123" curr="USD">
<activity color='False' settle_date='02 Jul 2010' amt='580.00' />
<activity color='True' settle_date='09 Jul 2010' amt='420.00' />
</account>
<account acct_nbr="456" curr="USD">
<activity color='True' settle_date='12 Dec 2010' amt='1500.00' />
<activity color='True' settle_date='19 Dec 2010' amt='500.00' />
</account>
</accounts>
</details>
</data>
Using Linq and XDocument, I can extract "summary" information but how can I extract "account" information under "summary" tag?
XDocument XMLDoc = XDocument.Load("testdata.xml");
XElement accounts = (from xml2 in XMLDoc.Descendants("summary")
select xml2).FirstOrDefault();
How can I specify something like "summary/account" so that it returns me all the elements under <summary>? Note, that I have <account> under <detail><accounts>, I only want the elements under summary tag.
You should use the Elements method:
var accounts = doc.Root.Elements("summary").Elements("account");
Or, alternatively, XPathSelectElements, which in this case is simpler:
var accounts = doc.XPathSelectElements("/data/summary/account");
In this instance you can also use Descendants, as Andrew Barber suggested, but in general you should only do this when you really want to find all descendants with a given name, and not just immediate children. Otherwise your code does a lot of searching that it doesn't need to, and may return elements you don't want it to.
var accountSummaryElems =
XMLDoc.Element("summary").Elements("account");
This gives you a collection of the account elements under the summary element. You can then iterate them to get the values.
EDITED to use the same pattern you were; I call First() instead of FirstOrDefault() because that code won't run anyway if the "account" element is not found.
Then you have the right idea with iterating over the collection returned.
This returns child elements as a string list not matter where it is.
using System.Xml.Linq;
XDocument xmlDocument = XDocument.Load(fileName);
public List<string> FindChilds(string parentTag)
{
return xmlDocument.Descendants().Where(x => x.Parent != null).Where(x => x.Parent.Name.ToString().Equals(parentTag)).Select(x => x.Name.ToString()).ToList();
}

get this xml value with c#

I need to get the number next to the word text, in this case the number is 1
<SD>
<POPULARITY URL="google.com/" TEXT="1"/>
<REACH RANK="1"/>
<RANK DELTA="+0"/>
</SD>
How can I get the number in c#
Thanks
In addition to the examples above you could try using linq to xml
See below.
var str = #"<ALEXA VER='0.9' URL='google.com/' HOME='0' AID='='>
<SD TITLE='A' FLAGS='DMOZ' HOST='google.com'>
<TITLE TEXT='Google '/>
<ADDR STREET='' CITY='' STATE='' ZIP='' COUNTRY='' />
<CREATED DATE='15-Sep-1997' DAY='15' MONTH='09' YEAR='1997'/>
<PHONE NUMBER='unlisted'/>
<OWNER NAME='unlisted'/>
<EMAIL ADDR='dns-admin#google.com'/>
<LANG LEX='en'/>
<LINKSIN NUM='704402'/>
<SPEED TEXT='1581' PCT='48'/>
<REVIEWS AVG='4.5' NUM='524'/>
<CHILD SRATING='0'/>
<ASSOCS>
<ASSOC ID='googlecom'/></ASSOCS>
</SD>
<KEYWORDS>
<KEYWORD VAL='Mountain View'/>
</KEYWORDS><DMOZ>
<SITE BASE='google.com/' TITLE='Google' DESC='Enables users to search the Web, Usenet, and images. Features include PageRank, caching and translation of results, and an option to find similar pages. The companys focus is developing search technology.'>
<CATS>
<CAT ID='Top/Computers/Internet/Searching/Search_Engines/Google' TITLE='Search Engines/Google' CID='374822'/>
<CAT ID='Top/Regional/North_America/United_States/California/Localities/M/Mountain_View/Business_and_Economy/Industrial/Computers_and_Internet' TITLE='Industrial/Computers and Internet' CID='625367'/>
<CAT ID='Top/World/Arabic/إقليمـي/الشرق_الأوسط/السعودية/تجارة_و_أقتصاد/كمبيوتر_و_إنترنت/محركات_بحث' TITLE='كمبيوتر و إنترنت/محركات بحث' CID='204954'/>
<CAT ID='Top/World/Français/Informatique/Internet/Recherche/Moteurs_de_recherche/Google' TITLE='Moteurs de recherche/Google' CID='247347'/>
</CATS>
</SITE>
</DMOZ>
<SD>
<POPULARITY URL='google.com/' TEXT='1'/>
<REACH RANK='1'/>
<RANK DELTA='+0'/>
</SD>
</ALEXA>";
var item = XElement.Parse(str);
var subSet = item.Elements("SD");
var actualItem = subSet.Where(x => x.Element("POPULARITY") != null).First();
var value = actualItem.Element("POPULARITY").Attribute("TEXT").Value;
Hope this helps
Something like this:
XmlDocument doc = new XmlDocument();
doc.LoadXml( #"<SD> <POPULARITY URL=""google.com/"" TEXT=""1""/> <REACH RANK=""1""/> <RANK DELTA=""+0""/> </SD> ");
XmlNode root = doc.FirstChild;
Debug.WriteLine(root["POPULARITY"].Attributes["TEXT"].InnerXml);
You can try:
XmlDocument doc = new Xmldocument();
doc.Load(stringWithYourXml);
XmlNode node = doc.SelectSingleNode("/SD/POPULARITY");
var val = node.Attributes["TEXT"].Value
Please consider this as a sample ( do some more checks and error detection )

Categories

Resources