November, 2009
Linq2Xml Example
This article is a short primer on the proper usage and the power of Linq2Xml. To
demonstrate Linq2Xml usage, I'm going to build a Console application that captures
and then queries against some public information about a few U.S. stocks. This article
is a good xml example and a c# example for Linq to Xml.
I'll start by creating a new Console application (image shows Visual Studio 2008).
I called mine "Linq2Xml_Example," but you can call yours whatever you
choose, of course.
The first thing I want to do is find a data source for our stock data. Create a
new "Service Reference" and point it to http://www.webservicex.net/stockquote.asmx.
I called mine "wsQuote," but you do whatever feels right. This is an Xml
Web Service that I found by Googling. It's not always up and running,
but it's been mostly reliable, and the price is right.
Next, let's test our connection by checking the current price of Apple. It took
a little experimentation to find the right function within the web service to call,
but ultimately, it was pretty straight-forward. Here's a picture what my console
application looks like now:
namespace Linq2Xml_Example
{
class Program
{
static void Main(string[] args)
{
wsQuote.StockQuoteSoapClient quote =
new wsQuote.StockQuoteSoapClient("StockQuoteSoap");
string result = quote.GetQuote("AAPL");
Console.WriteLine(result);
Console.Read();
}
}
}
If all is going according to plan, you will see an xml string. The
Console.Read() function allows us to test using F5 and see the results
instead of watching them flicker away in the background. The Xml code that I get
back from the service, after it's been formatted, looks like this:
<StockQuotes>
<Stock>
<Symbol>AAPL</Symbol>
<Last>201.46</Last>
<Date>11/9/2009</Date>
<Time>4:00pm</Time>
<Change>+7.12</Change>
<Open>196.95</Open>
<High>201.90</High>
<Low>196.26</Low>
<Volume>18887668</Volume>
<MktCap>181.5B</MktCap>
<PreviousClose>194.34</PreviousClose>
<PercentageChange>+3.66%</PercentageChange>
<AnnRange>78.20 - 208.71</AnnRange>
<Earns>6.289</Earns>
<P-E>30.90</P-E>
<Name>Apple Inc.</Name>
</Stock>
</StockQuotes>
While interesting, it's not all that useful by itself. So, let's take a step back,
encapsulate this logic into a class, and expand it to capture a wee bit more data.
internal class StockData
{
private static StockData _stockData = null;
public static StockData GetData()
{
if (_stockData == null) _stockData = new StockData();
return _stockData;
}
private StringBuilder _xml = new StringBuilder();
public string ToXml()
{
return String.Format("<Stocks>{0}</Stocks>", _xml.ToString());
}
private StockData()
{
wsQuote.StockQuoteSoapClient quote =
new wsQuote.StockQuoteSoapClient("StockQuoteSoap");
string[] stocks = new string[] { "AAPL", "MSFT", "X", "C", "FUQI" };
foreach (string stock in stocks)
{
string results = quote.GetQuote(stock);
if (!string.IsNullOrEmpty(results))
{
XElement xSQ = XElement.Parse(results);
if (xSQ != null)
{
var xStockNodes = xSQ.Descendants("Stock");
if (xStockNodes != null)
_xml.Append(xStockNodes.First());
}
}
}
}
}
Okay, so now I'm capturing some data for a handful of arbitrary stocks, but before
I move on, let's take a look at how easily this data was handled. First, there's
the XElement.Parse() function, which is a big improvement
in the .Net world, allowing us to instantly load our Xml string into the XElement
hierarchy. Second, I have the var keyword, which I would
not have used other than to write about it here. var allows
for run-time type casting. The XElement.Descendants()
function returns an IEnumerable<XElement> object
of all of the <Stock> elements. Hopefully, there
is one and only one such element, but I do a little checking to make sure that I
don't get the dreaded Object not set to an instance of an object
error. Here is our new xml:
<Stocks>
<Stock>
<Symbol>AAPL</Symbol>
<Last>201.46</Last>
<Date>11/9/2009</Date>
<Time>4:00pm</Time>
<Change>+7.12</Change>
<Open>196.95</Open>
<High>201.90</High>
<Low>196.26</Low>
<Volume>18887668</Volume>
<MktCap>181.5B</MktCap>
<PreviousClose>194.34</PreviousClose>
<PercentageChange>+3.66%</PercentageChange>
<AnnRange>78.20 - 208.71</AnnRange>
<Earns>6.289</Earns>
<P-E>30.90</P-E>
<Name>Apple Inc.</Name>
</Stock>
<Stock>
<Symbol>MSFT</Symbol>
<Last>28.99</Last>
<Date>11/9/2009</Date>
<Time>4:00pm</Time>
<Change>+0.47</Change>
<Open>28.60</Open>
<High>29.00</High>
<Low>28.53</Low>
<Volume>57522020</Volume>
<MktCap>257.4B</MktCap>
<PreviousClose>28.52</PreviousClose>
<PercentageChange>+1.65%</PercentageChange>
<AnnRange>14.87 - 29.35</AnnRange>
<Earns>1.539</Earns>
<P-E>18.53</P-E>
<Name>Microsoft Corpora</Name>
</Stock>
<Stock>
<Symbol>X</Symbol>
<Last>38.71</Last>
<Date>11/9/2009</Date>
<Time>4:00pm</Time>
<Change>+1.33</Change>
<Open>38.03</Open>
<High>38.97</High>
<Low>38.03</Low>
<Volume>8983488</Volume>
<MktCap>5.549B</MktCap>
<PreviousClose>37.38</PreviousClose>
<PercentageChange>+3.56%</PercentageChange>
<AnnRange>16.66 - 51.65</AnnRange>
<Earns>-6.613</Earns>
<P-E>N/A</P-E>
<Name>UNITED STATES STE</Name>
</Stock>
<Stock>
<Symbol>C</Symbol>
<Last>4.19</Last>
<Date>11/9/2009</Date>
<Time>4:00pm</Time>
<Change>+0.13</Change>
<Open>4.14</Open>
<High>4.19</High>
<Low>4.10</Low>
<Volume>234101664</Volume>
<MktCap>95.800B</MktCap>
<PreviousClose>4.06</PreviousClose>
<PercentageChange>+3.20%</PercentageChange>
<AnnRange>0.97 - 12.28</AnnRange>
<Earns>-2.782</Earns>
<P-E>N/A</P-E>
<Name>CITIGROUP INC</Name>
</Stock>
<Stock>
<Symbol>FUQI</Symbol>
<Last>19.18</Last>
<Date>11/9/2009</Date>
<Time>4:00pm</Time>
<Change>-4.15</Change>
<Open>24.78</Open>
<High>25.74</High>
<Low>18.83</Low>
<Volume>10613366</Volume>
<MktCap>529.9M</MktCap>
<PreviousClose>23.33</PreviousClose>
<PercentageChange>-17.79%</PercentageChange>
<AnnRange>3.31 - 32.68</AnnRange>
<Earns>1.668</Earns>
<P-E>13.99</P-E>
<Name>Fuqi Internationa</Name>
</Stock>
</Stocks>
Okay, so let's do something useful. Let's say that I want to sort the list by price.
I might try:
class Program
{
static void Main(string[] args)
{
XElement xStocks = XElement.Parse(StockData.GetData().ToXml());
IEnumerable<XElement> stocks = (from x in xStocks.Descendants("Stock")
orderby
Convert.ToDecimal(((XElement)x).Descendants("Last").First().Value)
select x);
foreach (XElement stock in stocks)
{
Console.WriteLine(stock.Descendants("Symbol").First().Value.PadLeft(10)
+ ": " + stock.Descendants("Last").First().Value.PadLeft(10));
}
Console.Read();
}
}
Note the orderby keyword and the fact that I can convert
the content of the Last element under the
x variable being selected. I have to use the First()
method because Descendants returns a collection. Even
though there is only one Last element,
First() is required to fetch it. If there was no Last
element, this code would throw an Exception. It's probably a better practice to
make sure the Count() is greater than zero, but I'm trusting
that the Xml comes back from the server consistently. The output looks like this:
C: 4.19
FUQI: 19.18
MSFT: 28.99
X: 38.71
AAPL: 201.46
Let's say that I just want to identify the stocks in the list that are down for
the day. I can use the where keyword, convert the Change element to a decimal and compare it to zero, like
so:
class Program
{
static void Main(string[] args)
{
XElement xStocks = XElement.Parse(StockData.GetData().ToXml());
IEnumerable<XElement> stocks = (from x in xStocks.Descendants("Stock")
where
Convert.ToDecimal(x.Descendants("Change").First().Value) < 0
select x);
foreach (XElement stock in stocks)
{
Console.WriteLine(stock.Descendants("Symbol").First().Value.PadLeft(10)
+ ": " + stock.Descendants("Last").First().Value.PadLeft(10)
+ ": " + stock.Descendants("Change").First().Value.PadLeft(10));
}
Console.Read();
}
}
Our results become:
FUQI: 19.18: -4.15
In this article, I've looked at referencing an Xml Web Service, loading Xml into
the XElement object, the orderby
and where keywords, and manipulating Xml elements during
the execution of the Linq statement.
In summary, Linq2Xml is incredibly powerful when it comes to searching and parsing
large amounts of Xml, especially when data values need to be manipulated, sorted
or compared. I hope this short primer helps you get started on making practical
use of Linq2Xml for yourself.
Your feedback helps me provide content that is meaningful. Please don't
be shy about speaking your mind.
Please provide feedback about this article
Email:
(optional)