LINQ to Xml list wrong query - c#

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

Related

Extracting XML values in multidimensional array c#

I have an xml file as below, and I need to extract values and put them inside a multidimensional array. The idea is, when I have more than one tag <string> per root element <Etiquette>, I need to repeat the same other values with each different value of the tag <string>
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfEtiquette xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Etiquette>
<BgColor>#8075D1C5</BgColor>
<BorderColor>#FF4E5B6F</BorderColor>
<AssociatedAffaireId>
<string>d4689f33-5600-47fe-883d-efcbf5e469c2</string>
<string>1bae35dd-d501-4d87-bdd4-147fc0ba29d2</string>
</AssociatedAffaireId>
<Label>Ouverte</Label>
</Etiquette>
<Etiquette>
<BgColor>#80949CA8</BgColor>
<BorderColor>#FF155E70</BorderColor>
<AssociatedAffaireId>
<string>203cc4a8-8c24-4a2d-837c-29c7c1f73007</string>
</AssociatedAffaireId>
<Label>Fermée</Label>
</Etiquette>
</ArrayOfEtiquette>
Desired result:
{"#8075D1C5","#FF4E5B6F","d4689f33-5600-47fe-883d-efcbf5e469c2","Ouverte"}
{"#8075D1C5","#FF4E5B6F","1bae35dd-d501-4d87-bdd4-147fc0ba29d2","Ouverte"}
{"#80949CA8","#FF155E70","203cc4a8-8c24-4a2d-837c-29c7c1f73007","Fermée"}
Regards,
Using Xml Linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication157
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("Etiquette")
.SelectMany(x => x.Descendants("string")
.Select(y => new { BgColor = (string)x.Element("BgColor"), BorderColor = (string)x.Element("BorderColor"), UID = (string)y }))
.ToList();
}
}
}
I you want just an array instead of anonymous type use :
new string[] { (string)x.Element("BgColor"), (string)x.Element("BorderColor"), (string)y }
You can try with XDocument
XDocument xdoc = XDocument.Load("XMLFile7.xml");
var mdAarray = xdoc.Descendants("Etiquette")
.SelectMany(etiquette =>
etiquette.Descendants("string")
.Select(associatedaffaire => new string[] {
etiquette.Element("BgColor").Value.ToString(),
etiquette.Element("BorderColor").Value.ToString(),
associatedaffaire.Value.ToString(),
etiquette.Element("Label").Value.ToString() }))
.ToArray();
Console.WriteLine(JsonConvert.SerializeObject(mdAarray));
OUTPUT
[
["#8075D1C5","#FF4E5B6F","d4689f33-5600-47fe-883d-efcbf5e469c2","Ouverte"],
["#8075D1C5","#FF4E5B6F","1bae35dd-d501-4d87-bdd4-147fc0ba29d2","Ouverte"],
["#80949CA8","#FF155E70","203cc4a8-8c24-4a2d-837c-29c7c1f73007","Fermée"]
]
you only need to iterate first on Etiquette then iterate again on AssociatedAffaireId
Each time you can insert inside the array or list (I will use list for simplicity)
XDocument xdoc = XDocument.Load("pathToXml.xml");
// iterate all Etiquette elements
foreach (var etiquette in xdoc.Root.Elements("Etiquette"))
{
// store common values
string bgColor = etiquette.Element("BgColor").Value;
string borderColor = etiquette.Element("BorderColor").Value;
string label = etiquette.Element("Label").Value;
// iterate all AssociatedAffaireId.string elements and add to list
var associatedAffaireIdEl = etiquette.Element("AssociatedAffaireId");
foreach (var associatedAffaireId in associatedAffaireIdEl.Elements("string"))
{
string aaid = associatedAffaireId.Value;
listOfArray.Add(new string[]{bgColor, borderColor, aaid, label});
}
}
I hope this could help.
Sorry I found some errors. Check out my fiddle here.

C# Linq-to-XML on XCCDF

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();
}
}
}

Unable to get list from xml using xPathNavigator

List<string> list = new List<string>();
foreach (XPathNavigator node in nav.Select("configuration/company/work/worktime"))
{
string day = getAttribute(node, "day");
string time = getAttribute(node, "time");
string worktype = ?? // how to get worktype attribute valuefrom parent node
list.Add(day,time,worktype); // add to list
}
</configuration>
<company>
<work worktype="homeWork">
<worktime day="30" time="10:28"></worktime>
<worktime day="25" time="10:50"></worktime>
</work>
<work worktype="officeWork">
<worktime day="12" time="09:28"></worktime>
<worktime day="15" time="12:28"></worktime>
</work>
</company>
</configuration>
need output as :
list[0] = homeWork,30,10:28
list[1] = homeWork,25,10:50
list[2] = officeWork,12,09:28
list[3] = officeWork,15,12:28
I am trying to get the list from XML but failed to get output like given above (using xpath navigator, how can I access parent node to get worktype attribute, and other remaining inner node attribute?
I'd suggest using LINQ to XML over XPath, but if you must use XPathNavigator then you need to iterate each work element followed by each of its worktime child elements. This way you can use the worktype from the parent context:
foreach (XPathNavigator work in nav.Select("configuration/company/work"))
{
var workType = work.GetAttribute("worktype", string.Empty);
foreach (XPathNavigator worktime in work.Select("worktime"))
{
var day = worktime.GetAttribute("day", string.Empty);
var time = worktime.GetAttribute("time", string.Empty);
list.Add($"{workType}, {day}, {time}");
}
}
See this fiddle for a working demo.
Use a nested loop. Initially retrieve the work nodes with configuration/company/work. Retrieve the worktype attribute and store in a variable. Then loop through the child worktype nodes and add a string to the list for each one
Use Net Library enhanced xml (linq xml)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("work").Select(x => new {
worktype = (string)x.Attribute("worktype"),
worktime = x.Elements("worktime").Select(y => new {
day = (int)y.Attribute("day"),
time = (DateTime)y.Attribute("time")
}).ToList()
}).ToList();
}
}
}

