Ugrás a tartalomhoz

.NET Programming Technologies

Gergely Kovásznai, Csaba Biró

Eszterházy Károly College

16. fejezet - LINQ to XML (written by Gergely Kovásznai)

16. fejezet - LINQ to XML (written by Gergely Kovásznai)

In small-scale software, it is common to store source data in files. The same holds for applications that have only limited access to network or external databases (e.g., in Silverlight applications).

Since, in WPF applications, one usually deals with textual or numerical data, it is worth to use text files to store data in. This is nice, but in what format should one store such objects? Do not forget that one must store each property (with its name and value) of individual objects, and, furthermore, some of those properties might be compound ones, i.e., their values might be objects, therefore, again, one must store each property of those objects as well, among which there might be compound ones again, and so on. Long story short, our data should be stored in a hierarchical format.

Before starting to invent some own file format, it is worth to check out already existing solutions. It is worth to look around and see what kinds of standards developers can access. For our goals, the XML format is a perfect choice. Furthermore, we as developers can profit from the various tools that support the parsing and handling of XML data. So does the .NET framework. Above .NET 3.5, one can even execute LINQ queries on XML data, too.

Although any text editor can create XML files, it is worth, in our case, to exploit Visual Studio’s (Section XVIII.1) support for XML. One can add XML files as new items to a project (c.f. Section XVIII.1.1), as can be seen in Figure 1.

XVI.1. Creating an XML file in Visual Studio

It is worth to copy the XML file that we have just created next to (the exe file of) our application; this we can either do manually or make Visual Studio do it (c.f. Figure 2): among the properties of the XML file (in the Properties window detailed in Section XVIII.1.4), set Build Action to Content, and Copy to Output Directory to Copy if newer. After doing so, whenever we make changes to the XML file, Visual Studio will update the one in the directory of our application, too.

XVI.2. Setting the properties of an XML file in Visual Studio

Let us start to edit the content of our XML file! We have total freedom in choosing the content; it must only meet the syntactical rules of the XML standard.  In the next example, we would like to store students’ data in an XML file. Let us use a student XML element to represent each student! Let this element have firstName, lastName and sex attributes, which represent the first name, the last name, and the gender of a student, respectively. Let us assume that even the date of birth of each student has to be stored; since date is compound data, we represent it as, for the sake of example, a separate dateOfBirth XML element, which has year, month, and day attributes. It is important that an XML file must contain exactly one root element; we will use the students XML element for this purpose.

<?xml version="1.0" encoding="utf-8" ?>

<students>

         <student firstName="Rita" lastName="Poratzki" sex="female">

                <dateOfBirth year="1992" month="07" day="12"/>

        </student>

        <student firstName="Laura" lastName="Ashley" sex="female">

                <dateOfBirth year="1993" month="01" day="09"/>

        </student>

        <student firstName="Steven" lastName="Drabant" sex="male">

                <dateOfBirth year="1991" month="02" day="27"/>

        </student>

        ...

<students>

Loading and parsing XML files is supported by several .NET tools. In the followings, we will introduce the toolkit of LINQ to XML, which supports, besides loading and parsing, LINQ queries on XML data. Related classes can be found in the System.Xml.Linq namespace.

Loading XML files

When dealing with XML data, the XElement class plays a key role. First, let us introduce two of its static methods, which are for parsing text in XML format! The XElement.Parse method expects a string as parameter, in which one can pass arbitrary text in XML format and make the framework parse it. It is worth to use the XElement.Load method if the text being parsed is located in a file; this method expects a filename as parameter.[12] Both Parse and Load return an instance of the XElement class.

XElement

  

Name

Name of the XML element. E.g., string „dateOfBirth” in the case of <dateOfBirth ...>.

Value

If the XML element has a start-tag and an end-tag, and there is some text between them, then this text is returned in this property. E.g., string „Manchester” in the case of <city>Manchester</city>.

Attribute(...)Attributes([...])

XName

Returns the element’s attribute(s) that has/have the given name.

Element(...)Elements([...])

XName

Return the direct descendant element(s) that has/have the given name.

Descendants([...])

XName

Returns all the (direct or indirect) descendant elements.

Parse(...)

string

Parses (the root element of) the XML data passed in the string.

Load(...)

string

Load a file and parses its root element.

The methods that expect a parameter of type XName accept a simple string as well.[13]

All the above methods return XElement objects, except for the Attribute and Attributes methods, which return XAttribute objects. An XML attribute is basically a „name=value” pair, therefore it is sufficient for us to get to know the Name and Value properties of the XAttribute class.

XAttribute

  

Name

Name of the XML attribute. E.g., string “year” in the case of year=”1993”.

Value

Value of the XML attribute. E.g., string “1993” in the case of year=”1993”.

In the first line of the next example, we can see how simple it is to load and parse an XML file, in a single line. In order to understand properly that the parsing result is a hierarchy of objects stored in the xStudents variable, let us check the subsequent two commands! The first command accesses the first student XML element, and then reads the value of its firstName attribute (supposed to be equal to „Rita”, based on the previous example). The second command accesses the third student element (i.e., the one at index 2), and then its dateOfBirth element, whose year attribute is accessed, whose value is returned finally; supposed to be equal to „1991”.

XElement xStudents = XElement.Load("students.xml");

string firstName = xStudents.Element("student").Attribute("firstName").Value;

string year = xStudents.Elements("student").ElementAt(2)

                                                .Element("dateOfBirth").Attribute("year").Value;

As this example shows, even numerical data (e.g. 1991) are treated as text; therefore, both the XElement.Value and XAttribute.Value properties are of type string. Developers have to take care of converting texts to numbers; e.g., the last command in the above example could be rewritten as follows: int year = int.Parse(xStudents...Value);



[12] The Load method offers several signatures, which can even read from streams.

[13] The reason for that is an implicit cast operator of the XName class for casting to string.