C# Linq-to-XML on XCCDF - c#

I'm trying to parse out information from a XCCDF output file but my Linq-to-Xml queries keep returning empty. Here are some of the ones I've tried to this point:
XElement xelement = XElement.Load(s);
IEnumerable<XElement> findings = xelement.Elements();
XNamespace ns = "http://checklists.nist.gov/xccdf/1.1";
var findingDetails = from f in findings.Descendants(ns + "Benchmark")
select new
{
title = f.Element("title").Value
};
foreach (var fd in findingDetails)
{
Console.WriteLine(fd.ToString());
}
I also tried:
var findingDetails = from f in findings.Descendants(ns + "Benchmark")
select f;
var findingDetails = from f in findings.Descendants("Benchmark")
select new
{
title = f.Element("title").Value
};
var findingDetails = from f in findings.Elements(ns + "Benchmark")
select new
{
title = f.Element("title").Value
};
var findingDetails = from f in findings.Elements(ns + "Benchmark")
select f;
Here is a condensed version of the xccdf.xml file. Based on this version how would I get the title "Red Hat..." (line 5) and the title "daemon umask" (line 19)? (I do understand that my examples above do not attempt to get this data, I had to break it down to just trying to get anything...
<?xml version="1.0" encoding="UTF-8"?>
<cdf:Benchmark style="SCAP_1.1" resolved="1" id="RHEL_6_STIG" xsi:schemaLocation="http://checklists.nist.gov/xccdf/1.1 http://nvd.nist.gov/schema/xccdf-1.1.4.xsd http://cpe.mitre.org/dictionary/2.0 http://scap.nist.gov/schema/cpe/2.2/cpe-dictionary_2.2.xsd" xmlns:cdf="http://checklists.nist.gov/xccdf/1.1" xmlns:cpe="http://cpe.mitre.org/dictionary/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xhtml="http://www.w3.org/1999/xhtml">
<cdf:status date="2016-04-22">accepted</cdf:status>
<cdf:title>Red Hat Enterprise Linux 6 Security Technical Implementation Guide</cdf:title>
<cdf:description>The Red Hat Enterprise Linux 6 Security Technical Implementation Guide (STIG) is published as a tool to improve the security of Department of Defense (DoD) information systems. Comments or proposed revisions to this document should be sent via e-mail to the following address: disa.stig_spt#mail.mil.</cdf:description>
<cdf:notice id="terms-of-use"></cdf:notice>
<cdf:reference href="http://iase.disa.mil">
<dc:publisher>DISA</dc:publisher>
<dc:source>STIG.DOD.MIL</dc:source>
</cdf:reference>
<cdf:plain-text id="release-info">Release: 11 Benchmark Date: 22 Apr 2016</cdf:plain-text>
<cdf:platform idref="cpe:/o:redhat:enterprise_linux:6"></cdf:platform>
<cdf:version>1</cdf:version>
<cdf:Profile id="MAC-1_Classified">
<cdf:title>I - Mission Critical Classified</cdf:title>
</cdf:Profile>
<cdf:Value id="var_umask_for_daemons">
<cdf:title>daemon umask</cdf:title>
<cdf:description>Enter umask for daemons</cdf:description>
<cdf:value>022</cdf:value>
<cdf:value selector="022">022</cdf:value>
<cdf:value selector="027">027</cdf:value>
</cdf:Value>
</cdf:Benchmark>

Both Benchmark and title have the namespace http://checklists.nist.gov/xccdf/1.1, so you need to use this if you're going to query for title.
Secondly, you parse using XElement.Parse, so the result is an element representing the Benchmark element. You then get its child elements (status to Value). Then you search for descendants of any of those called Benchmark - you're not going to find any, as Benchmark was where you started.
This should work:
var element = XElement.Load(s);
var findingDetails = new
{
title = (string)element.Element(ns + "title")
};
Alternatively, load as a document:
var doc = XDocument.Load(s);
var findingDetails =
from benchmark in doc.Descendants(ns + "Benchmark")
select new
{
title = (string)benchmark.Element(ns + "title")
};

Try following
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ProgrammingBasics
{
class Exercise
{
const string FILENAME = #"c:\temp\test.xml";
static void Main()
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants().Where(x => x.Name.LocalName == "Benchmark").Select(y => new
{
title = (string)y.Elements().Where(z => z.Name.LocalName == "title").FirstOrDefault()
}).FirstOrDefault();
}
}
}

