|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionWorking with XML using Microsoft’s .NET Framework in version 2.0 and below is a cumbersome task. The API available follows the W3C DOM model and is document-centric. Everything begins with the document; you can’t create elements without having a document, even a fragment of XML, is a document. In the latest release of the .NET Framework, however, this has changed. XML is now element-centric. With features like object initialization and anonymous types, it’s very easy to create XML. Add to this the features of LINQ and we now have a very easy to use and powerful tool for XML. In this article I will explore some of the features available in .NET Framework release 3.5 related to XML and LINQ. This is, of course, not an extensive discussion of either subject, merely a familiarization and stepping stone for more learning and exploration. The LINQ PartWhen discussing LINQ to XML, or LINQ to whatever, the first thing that needs to be discussed is, of course LINQ. LINQLanguage-Integrated query, or LINQ, is an extension to the .NET Framework in version 3.5 that makes queries and set operations first class citizens of .NET languages such as C#. It has been further defined as, “A set of general purpose standard query operators that allow traversal, filter, and projection operations to be expressed in a direct yet declarative way in any .NET language.” Getting startedThis is an example of a very basic LINQ query string[] names = new string[] { "John", "Paul", "George", "Ringo" }; var name = names.Select(s => s); Two things noticeable here is the Var keyword
In the above example, name is a resolved to var name = "Hello, World"; In this example though, name is resolved to a string. This ambiguity is useful when you are unsure of what exactly will be returned from a query, and the fact that it is not necessary to cast the variable to another type before using it, is very convenient. Lambda ExpressionsLambda expressions were first introduced in 1936 by mathematician Alonzo Church as a short hand for expressing algorithms. In .NET 3.5 they are a convenient way for developers to define functions that can be passed as arguments, and are an evolution of anonymous methods introduced in .NET 2.0. The => operator is used to separate input variables, on the left, and the body of the expression on the right. string[] names = new string[] { "John", "Paul", "George", "Ringo" };
var name = names.Select(s => s.StartsWith("P"));
In this example, each string in the names array is represented by the variable s. It’s not necessary to declare a datatype because it is inferred from the type of the collection, names in this case. These two statements would be somewhat analogous var name = names.Select(s => s);
foreach(string s in names) { }
The body of the expression, Func and Action
Func<TSource, TResult> This is used to represent a delegate that returns a value, Action<T> On the other hand, this is used to represent a delegate does not return a value. The example we have been using can be rewritten as below Func<string, bool> pOnly = delegate(string s) { return s.StartsWith("P"); }; string[] names = new string[] { "John", "Paul", "George", "Ringo" }; var name = names.Select(pOnly); SequencesRunning the demo code from this article you will notice the in all of the examples above do not return a single value. Rather the return a collection of Boolean values indicating whether each element in the input collection matched the specified expression. This collection is referred to as a sequence in LINQ.
string name = names.Single(pOnly); Notice here that the Extension methodsExtension methods are a feature of .NET 3.5 that allows developers to add functionality to existing classes without modifying the code for the original class. A useful scenario when you want to provide additional functionality and don’t have access to the code base, such as when using third-party libraries. Extension methods are static methods on static classes. The first parameter of these methods is typed as the datatype for which it is extending, and uses the public static class StringExtensions
{
public static int ToInt(this string number)
{
return Int32.Parse(number);
}
public static string DoubleToDollars(this double number)
{
return string.Format("{0:c}", number);
}
public static string IntToDollars(this int number)
{
return string.Format("{0:c}", number);
}
}
When this class in compiled, .NET applies the
As we can see here, in the first example Intellisence knows that on the Query Expression and MethodsThere are two ways to execute LINQ queries, what is known as query expression and dot-notation. The former resembles a SQL query, except that the select clause is last. string[] camps = new string[]{"CodeCamp2007","CodeCamp2008","CodeCamp2009"};
var currentCamp = from camp in camps
where camp.EndsWith(DateTime.Now.Year.ToString())
select camp;
string currentCamp = camps.Single(c => c.EndsWith(DateTime.Now.Year.ToString()));
These two statements produce the same results because the query expression format is converted to methods at compile time. There are several ways to produce results with methods. Each of the below will produce the same results. string currentCamp2 = camps.Where(c => c.EndsWith(DateTime.Now.Year.ToString())).Single();
string currentCamp3 = camps.Single(c => c.EndsWith(DateTime.Now.Year.ToString()));
string currentCamp4 = camps.Select(c => c).Where(c => c.EndsWith(DateTime.Now.Year.ToString())).Single();
The XML PartNow that we have an understanding of LINQ, it’s time to move on the XML part. For this article we will be using this XML file <?xml version="1.0" encoding="utf-8" ?>
<employees>
<employee id="1" salaried="no">
<name>Gustavo Achong</name>
<hire_date>7/31/1996</hire_date>
</employee>
<employee id="3" salaried="yes">
<name>Kim Abercrombie</name>
<hire_date>12/12/1997</hire_date>
</employee>
<employee id="8" salaried="no">
<name>Carla Adams</name>
<hire_date>2/6/1998</hire_date>
</employee>
<employee id="9" salaried="yes">
<name>Jay Adams</name>
<hire_date>2/6/1998</hire_date>
</employee>
</employees>
The Old WayIn previous versions of the .NET Framework, XML was document-centric, in other words, to create any structure you first had to start with an public class OldWay
{
private static XmlDocument m_doc = new XmlDocument();
public static void CreateEmployees()
{
XmlElement root = m_doc.CreateElement("employees");
root.AppendChild(AddEmployee(1, "Gustavo Achong", DateTime.Parse("7/31/1996"), false));
root.AppendChild(AddEmployee(3, "Kim Abercrombie", DateTime.Parse("12/12/1997"), true));
root.AppendChild(AddEmployee(8, "Carla Adams", DateTime.Parse("2/6/1998"), false));
root.AppendChild(AddEmployee(9, "Jay Adams", DateTime.Parse("2/6/1998"), false));
m_doc.AppendChild(root);
Console.WriteLine(m_doc.OuterXml);
}
private static XmlElement AddEmployee(int ID, string name, DateTime hireDate, bool isSalaried)
{
XmlElement employee = m_doc.CreateElement("employee");
XmlElement nameElement = m_doc.CreateElement("name");
nameElement.InnerText = name;
XmlElement hireDateElement = m_doc.CreateElement("hire_date");
hireDateElement.InnerText = hireDate.ToShortDateString();
employee.SetAttribute("id", ID.ToString());
employee.SetAttribute("salaried", isSalaried.ToString());
employee.AppendChild(nameElement);
employee.AppendChild(hireDateElement);
return employee;
}
}
Smart developers would create helper methods to ease the pain, but it was still a verbose, cumbersome process. An XmlElement employee = m_doc.CreateElement("employee");
Trying to do this generate a compiler error. XmlElement employee = new XmlElement();
Looking at the above example, it is also difficult to get an idea about the scheme for this document. The New WayUsing the classes from the public static void CreateEmployees()
{
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment("A sample xml file"),
new XElement("employees",
new XElement("employee",
new XAttribute("id", 1),
new XAttribute("salaried", "false"),
new XElement("name", "Gustavo Achong"),
new XElement("hire_date", "7/31/1996")),
new XElement("employee",
new XAttribute("id", 3),
new XAttribute("salaried", "true"),
new XElement("name", "Kim Abercrombie"),
new XElement("hire_date", "12/12/1997")),
new XElement("employee",
new XAttribute("id", 8),
new XAttribute("salaried", "false"),
new XElement("name", "Carla Adams"),
new XElement("hire_date", "2/6/1998")),
new XElement("employee",
new XAttribute("id", 9),
new XAttribute("salaried", "false"),
new XElement("name", "Jay Adams"),
new XElement("hire_date", "2/6/1998"))
)
);
}
Constructing a document in this way is possible because of the functional construction feature in LINQ to XML. Functional construction is simply a means of create an entire document tree in a single statement. public XElement(XName name, Object[] content)
As we can see from one of the constructors for In the above example we could have replaced public XDocument(XDeclaration declaration,Object[] content)
Another thing to note when running the demo is how both documents are printed to the console window.
As we can see the old method just streams the contents of the document to the console. The method does that also, however, it is nicely formatted with no extra effort. Namespace supportNamespaces are, of course, supported through the XNamespace ns = "http://mycompany.com/">http://mycompany.com;
XElement doc = new XElement(
new XElement(ns + "employees",
new XElement("employee",
One this to note is that if one element uses a namespace, they all must use one. In the case above we can see that an empty <employees xmlns="http://mycompany.com">
<employee id="1" salaried="false" xmlns="">
Explicit conversionOne of the many nice thing with the new XML support is it support for explicit conversion of values. Previously, all xml values were treated as strings and had to be converted as necessary. // Must be string, or converted to string
//idElement.InnerText = 42;
idElement.InnerText = "42";
int id = Convert.ToInt32(idElement.Value);
With the new API this is much more intuitive XElement element1 = new XElement("number", 42);
// It doesn't matter it the value is a string or int
XElement element2 = new XElement("number", "42");
int num1 = (int)element1;
int num2 = (int)element2;
Traversing an XML treeTraversing an XML tree is still very easy foreach(var node in doc.Nodes())
We can use the nodes in collections of the document, or root element. Note here however that this will traverse the entire tree, including all children, not just the sibling nodes. foreach(var node in doc.Nodes().OfType<XComment>())
This method can be used to traverse specific node node types, comments in this case. Or we can get to specific child nodes this way. foreach(var node in doc.Elements("employees").Elements("employee").Elements("name"))
This is an improvement over nested iterations or obtaining an XPathXPath support has been built into the API through the use of extension methods such as:
This is not an extensive list, so check the documentation for all the others available. Transforming XMLTransforming and XML document or element is still possible using the methods I’m sure we are all familiar with //Load the stylesheet.
XslTransform xslt = new XslTransform();
xslt.Load(stylesheet);
//Load the file to transform.
XPathDocument doc = new XPathDocument(filename);
//Create an XmlTextWriter which outputs to the console.
XmlTextWriter writer = new XmlTextWriter(Console.Out);
//Transform the file and send the output to the console.
xslt.Transform(doc, null, writer, null);
writer.Close();
XElement element = new XElement("salaried_employees", from e in doc.Descendants("employee")
where e.Attribute("salaried").Value == "true"
select new XElement("employee",
new XElement(e.Element("name")) ) );
ConclusionXML is fantastic construct that has been deeply ingrained into just about everything. Having the ability to easily construct, query, transform and manipulate XML documents is an invaluable service that will improve the speed of which applications can be built and the quality of those applications. This article is not an exhaustive investigation of LINQ to XML; there have been many other articles, snippets and blogs written on the subject. It mainly just a taste and familiarization of what is possible using .NET 3.5. Referenceshttp://msdn2.microsoft.com/en-us/library/bb308960.aspx http://www.hookedonlinq.com/LINQOverview.ashx HistoryInitial release: 3/14/08
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||