i have created a custom XDocument in c# and it looks like the following
<Filters datetimegenerated="28.07.2013 23:12PM">
<SimpleStringFilter column="xxx" table="yyy" groupby="True" seperatereport="true">
good,bad,ugly
</SimpleStringFilter>
<NumaricalFilter column="zzz" table = "mmm">zzz = 100 or zzz= 50</NumaricalFilter>
</Filters>
parsing it with in c# doesn't seem to work here is my code when i try to parse the StringFilterTags, however i get zero count from the above sample
var filters = from simplestringfilter in xdoc.Root.Element("Filters").Elements("SimpleStringFilter")
let column = simplestringfilter.Attribute("column")
let table = simplestringfilter.Attribute("table")
let groupby = simplestringfilter.Attribute("groupby")
let seperatecolumnby = simplestringfilter.Attribute("seperatereport")
let filterstringval = simplestringfilter.Value
select new
{
Column = column,
Table = table,
GroupBy = groupby,
SeperateColumnBy = seperatecolumnby,
Filterstring = filterstringval
};
what am i doing wrong?
Your query is searching off of the root element checking to see if it has a child Filters element. Since the root is the Filters element, that obviously fails which is why you are not getting any results.
There are two ways to resolve this problem. Just don't search for the Filters off of the root and your query should be fine.
var filters =
from simplestringfilter in xdoc.Root.Elements("SimpleStringFilter")
...
A better way to write it IMHO would be to not query off of the root but the document itself. It will look more natural.
var filters =
from simplestringfilter in xdoc.Element("Filters")
.Elements("SimpleStringFilter")
...
I am a newbie to Linq and having difficulties to solve an easy proble..as I 've never done before.
The scenario is a single XML table with books..like :
<?xml version="1.0" encoding="utf-8"?>
<dbproject>
<books_dataset>
<book>
<id>23</id>
<isbn>075221912X</isbn>
<title>Big Brother: The Unseen Story</title>
<author>Jean Ritchie</author>
<publicationYr>2000</publicationYr>
<publisher>Pan Macmillan</publisher>
<pages>169</pages>
<imageBigLink>/images/P/075221912X.01.LZZZZZZZ.jpg</imageBigLink>
<priceActual>0</priceActual>
<numberOfBids>0</numberOfBids>
<sf>kw</sf>
<df></df>
<ef></ef>
<description>Lorem ipsum dolor sit amet</description>
</book>
</books_dataset>
</dbproject>
I am trying to create a query which gives me the ID (next one / first one) of the next/previous book which has a "kw" string in the node.
The IDs are not continuous and there is no index. So for instance a next button is looking for an ID as follows:
Next (higher) ID = Next Book
Which has a "kw" string in
I 've tried many solutions but just got confused :/.
I am able to jump to the next/previous node.. but to be honest I am sure it isn't the best approach to achieve the task.
I am able to list the books which has a kw string but this two requirements do not work together :/
I use this query to ask for a next ID :
var btnNextEval = (from databack in xmlData.Element("dbproject").Elements(QRY).Elements(QRY_sub)
where databack.Element(fid1).Value == trgtCounter.ToString()
select databack).Single().ElementsAfterSelf().First().Element("id").Value;
trgtCounter = Convert.ToInt16(btnNextEval);
I tried to use && to create multiple where but didn't work :/
Please help and show me possible solutions for this silly problem.
Thanks!
Try this:
var nextId = (
from book in xmlData.Elements("book")
let id = (int)book.Element("id")
where ((string)book.Element("sf")) == "kw"
&& id > currentId
select (int)book.Element("id")
).DefaultIfEmpty(-1).Min();
This returns the next ID. To get the book with next ID, do the following:
var nextBook = (
from book in xmlData.Elements("book")
where (int)book.Element("id") == nextId
select book
).First();
Notes:
This assumes there is a variable currentId of type int containing the current id.
You need the DefaultIfEmpty in case there are no ids greater than the current one. In that case, Min will return an error. Using DefaultIfEmpty(-1) will return a single set with -1.
First will also return an error if used on an empty sequence.
I've read MANY different solutions for the separate functions of LINQ that, when put together would solve my issue. My problem is that I'm still trying to wrap my head about how to put LINQ statements together correctly. I can't seem to get the syntax right, or it comes up mish-mash of info and not quite what I want.
I apologize ahead of time if half of this seems like a duplicate. My question is more specific than just reading the file. I'd like it all to be in the same query.
To the point though..
I am reading in a text file with semi-colon separated columns of data.
An example would be:
US;Fort Worth;TX;Tarrant;76101
US;Fort Worth;TX;Tarrant;76103
US;Fort Worth;TX;Tarrant;76105
US;Burleson;TX;Tarrant;76097
US;Newark;TX;Tarrant;76071
US;Fort Worth;TX;Tarrant;76103
US;Fort Worth;TX;Tarrant;76105
Here is what I have so far:
var items = (from c in (from line in File.ReadAllLines(myFile)
let columns = line.Split(';')
where columns[0] == "US"
select new
{
City = columns[1].Trim(),
State = columns[2].Trim(),
County = columns[3].Trim(),
ZipCode = columns[4].Trim()
})
select c);
That works fine for reading the file. But my issue after that is I don't want the raw data. I want a summary.
Specifically I need the count of the number of occurrences of the City,State combination, and the count of how many times the ZIP code appears.
I'm eventually going to make a tree view out of it.
My goal is to have it laid out somewhat like this:
- Fort Worth,TX (5)
- 76101 (1)
- 76103 (2)
- 76105 (2)
- Burleson,TX (1)
- 76097 (1)
- Newark,TX (1)
- 76071 (1)
I can do the tree thing late because there is other processing to do.
So my question is: How do I combine the counting of the specific values in the query itself? I know of the GroupBy functions and I've seen Aggregates, but I can't get them to work correctly. How do I go about wrapping all of these functions into one query?
EDIT: I think I asked my question the wrong way. I don't mean that I HAVE to do it all in one query... I'm asking IS THERE a clear, concise, and efficient way to do this with LINQ in one query? If not I'll just go back to looping through.
If I can be pointed in the right direction it would be a huge help.
If someone has an easier idea in mind to do all this, please let me know.
I just wanted to avoid iterating through a huge array of values and using Regex.Split on every line.
Let me know if I need to clarify.
Thanks!
*EDIT 6/15***
I figured it out. Thanks to those who answered it helped out, but was not quite what I needed. As a side note I ended up changing it all up anyways. LINQ was actually slower than doing it other ways that I won't go into as it's not relevent. As to those who made multiple comments on "It's silly to have it in one query", that's the decision of the designer. All "Best Practices" don't work in all places. They are guidelines. Believe me, I do want to keep my code clear and understandable but I also had a very specific reasoning for doing it the way I did.
I do appreciate the help and direction.
Below is the prototype that I used but later abandoned.
/* Inner LINQ query Reads the Text File and gets all the Locations.
* The outer query summarizes this by getting the sum of the Zips
* and orders by City/State then ZIP */
var items = from Location in(
//Inner Query Start
(from line in File.ReadAllLines(FilePath)
let columns = line.Split(';')
where columns[0] == "US" & !string.IsNullOrEmpty(columns[4])
select new
{
City = (FM.DecodeSLIC(columns[1].Trim()) + " " + columns[2].Trim()),
County = columns[3].Trim(),
ZipCode = columns[4].Trim()
}
))
//Inner Query End
orderby Location.City, Location.ZipCode
group Location by new { Location.City, Location.ZipCode , Location.County} into grp
select new
{
City = grp.Key.City,
County = grp.Key.County,
ZipCode = grp.Key.ZipCode,
ZipCount = grp.Count()
};
The downside of using File.ReadAllLines is that you have to pull the entire file into memory before operating over it. Also, using Columns[] is a bit clunky. You might want to consider my article describing using DynamicObject and streaming the file as an alternative implemetnation. The grouping/counting operation is secondary to that discussion.
var items = (from c in
(from line in File.ReadAllLines(myFile)
let columns = line.Split(';')
where columns[0] == "US"
select new
{
City = columns[1].Trim(),
State = columns[2].Trim(),
County = columns[3].Trim(),
ZipCode = columns[4].Trim()
})
select c);
foreach (var i in items.GroupBy(an => an.City + "," + an.State))
{
Console.WriteLine("{0} ({1})",i.Key, i.Count());
foreach (var j in i.GroupBy(an => an.ZipCode))
{
Console.WriteLine(" - {0} ({1})", j.Key, j.Count());
}
}
There is no point getting everything into one query. It's better to split the queries so that it would be meaningful. Try this to your results
var grouped = items.GroupBy(a => new { a.City, a.State, a.ZipCode }).Select(a => new { City = a.Key.City, State = a.Key.State, ZipCode = a.Key.ZipCode, ZipCount = a.Count()}).ToList();
Result screen shot
EDIT
Here is the one big long query which gives the same output
var itemsGrouped = File.ReadAllLines(myFile).Select(a => a.Split(';')).Where(a => a[0] == "US").Select(a => new { City = a[1].Trim(), State = a[2].Trim(), County = a[3].Trim(), ZipCode = a[4].Trim() }).GroupBy(a => new { a.City, a.State, a.ZipCode }).Select(a => new { City = a.Key.City, State = a.Key.State, ZipCode = a.Key.ZipCode, ZipCount = a.Count() }).ToList();
Scenario:
Grid view gets populated in WPF window.
Having a static list in code behind.(which i want to get from a xml file).
Trying to move the static list into an xml file.For that i created a ml file in the following format
<customers>
<customer Name="abc"/>
<customer Name="def"/>
</customers>
CodeBehind:
Xdocument doc=Xdocument.load("customers.xml");
var customerList = (from e in doc.Descendants("Cusomters")
select new
{
CustomerName = e.Attribute("Name").Value
}).ToList();
I am unable to get the customer names from the xml file to the customerList.I would appreciate if someone can help me to move forward.
"Cusomters" is spelled incorrectly, should be "Customers".
Obviously this is not the code your are using since it doesn't even compile. It should be this:
XDocument doc = XDocument.Load( "customers.xml" );
var customerList = (from e in doc.Descendants( "customer" )
select new
{
CustomerName = e.Attribute( "Name" ).Value
}).ToList();
You really should mention the fact that it won't compile. That, or you copied it in by hand incorrectly, which doesn't help us help you either.
The logical problem here is that you are asking for all Customers tags, note the s at the end. You really want to look for Customer tags, which have a name attribute. Customer*s* is simply the top level group.
Use customer instead of Cusomters (XML is case-sensitive):
from e in doc.Descendants("customer")
You most likely want a List<string> so you don't need to project to an anonymous class - also there is a typo in your query ("Cusomters"):
var customerList = (from e in doc.Descendants("Customer")
select e.Attribute("Name").Value).ToList();
or with extension method syntax:
var customerList = doc.Descendants("Customer")
.Select( e => e.Attribute("Name").Value)
.ToList();
Ive always used :
doc.root.elements("Customer")
for small snippets like this.
I've got an XML file where a part of it are the GoodsItems element. I want to order the GoodsItem elements so that the ones having a subelement SupplementaryInformationLines.SupplementaryInformationLine with Code == "X002" and Text != "NLR" comes first. Subsequently all elements must be ordered by the GoodsItem.TaricCode element.
<GoodsItems>
<GoodsItem>
<GoodsDescription1>Some goods to be sorted last</GoodsDescription1>
<TaricNumber>854129</TaricNumber>
<SupplementaryInformationLines>
<SupplementaryInformationLine>
<Type>B.H</Type>
<Code>X002</Code>
<Text>NLR</Text>
</SupplementaryInformationLine>
<SupplementaryInformationLine>
<Type>SU</Type>
<Code></Code>
<Text>Some text</Text>
</SupplementaryInformationLine>
</SupplementaryInformationLines>
</GoodsItem>
<GoodsItem>
<GoodsDescription1>Some goods to be sorted first</GoodsDescription1>
<TaricNumber>854129</TaricNumber>
<SupplementaryInformationLines>
<SupplementaryInformationLine>
<Type>B.H</Type>
<Code>X002</Code>
<Text>SE_A_4324234</Text>
</SupplementaryInformationLine>
<SupplementaryInformationLine>
<Type>SU</Type>
<Code></Code>
<Text>Some text</Text>
</SupplementaryInformationLine>
</SupplementaryInformationLines>
</GoodsItem>
</GoodsItems>
I tested it and got it to work correctly with the first part of the ordering, then I added the TaricNumber ordering and changed from using .Value to get the string values of the elements in the where clause to casting to string instead since some files got a NullPointerException when using .Value. After these changes I cannot get it to work again. It only orders the GoodsItems by TaricNumber.
var query = from xeGoodsItem in xeCustClearance.Element(nsName + "GoodsItems").Elements(nsName + "GoodsItem")
let sortValue1 = (
from xeSuppInfo in xeGoodsItem.Element(nsName + "SupplementaryInformationLines").Elements(nsName + "SupplementaryInformationLine")
where ((string)xeSuppInfo.Element("Code") == "X002" && (string)xeSuppInfo.Element("Text") != "NLR")
select 1).FirstOrDefault()
orderby sortValue1 descending, (string)xeGoodsItem.Element(nsName + "TaricNumber").Value ascending
select xeGoodsItem;
I don't need to save the XML file with the ordering, I'm only doing the sort in-memory. Although using a ReplaceNode approach instead of a linq query could be a solution as well, I just need to get this thing to work.
Answered in my comment. It was a missing namespace name in front of the element name causing the ordering to not work as expected.