Related

Linq to XML select node from config.xml (Lambda expression or classic query)

I am using .net 4.0 winforms. In my application a have config file (config.xml), on this file i have lots nodes and child nodes, all are different, i want to select specific node and nodes inside the selected node.
I tried lots of solutions but not succeed.
Thanks in advance for your help.
This should give you the correct result:-
XDocument doc = XDocument.Load(#"XMLFilePath");
XNamespace ns = "http://schemas.datacontract.org/2004/07/Silvio.Settings";
var result = doc.Root.Element(ns + "maintenance_anomalies")
.Descendants(ns + "nom_operation")
.Select(x =>
new
{
NomOperation = (string)x,
statutList = x.Parent.Element(ns + "statuts")
.Elements(ns + "statut")
.Select(z => (string)z).ToList()
}).ToList();
Approach:
From the Xdocument object select the root node which is Main. From this select the Element maintenance_anomalies by including the Namespace associated with it. From there you can select all the descendants of nom_operation and fetch it's value. To find all statut inside nom_operation go back to parent node which is operation and from there select all statut elements.
You can also project a Type instead of anonymous type.
Getting following output:-
I had a couple of issues with your xml. First there is an invalid character so instead of using the Load method. There is also a namespace issue so I used Where method to get the tag maintenance_anomalies.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication53
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
string file = File.ReadAllText(FILENAME);
XDocument doc = XDocument.Parse(file);
XElement maintenance_anomalies = doc.Descendants().Where(x => x.Name.LocalName == "maintenance_anomalies").FirstOrDefault();
XNamespace ns = maintenance_anomalies.Name.Namespace;
var results = maintenance_anomalies.Elements(ns + "operation").Select(x => new{
nom_operation = x.Element(ns + "nom_operation").Value,
statut = string.Join(",",x.Descendants(ns + "statut").Select(y => y.Value).ToArray())
}).ToList();
}
}
}
The line below should let u access the node u requested. Use this is your class using System.Xml;
XmlDocument XmlDocObj = new XmlDocument();
XmlNode UserNameNode = XmlDocObj.SelectSingleNode("maintenance_anomalies");

LINQ to Xml list wrong query

