I've got XML set up like this
<customers>
<customer id="1">
<name title="Mr" first="John" last="Smith" />
<contact number="07123123123" email="john.smith#johnsmith.com" />
<address postcode="E1 1EW">1 Paper Street, London, England, GB</address>
</customer>
(...)
</customers>
I'm trying to query it with Linq to XML for learning purposes. So far I can XDocument.Load the file fine and add/remove etc. But I can't seem to figure out a way to query my XML documents for use in an if block. for example something like (Pseudo code):
XDocument document = XDocument.Load("People.xml");
if(
exists(
document.customers.customer.name.first.value("john")
&& document.customers.customer.name.last.value("smith")
)
)
{
bool exists = true;
}
Whatever I try something the compiler will either laugh at me or spit out something about how it cannot implicitly convert an Ienumerable bool to a bool.
I've been trying many combinations of things from many different google searches for a while now and I think guessing is starting to do more harm than good, can anybody provide a C# snippet that would work in an if block for my xml setup? Just to see if first and last name exist together in the same node. Usually once I see something actually work I can take it from there. Most of the questions I find on the net are only searching to see if the entire node exists or are only searching for one attribute and I just can't seem to tailor it.
(Before anybody bursts into flames over my XML structure, this is not for production purposes, I just want to get a grasp of using this in case I need to in an upcoming project.)
Bonus love for anybody that can link any documentation that's not MSDN (I already have about 10 tabs open on it) and involves some good low level/beginner tutorials on Linq to XML.
To check for John Smith exists as a customer in your XML you ll use
XDocument doc = XDocument.Load(Path);
var customers = doc.Descendants("customer");
//To Check for John Smith
if (customers.Elements("name")
.Any(E => E.Attribute("first").Value == "John"
&& E.Attribute("last").Value == "Smith"))
{
//Do your thing
}
Created a boolean called check to hold the result.
do a count on how many elements called name have attributes First=john and last=smith
Then set the result to check variable.
BTW your XML was missing a " in front of John. That will cause you problems :)
bool check;
var res = XDocument.Load(#"c:\temp\test.xml");
var results = res.Descendants("name")
.Where(x => x.Attribute("first").Value == "john" && x.Attribute("last").Value == "smith")
.Select(x => x.Elements()).Count();
check = results != 0;
XPath is your friend
using System.Xml.XPath;
...
void foo(){
XDocument document = XDocument.Load("People.xml");
var firstCustomerNode = document.XPathSelectElement(
"/customers/customer[0]/name"
);
var hasfirstNameAndLastName = firstCustomerNode.Attribute("firstname") != null && firstCustomerNode.Attribute("lastname") != null;
if(hasfirstNameAndLastName)
{
}
}
Please note that you may also use a schema to validate your Xml, but it's more complex to write.
If you don't want to use XPath, you may also write:
var firstCustomerNode = document.Root.Element("Customers").Elements("customer").First().Element("name");
But honestly, the XPath query is far more readable, and will simplify the error check. This code suppose all the elements up to the target node exists. If not, your code will fail with a poor NullReferenceException.
You need to start by querying the Document.Root.Descendants. Then a method such as IfExists
var nodes = document.Root.Descendants("customer");
bool IfExists(string FirstName, string LastName) {
return nodes.Elements("name").Any(node =>
node.Attribute("first").Value.Equals(FirstName) &&
node.Attribute("last").Value.Equals(LastName));
}
You may want to add exception handling in case an attribute is missing or contains an empty value.
Related
I got a error when i get lat and long using locationService.GetLatLongFromAddress
The error is :
Sequence contains no elements
I have tried this code
var locationService = new GoogleLocationService();
var points = locationService.GetLatLongFromAddress("Ram Theatre Bus Stop, Arcot Road, Vadapalani, Chennai, Tamil Nadu");
mapDetail.Latitude = points.Latitude;
mapDetail.Longitude = points.Longitude;
mapDetail.CollegeAddressId = addressDetail[i].CollegeAddressId;
What is the problem? How can i solve this?
You would typically get that if code uses .First() or .Single() on a sequence (IEnumerable<T>) that has (as the message suggests): no elements. Meaning: an empty sequence (not to be confused with a null sequence). You don't show code that does that, so I can only assume this happens inside .GetLatLongFromAddress(). So it sounds like there is a bug, probably relating to the "not found" case, but in code that we can't see. Personally, I would expect the "not found" case to return a null, or to throw some explicit "not found" exception. If this bug is inside a library: tell the library authors about it. Or better: fix it, and submit a pull request (if you can).
Edit: here we go:
XDocument doc = XDocument.Load(string.Format(APIUrlLatLongFromAddress,
Uri.EscapeDataString(address)));
var els = doc.Descendants("result").Descendants("geometry")
.Descendants("location").First();
if (null != els) {...}
IMO, that should be:
XDocument doc = XDocument.Load(string.Format(APIUrlLatLongFromAddress,
Uri.EscapeDataString(address)));
var els = doc.Descendants("result").Descendants("geometry")
.Descendants("location").FirstOrDefault();
if (null != els) {...}
One line code fix to send them...
I've merged in Mark Gravell's pull request for the GoogleLocationService and have pushed an updated Nuget package.
https://www.nuget.org/packages/GoogleMaps.LocationServices/
I'm rewriting some code and trying to use Linq whenever I can. My problem right now is a simple file import function.
The file basically looks like this:
A1
B1|1882
C1|7315808907578
B9
B1|1883
C1|4025595431639
C1|8717734091786
C1|8717734091809
C1|8717734098280
C1|8717795041874
C1|8717795060998
C1|8717795108973
B9
B1|1884
C1|4025595732071
C1|7315808887184
C1|8717734001983
B9
A9
The existing code looks like this:
line = reader.ReadLine();
string itemNo = "";
List<AltItems> list = new List<AltItems>();
while (line != null) {
posts = line.Split(postSep);
if (posts.Length == 0) { }
else if (posts[0].Equals("A1")) { }
else if (posts[0].Equals("B1"))
itemNo = posts[1];
else if (posts[0].Equals("C1")) {
string altItemNo = posts[1];
list.Add(new AltItems() { ItemNo = itemNo, AltItemNo = altItemNo });
}
line = reader.ReadLine();
}
What would the equivalent Linq query look like? Any thoughts about performance penalties rewriting this code using Linq? I also have files with multiple hierarchical levels where data from items on the first level is needed to build up items on the third level...
I would agree with Ahmed ilyas. Rewriting everything in LINQ does not mean it would work faster.
Especially, regarding string parsing, you should know that it is not supported in LINQ. This is absolutely reasonable if you think that LINQ is a query language targeting data sources of many kinds in a unified way. There are ways to do it, but they are always too indirect and I don't particularly like them. One of them, is using Scott Guthry's DynamicLinq library, the way it suggested here.
Please note that I only show you a way to do string parsing using LINQ. This does not mean that I would suggest using LINQ to replace conditional code.
Hope I helped!
I find it puzzling to determine the best way to parse some XML. It seems they are so many possible ways and none have really clicked with me.
My current attempt looks something like this:
XElement xelement = XElement.Parse(xmlText);
var name = xelement.Element("Employee").Attribute("name").Value;
So, this works. But it throws an exception if either the "Employee" element or the "name" attribute is missing. I don't want to throw an exception.
Exploring some examples available online, I see code like this:
XElement xelement = XElement.Load("..\\..\\Employees.xml");
IEnumerable<XElement> employees = xelement.Elements();
Console.WriteLine("List of all Employee Names :");
foreach (var employee in employees)
{
Console.WriteLine(employee.Element("Name").Value);
}
This would seem to suffer from the exact same issue. If the "Name" element does not exist, Element() returns null and there is an error calling the Value property.
I need a number of blocks like the first code snippet above. Is there a simple way to have it work and not throw an exception if some data is missing?
You can use the combination of the explicit string conversion from XAttribute to string (which will return null if the operand is null) and the FirstOrDefault method:
var name = xelement.Elements("Employee")
.Select(x => (string) x.Attribute("name"))
.FirstOrDefault();
That will be null if either there's no such element (because the sequence will be empty, and FirstOrDefault() will return null) or there's an element without the attribute (in which case you'll get a sequence with a null element, which FirstOrDefault will return).
I often use extension methods in cases like this as they work even if the reference is null. I use a slightly modified version of the extension method's from Anders Abel's very good blog posting from early 2012 'Null Handling with Extension Methods':
public static class XElementExtension
{
public static string GetValueOrDefault(this XAttribute attribute,
string defaultValue = null)
{
return attribute == null ? defaultValue : attribute.Value;
}
public static string GetAttributeValueOrDefault(this XElement element,
string attributeName,
string defaultValue = null)
{
return element == null ? defaultValue : element.Attribut(attributeName)
.GetValueOrDefault(defaultValue);
}
}
If you want to return 'null' if the element or attribute doesn't exist:
var name = xelement.Element("Employee")
.GetAttributeValueOrDefault("name" );
If you want to return a default value if the element or attribute doesn't exist:
var name = xelement.Element("Employee")
.GetAttributeValueOrDefault("name","this is the default value");
To use in your for loop:
XElement xelement = XElement.Load("..\\..\\Employees.xml");
IEnumerable<XElement> employees = xelement.Elements();
Console.WriteLine("List of all Employee Names :");
foreach (var employee in employees)
{
Console.WriteLine(employee.GetAttributeValueOrDefault("Name"));
}
You could always use XPath:
string name = xelement.XPathEvaluate("string(Employee/#name)") as string;
This will be either the value of the attribute, or null if either Employee or #name do not exist.
And for the iterative example:
foreach (XNode item in (IEnumerable)xelement.XPathEvaluate("Employee/Name"))
{
Console.WriteLine(item.Value);
}
XPathEvaluate() will only select valid nodes here, so you can be assured that item will always be non-null.
It all depends on what you want to do with the data once you've extracted it from the XML.
You would do well to look at languages that are designed for XML processing, such as XSLT and XQuery, rather than using languages like C#, which aren't (though Linq gives you something of a hybrid). Using C# or Java you're always going to have to do a lot of work to cope with the fact that XML is so flexible.
Use the native XmlReader. If your problem is reading large XML files instead of allowing the XElement to build an object representation, you can build something like Java SAX parser that only stream the XML.
Ex:
http://www.codeguru.com/csharp/csharp/cs_data/xml/article.php/c4221/Writing-XML-SAX-Parsers-in-C.htm
I have a large XML document and I am using C# to query the content. I have something similar to the following:
var bookA = xmlDoc.SelectSingleNode("/books/book[yearPublished=2012 and id=123]");
var bookB = xmlDoc.SelectSingleNode("/books/book[yearPublished=2012 and id=456]");
var bookC = xmlDoc.SelectSingleNode("/books/book[yearPublished=2012 and id=789]");
As you can see, the filter on "yearPublished" is repeated in each query and, please correct me if I'm wrong, the entire list of books is parsed repeatedly. Would it be more efficient to have something like the following:
var newBooks = xmlDoc.SelectNodes("/books/book[yearPublished=2012]");
var bookA = newBooks.SelectSingleNode("book[id=123]");
var bookB = newBooks.SelectSingleNode("book[id=456]");
var bookC = newBooks.SelectSingleNode("book[id=789]");
Assuming my document is large (let's say it contains the data about several thousand books), am I correct that it would be more efficient to filter the data based on the first criteria and then select the desired XmlNode from the filtered list?
Secondly, I was trying to validate my assumption but I am having trouble. I get an error message about SelectSingleNode:
'System.Xml.XmlNodeList' does not contain a definition for
'SelectSingleNode' and no extension method 'SelectSingleNode'
accepting a first argument of type 'System.Xml.XmlNodeList' could be
found (are you missing a using directive or an assembly reference?)
I have a reference to System.Xml and also "using System.Xml". Am I missing something else?
The SelectNodes method returns an XmlNodeList type. The method SelectSingleNode belongs to the XmlNode class.
You can get your books like this:
var bookA = newBooks.Where(x => x.Attributes["id"].Value == 123);
var bookB = newBooks.Where(x => x.Attributes["id"].Value == 456);
var bookC = newBooks.Where(x => x.Attributes["id"].Value == 789);
var newBooks = xmlDoc.SelectNodes("/books/book[yearPublished=2012]");
This will return a XmlNodeList object. This object does not contain a method called SelectSingleNode, which is why the compiler is telling you that.
You can iterate over nodes in the XmlNodeList returned from the SelectNodes method like so:
foreach (XmlNode node in newBooks)
{
//...
}
As for your performance issue, or perceived performance issue, I suggest you load a file of your desired size and benchmark it. Only then can you tell if you have a performance problem, as you will have measured it.
You can evaluate an XPath expression that has variable references -- see XPathExpression.SetContext() and this example how to implement an IXsltContextVariable.
I want to get all visible columns (Hidden == false) for specific list in sharePoint site, I tried to look through the SharePointWebService.Lists.GetList(listName), but couldn't find anything useful, also checked the methods provided by the Lists WebService and also nothing new,
Please advice.
You can use the GetListAndView method of the Lists web service to get the schemas for the list and a view.
From the documentation, if you leave the viewName parameter empty, the default view will be returned. Then, you can read the <ViewFields></ViewFields> node for the list of fields.
*Edit*
Turns out using XPath to query the returned XML was tougher than I thought... here is what I came up with:
XmlNode result = webService.GetListAndView("My Pictures", string.Empty);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(result.OwnerDocument.NameTable);
nsmgr.AddNamespace("sp", "http://schemas.microsoft.com/sharepoint/soap/");
string xpathQuery = "sp:View/sp:ViewFields/sp:FieldRef";
XmlNodeList nodes = result.SelectNodes(xpathQuery, nsmgr);
for (int i = 0; i < nodes.Count; i++)
{
Console.WriteLine(nodes[i].Attributes["Name"].Value);
}
Looks like you have to have a XmlNamespaceManager otherwise your query always returns no values. Something about specifying the namespace... Here is a good reference.
The GetList() method returns a CAML fragment that includes the list's field (column) definitions. You might want to try an XPath expression:
XmlNode list = yourListService.GetList("yourListName");
XmlNodeList visibleColumns
= list.SelectNodes("Fields/Field[not(#Hidden) or #Hidden='FALSE']");
I used the above code but, after a long search I found the solution to get all or custom columns from the sharepoint list. The code for that is shared on ..
Custom Columns or ALL Columns
Solution is quite simple. Using GetList() or similar functions is the wrong way to go.
Instead use GetListContentTypesAsync() to get Content ID and then get the specific ContentType using GetListContentTypeAsync(), it returns XML which includes all visible columns in sharepoint list:
var Contents = await soapListClient.GetListContentTypesAsync(list.Title, "0x01"); // 0x01 is kind of a root element for sharepoint.
String ContentID = Contents.Body.GetListContentTypesResult.Descendants().FirstOrDefault().Attribute("ID").Value.ToString();
var ContentType = await soapListClient.GetListContentTypeAsync(list.Title, ContentID);
XElement xmll = XElement.Parse(ContentType.Body.GetListContentTypeResult.ToString());