Updating a specific XML node

I am new to XML files and how to manage them. This is for a web app I am writing (aspx).
At the present time I am able to find the first instance of a node and add an item to it with the following code:
xmlClone.Element("PCs").Element("PC").Element("pc_hwStatus").AddAfterSelf(new XElement("user_name", txt_v0_nombre.Text));
What I really want is to add ("user_name", txt_v0_nombre.Text) to a node in particular, not the first one. The content of my XML file is:
<PCs>
<PC>
<pc_name>esc01</pc_name>
<pc_ip>10.10.10.10</pc_ip>
<pc_hwStatus>Working</pc_hwStatus>
</PC>
<PC>
<pc_name>esc02</pc_name>
<pc_ip>10.10.10.11</pc_ip>
<pc_hwStatus>Under Maintenance</pc_hwStatus>
</PC>
</PCs>
The decision of what node to update is made selecting an item from a dropdown list (the PC name).
With my current code, the new item is always added as last line of node with "pc_
name = esc01". I want to be able to added it to esc02 or esc03 and so on... How can this be accomplished? (Using xdocument)
If I understand you correctly, what you are looking for is the FirstOrDefault extension method. In there specify which node you are wanting, in this case a string from your dropdown box, which can be passed in. So to get the first node:
var pc = xmlClone.Element("PCs").Elements("PC").FirstOrDefault(e => e.Element("pc_name").Value == "esc01");
Now you have this in your XElement:
<PC>
<pc_name>esc01</pc_name>
<pc_ip>10.10.10.10</pc_ip>
<pc_hwStatus>Working</pc_hwStatus>
</PC>
To get any element like that, just replace this clause:
.FirstOrDefault(e => e.Element("pc_name").Value == "esc01");
with this one
.FirstOrDefault(e => e.Element("pc_name").Value == desiredPC);
where desiredPC is the value of the xml node: pc_name.
Now to add your data just call the plain old Add method:
pc.Add(new XElement("user_name", txt_v0_nombre.Text);
That should do the trick for you.
Here's a solution that uses LINQ query syntax with LINQ to XML:
XDocument document = XDocument.Parse(xmlContent);
string pcName = "esc02";
IEnumerable<XElement> query =
from pc in document.Element("PCs").Elements("PC")
where pc.Element("pc_name").Value.Equals(pcName)
select pc;
XElement xe = query.FirstOrDefault();
if (xe != null)
{
xe.Add(new XElement("user_name", "DMS"));
}
I have incorporated your sample data and this query into a demonstration program. Please see below for the output from the demonstration program followed by the program itself.
Expected Output
<PC>
<pc_name>esc02</pc_name>
<pc_ip>10.10.10.11</pc_ip>
<pc_hwStatus>Under Maintenance</pc_hwStatus>
<user_name>DMS</user_name>
</PC>
Demonstration Program
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace LinqToXmlDemo
{
public class Program
{
public static void Main(string[] args)
{
string xmlContent = GetXml();
XDocument document = XDocument.Parse(xmlContent);
XElement xe = FindPCName(document, "esc02");
if (xe != null)
{
xe.Add(new XElement("user_name", "DMS"));
Console.WriteLine(xe);
}
else
{
Console.WriteLine("Query returned no results.");
}
}
private static XElement FindPCName(XDocument document, String pcName)
{
IEnumerable<XElement> query =
from pc in document.Element("PCs").Elements("PC")
where pc.Element("pc_name").Value.Equals(pcName)
select pc;
return query.FirstOrDefault();
}
private static String GetXml()
{
return
#"<?xml version='1.0' encoding='utf-8'?>
<PCs>
<PC>
<pc_name>esc01</pc_name>
<pc_ip>10.10.10.10</pc_ip>
<pc_hwStatus>Working</pc_hwStatus>
</PC>
<PC>
<pc_name>esc02</pc_name>
<pc_ip>10.10.10.11</pc_ip>
<pc_hwStatus>Under Maintenance</pc_hwStatus>
</PC>
</PCs>";
}
}
}
Method .Element returns the first element with the specified name.
You can get the whole list with method .Elements, and iterate this list to find the one you are looking for.

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));
}
}
}

Categories

Resources