I want to get some data with LINQ out of an xml file, but I don't get it.
This is the xml file:
<Data>
<Customer>
<Name>bla1</Name>
<d1>
<IP>888.888.888.888</IP>
<UserLogin>userxy</UserLogin>
<UserPw>pwxy</UserPw>
</d1>
<d2>
<IP>889.889.889.889</IP>
<UserLogin>userzp</UserLogin>
<UserPw>pwzp</UserPw>
</d2>
</Customer>
</Data>
I want to get e.g. all IPs of a specific Customer into a List<string> but the problem for me is to handle the different elements d1, d2... dn. Because the program don't know the exact name when running.
Obviously my try is wrong..
XDocument root = XDocument.Load(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\Xml.xml");
List<string> IP = new List<string>();
IP = root.Descendants("Customer").Descendants("Name")
.Where(x => x.Element("Name").Value == name)
.Select(x => x.Element("Name").Descendants("IP").ToList<string>();
I want to get e.g. all IPs of a specific Customer into a List
I guess you are looking for something like this (Using Linq2Xml and Xpath)
var xDoc = XDocument.Parse(xmlstring); // XDocument.Load(filename)
string custName = "bla1";
var ips = xDoc.XPathSelectElement("//Customer[Name[text()='" + custName + "']]")
.Descendants("IP")
.Select(x => (string)x)
.ToList();
EDIT
Let's make #Bobson happy by using pure Linq
var ips = xDoc.Descendants("Customer")
.FirstOrDefault(c=>c.Elements("Name").Any(e=>(string)e==custName))
.Descendants("IP")
.Select(x => (string)x)
.ToList();
Here is a solution that is based on LINQ Query Syntax:
string customerName = "bla1";
XElement dataElem = XElement.Parse(dataXml);
var ipAddresses =
from customerElem in dataElem.Elements("Customer")
where (string)customerElem.Element("Name") == customerName
from ipElem in customerElem.Descendants("IP")
select (string)ipElem;
foreach (var ipAddress in ipAddresses)
{
Console.WriteLine("[{0}]", ipAddress);
}
See the following for a complete working example preceded by its expected output. (Also see the live demo.)
Expected Output:
[888.888.888.888]
[889.889.889.889]
Program:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
namespace LinqQuerySyntaxDemo
{
public class Program
{
static public void Main(string[] args)
{
string customerName = "bla1";
XElement dataElem = XElement.Parse(GetXml());
var ipAddresses =
from customerElem in dataElem.Elements("Customer")
where (string)customerElem.Element("Name") == customerName
from ipElem in customerElem.Descendants("IP")
select (string)ipElem;
foreach (var ipAddress in ipAddresses)
{
Console.WriteLine("[{0}]", ipAddress);
}
}
static string GetXml()
{
return
#"<Data>
<Customer>
<Name>bla1</Name>
<d1>
<IP>888.888.888.888</IP>
<UserLogin>userxy</UserLogin>
<UserPw>pwxy</UserPw>
</d1>
<d2>
<IP>889.889.889.889</IP>
<UserLogin>userzp</UserLogin>
<UserPw>pwzp</UserPw>
</d2>
</Customer>
</Data>";
}
}
}
try this it worked for me
var xml = XDocument.Load("IPs.xml");
var Ips = from ip in xml.Descendants("IP")
select ip.Value;
from the above code
line 1 will help you load the xml using the XDocument.Load method,
IPs.xml is the name of the xml file
.once u have the xml loaded with the second line of code you will have Ips as the list of string which contains the inner text of the
element IP in your xml

How to get an element that has : in its name?

I need to get the CountryName from this XML: http://api.hostip.info/?ip=12.215.42.19
The response XML is:
<HostipLookupResultSet version="1.0.1"
xmlns:gml="http://www.opengis.net/gml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.hostip.info/api/hostip-1.0.1.xsd">
<gml:description>This is the Hostip Lookup
Service</gml:description>
<gml:name>hostip</gml:name>
<gml:boundedBy>
<gml:Null>inapplicable</gml:Null>
</gml:boundedBy>
<gml:featureMember>
<Hostip>
<ip>12.215.42.19</ip>
<gml:name>Sugar Grove, IL</gml:name>
<countryName>UNITED STATES</countryName>
<countryAbbrev>US</countryAbbrev>
<!-- Co-ordinates are available as lng,lat -->
<ipLocation>
<gml:pointProperty>
<gml:Point srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">
<gml:coordinates>-88.4588,41.7696</gml:coordinates>
</gml:Point>
</gml:pointProperty>
</ipLocation>
</Hostip>
</gml:featureMember>
</HostipLookupResultSet>
Problem is I can't include : in the Descendants method because it throws:
XmlException: The ':' chracater,
hexadecimal value 0x3A, cannot be
included in a name.
Thanks
try this
var descendants = from i in XDocument.Load(xml).Descendants("Hostip")
select i.Element("countryName");
Update
complete code for downloading the xml and finding the name of countryName
string xml;
using(var web = new WebClient())
{
xml = web.DownloadString("http://api.hostip.info/?ip=12.215.42.19");
}
var descendants = from i in XDocument.Parse(xml).Descendants("Hostip")
select i.Element("countryName");
A small example on how to apply namespaces in LINQ to XML:
XElement doc = XElement.Load("test.xml");
XNamespace ns = "http://www.opengis.net/gml";
var firstName = doc.Descendants(ns + "name").First().Value;
You need to reference the gml namespace; once you've done that you should be able to navigate using the tag names that appear to the right of "gml:"
UPDATE
I'm not sure what context you're applying this to, but here's a sample console app that works:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace LinqToXmlSample
{
class Program
{
static void Main(string[] args)
{
XElement x = XElement.Load("http://api.hostip.info/?ip=12.215.42.19");
foreach (XElement hostip in x.Descendants("Hostip"))
{
string country = Convert.ToString(hostip.Element("countryName").Value);
Console.WriteLine(country);
}
Console.ReadLine();
}
}
}
var gml = (XNamespace)"http://www.opengis.net/gml";
var doc = XDocument.Load(...) or XDocument.Parse(...);
var name = doc.Descendants(gml + "featureMember").Descendants("countryName").First().Value;
Or you could go brute force and strip all the namespaces:
void RemoveNamespace(XDocument xdoc)
{
foreach (XElement e in xdoc.Root.DescendantsAndSelf())
{
if (e.Name.Namespace != XNamespace.None)
{
e.Name = XNamespace.None.GetName(e.Name.LocalName);
}
if (e.Attributes().Any(a => a.IsNamespaceDeclaration || a.Name.Namespace != XNamespace.None))
{
e.ReplaceAttributes(e.Attributes().Select(a => a.IsNamespaceDeclaration ? null : a.Name.Namespace != XNamespace.None ? new XAttribute(XNamespace.None.GetName(a.Name.LocalName), a.Value) : a));
}
}
}

Help in parsing XML, simple string - but I can't seem to parse it

I have the following XML:
<iq xmlns="jabber:client" to="39850777771287777738178727#guest.google.com/agsXMPP" xml:lang="en" id="sub23" from="search.google.com" type="result">
<pubsub xmlns="http://jabber.org/protocol/pubsub">
<subscription subscription="subscribed" subid="5077774B57777BD77770" node="search" jid="39850777771287777738178727#guest.google.com/agsXMPP" />
</pubsub>
</iq>
I've tried parsing with linq to sql, but it doesn't seem to understand that these are different nodes. It groups the whole iq into a single element.
Can anyone help in parsing this using XML?
The data I want to get is the subid="5077774B57777BD77770" and the id="sub23"
Thanks!
Edit:
Here's the code I have, tried doing it in two ways:
XDocument doc = XDocument.Parse("<xml>" + iq.ToString() + "</xml>");
var results = from feed in doc.Elements("xml")
select new
{
Id = (string)feed.Element("iq").Attribute("id"),
Subid = (string)feed.Element("iq").Element("pubsub").Element("subscription").Attribute("subid")
};
and
var doc = new System.Xml.XmlDocument();
doc.LoadXml(iq.ToString());
var searchId = doc.Attributes["id"];
var subid = doc.SelectSingleNode("/pubsub/subscription").Attributes["subid"];
As Dimitre pointed out, you have a namespace issue. This will work:
using System;
using System.Xml;
namespace XMLTest
{
class Program
{
static void Main(string[] args)
{
XmlDocument doc = new XmlDocument();
XmlNamespaceManager namespaces = new XmlNamespaceManager(doc.NameTable);
namespaces.AddNamespace("ns1", "jabber:client");
namespaces.AddNamespace("ns2", "http://jabber.org/protocol/pubsub");
doc.Load("xmltest.xml");
XmlNode iqNode = doc.SelectSingleNode("/ns1:iq", namespaces);
string ID = iqNode.Attributes["id"].Value;
Console.WriteLine(ID);
XmlNode subscriptionNode = doc.SelectSingleNode("/ns1:iq/ns2:pubsub/ns2:subscription", namespaces);
string subID = subscriptionNode.Attributes["subid"].Value;
Console.WriteLine(subID);
Console.ReadLine();
}
}
}
Read this for an explanation and a complete code example how to evaluate an XPath expression that contains location steps with nodes whose names are in a default namespace and are unprefixed in the XML document..
I'm not sure if this is what you're after, but it works:
XNamespace jabber = "jabber:client";
XNamespace pubsub = "http://jabber.org/protocol/pubsub";
string xmltext = "<iq xmlns=\"jabber:client\" to=\"39850777771287777738178727#guest.google.com/agsXMPP\" xml:lang=\"en\" id=\"sub23\" from=\"search.google.com\" type=\"result\">\n"
+ "<pubsub xmlns=\"http://jabber.org/protocol/pubsub\">\n"
+ "<subscription subscription=\"subscribed\" subid=\"5077774B57777BD77770\" node=\"search\" jid=\"39850777771287777738178727#guest.google.com/agsXMPP\" />\n"
+ "</pubsub>\n"
+ "</iq>";
XDocument xdoc = XDocument.Parse(xmltext);
var iqelem = xdoc.Element(jabber + "iq");
var id = iqelem.Attribute("id").Value;
var subselem = iqelem.Element(pubsub + "pubsub").Element(pubsub + "subscription");
var subid = subselem.Attribute("subid").Value;
Console.WriteLine("SubId = {0}\nId={1}", subid, id);
I concur with Dimitre - the "empty" xmlns namespace at the top is probably causing the problem. I sometimes strip these out with a regex if they're not used, otherwise play around with XmlNameSpaceManager as described

XPath and *.csproj

I am for sure missing some important detail here. I just cannot make .NET's XPath work with Visual Studio project files.
Let's load an xml document:
var doc = new XmlDocument();
doc.Load("blah/blah.csproj");
Now execute my query:
var nodes = doc.SelectNodes("//ItemGroup");
Console.WriteLine(nodes.Count); // whoops, zero
Of course, there are nodes named ItemGroup in the file. Moreover, this query works:
var nodes = doc.SelectNodes("//*/#Include");
Console.WriteLine(nodes.Count); // found some
With other documents, XPath works just fine.
I am absolutely puzzled about that. Could anyone explain me what is going on?
You probably need to add a reference to the namespace http://schemas.microsoft.com/developer/msbuild/2003.
I had a similar problem, I wrote about it here. Do something like this:
XmlDocument xdDoc = new XmlDocument();
xdDoc.Load("blah/blah.csproj");
XmlNamespaceManager xnManager =
new XmlNamespaceManager(xdDoc.NameTable);
xnManager.AddNamespace("tu",
"http://schemas.microsoft.com/developer/msbuild/2003");
XmlNode xnRoot = xdDoc.DocumentElement;
XmlNodeList xnlPages = xnRoot.SelectNodes("//tu:ItemGroup", xnManager);
Look at the root namespace; you'll have to include an xml-namespace manager and use queries like "//x:ItemGroup", where "x" is your designated alias for the root namespace. And pass the manager into the query. For example:
XmlDocument doc = new XmlDocument();
doc.Load("my.csproj");
XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);
mgr.AddNamespace("foo", doc.DocumentElement.NamespaceURI);
XmlNode firstCompile = doc.SelectSingleNode("//foo:Compile", mgr);
I posted a LINQ / Xml version over at:
http://granadacoder.wordpress.com/2012/10/11/how-to-find-references-in-a-c-project-file-csproj-using-linq-xml/
But here is the gist of it. It may not be 100% perfect......but it shows the idea.
I'm posting the code here, since I found this (original post) when searching for an answer. Then I got tired of searching and wrote my own.
using System;
using System.Linq;
using System.Xml.Linq;
string fileName = #"C:\MyFolder\MyProjectFile.csproj";
XDocument xDoc = XDocument.Load(fileName);
XNamespace ns = XNamespace.Get("http://schemas.microsoft.com/developer/msbuild/2003");
//References "By DLL (file)"
var list1 = from list in xDoc.Descendants(ns + "ItemGroup")
from item in list.Elements(ns + "Reference")
/* where item.Element(ns + "HintPath") != null */
select new
{
CsProjFileName = fileName,
ReferenceInclude = item.Attribute("Include").Value,
RefType = (item.Element(ns + "HintPath") == null) ? "CompiledDLLInGac" : "CompiledDLL",
HintPath = (item.Element(ns + "HintPath") == null) ? string.Empty : item.Element(ns + "HintPath").Value
};
foreach (var v in list1)
{
Console.WriteLine(v.ToString());
}
//References "By Project"
var list2 = from list in xDoc.Descendants(ns + "ItemGroup")
from item in list.Elements(ns + "ProjectReference")
where
item.Element(ns + "Project") != null
select new
{
CsProjFileName = fileName,
ReferenceInclude = item.Attribute("Include").Value,
RefType = "ProjectReference",
ProjectGuid = item.Element(ns + "Project").Value
};
foreach (var v in list2)
{
Console.WriteLine(v.ToString());
}

Categories

Resources