Professional ASP.NET 3.5 in C# and Visual Basic Part 56 potx

10 337 0
Professional ASP.NET 3.5 in C# and Visual Basic Part 56 potx

Đang tải... (xem toàn văn)

Thông tin tài liệu

Evjen c10.tex V2 - 01/28/2008 2:13pm Page 507 Chapter 10: Working with XML and LINQ to XML Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load Dim bookcount As Integer = 0 Dim settings As New XmlReaderSettings() settings.IgnoreWhitespace = True settings.IgnoreComments = True Dim booksFile As String = Server.MapPath("books.xml") Using reader As XmlReader = XmlReader.Create(booksFile, settings) While (reader.Read()) If (reader.NodeType = XmlNodeType.Element _ And "book" = reader.LocalName) Then bookcount += 1 End If End While End Using Response.Write(String.Format("Found {0} books!", bookcount)) End Sub End Class C# using System; using System.IO; using System.Xml; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { int bookcount = 0; XmlReaderSettings settings = new XmlReaderSettings(); settings.IgnoreWhitespace = true; settings.IgnoreComments = true; string booksFile = Server.MapPath("books.xml"); using (XmlReader reader = XmlReader.Create(booksFile, settings)) { while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element && "book" == reader.LocalName) { bookcount++; } } } Response.Write(String.Format("Found {0} books!", bookcount)); } } Notice the use of the XmlReader.Create method in Listing 10-5. You may be used to creating concrete implementations of an XmlReader , but if you try this technique, you should find it much more flexible 507 Evjen c10.tex V2 - 01/28/2008 2:13pm Page 508 Chapter 10: Working with XML and LINQ to XML because you can reuse the XmlReaderSettings objects in the creation of other instances of XmlReader . XmlReader implements IDisposable ,sothe Using keyword is correct in both VB and C#. In Listing 9-5 the Books.xml file is in the same directory as this ASPX page, so a call to Server.MapPath gets the complete path to the XML file. The filename with full path is then passed into XmlReader.Create , along with the XmlReaderSettings instance from a few lines earlier. The Read method continues to return true if the node was read successfully. It will return false when no more nodes are left to read. From the point of view of an XmlReader , everything is a node including white space, comments, attributes, elements, and end elements. If Listing 10-5 had simply spun through the while loop incrementing the bookcount variable each time reader.LocalName equaled book ,the final value for bookcount would have been six. You would have counted both the beginning book tag and the ending book tag. Consequently, you have to be more explicit, and ensure that the if statement is modified to check not only the LocalName but also the NodeType . The Reader.LocalName property contains the non–namespace qualified name of that node. The Reader.Name property is different and contains the fully qualified name of that node including namespace. The Reader.LocalName property is used in the example in Listing 10-5 for simplicity and ease. You’ll hear more about namespaces a little later in the chapter. Using XDocument Rather Than XmlReader The System.Xml.Linq namespace introduces a new XDocument class that presents a much friendlier face than XmlDocument while still allowing for interoperability with XmlReaders and XmlWriters . Listing10-5q accomplishes the same thing as Listing 10-5 but uses XDocument instead. The XDocument is loaded just like an XmlDocument but the syntax for retrieving the elements we want is significantly different. The syntax for this query is very clean, but slightly reversed from what you may be used to if you’ve used T-SQL. Rather than select from , we’re using the standard LINQ from select syntax. We ask the booksXML XDocument for all its book descendants, and they are selected into the book range variable. The value of all the book title elements is then selected into the books variable. VB takes the opportunity in Visual Studio 2008 to distinguish itself considerably from C# by including a number of bits of ‘‘syntactic sugar,’’ which makes the experience of working with Visual Basic and XML more integrated. Notice the use of the Imports keyword to declare an XML namespace, as well as the use of ‘‘ <>’’ to indicate the method call to Descendants and ‘‘.<>’’ to call Elements . This extraordinary level of XML integration with the compiler really makes working with XML in VB a joy — and this is a C# lover speaking. Listing 10-5q: P rocessing XML with a XDocument VB Imports System.IO Imports System.Xml Imports System.Linq Imports System.Xml.Linq 508 Evjen c10.tex V2 - 01/28/2008 2:13pm Page 509 Chapter 10: Working with XML and LINQ to XML Imports <xmlns:b="http://example.books.com"> Partial Class _Default Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load Dim booksXML = XDocument.Load(Server.MapPath("books.xml")) Dim books = From book In booksXML <b:book> Select book.<b:title>.Value Response.Write(String.Format("Found {0} books!", books.Count())) End Sub End Class C# using System; using System.IO; using System.Linq; using System.Xml.Linq; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { XDocument booksXML = XDocument.Load(Server.MapPath("books.xml")); var books = from book in booksXML.Descendants("{http://example.books.com}book") select book.Element("{http://example.books.com}title").Value; Response.Write(String.Format("Found {0} books!", books.Count())); } } In both the C# and VB examples, we take advantage of the implicit typing by not indicating the return type in the call to XDocument.Decendants .InVBweuse Dim books and in C# we use var books .Inthis example, because we are using the from select syntax to select our books from the booksXml object, thetypeofthevariable books is System.Linq.Enumerable.SelectIterator , which is ultimately IEnumerable .The count method is added by LINQ as an extension method, allowing us to retrieve the number of books. Notice also that the Books.xml document has a namespace of http://examples.books.com ,soelements with this namespace are included in the query using the LINQ for XML format of namespaceelement .In later examples we’ll use the XNamespace object to make the C# syntax slightly cleaner. Using Schema with XmlTextReader The code in Listing 10-5 reads any XML document regardless of its schema, and if the document contains an element named book ,thecodecountsit.Ifthiscodeismeanttocountbooksofaparticu- lar schema type only, specifically the books from the Books.xml file, it should be validated against the Books.xsd schema. Now modify the creation of the XmlReader class from Listing 10-5 to validate the XmlDocument against the XML Schema used earlier in the chapter. Note that the XmlValidatingReader class is now considered obsolete because all reader creation is done using the Create method of the XmlReader class. 509 Evjen c10.tex V2 - 01/28/2008 2:13pm Page 510 Chapter 10: Working with XML and LINQ to XML Listing 10-6 shows a concrete example of how easy it is to add schema validation to code using XmlRead- erSettings and the XmlReader Create method. Listing 10-6: Validating XML with an XmlReader against an XML Schema VB Imports System.Xml.Schema Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load Dim bookcount As Integer = 0 Dim settings As New XmlReaderSettings() Dim booksSchemaFile As String = Server.MapPath("books.xsd") settings.Schemas.Add(Nothing, XmlReader.Create(booksSchemaFile)) settings.ValidationType = ValidationType.Schema settings.ValidationFlags = _ XmlSchemaValidationFlags.ReportValidationWarnings AddHandler settings.ValidationEventHandler, _ AddressOf settings_ValidationEventHandler settings.IgnoreWhitespace = True settings.IgnoreComments = True Dim booksFile As String = Server.MapPath("books.xml") Using reader As XmlReader = XmlReader.Create(booksFile, settings) While (reader.Read()) If (reader.NodeType = XmlNodeType.Element _ And "book" = reader.LocalName) Then bookcount += 1 End If End While End Using Response.Write(String.Format("Found {0} books!", bookcount)) End Sub Sub settings_ValidationEventHandler(ByVal sender As Object, _ ByVal e As System.Xml.Schema.ValidationEventArgs) Response.Write(e.Message) End Sub C# using System.Xml.Schema; protected void Page_Load(object sender, EventArgs e) { int bookcount = 0; XmlReaderSettings settings = new XmlReaderSettings(); string booksSchemaFile = Server.MapPath("books.xsd"); settings.Schemas.Add(null, XmlReader.Create(booksSchemaFile)); 510 Evjen c10.tex V2 - 01/28/2008 2:13pm Page 511 Chapter 10: Working with XML and LINQ to XML settings.ValidationType = ValidationType.Schema; settings.ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings; settings.ValidationEventHandler += new ValidationEventHandler(settings_ValidationEventHandler); settings.IgnoreWhitespace = true; settings.IgnoreComments = true; string booksFile = Server.MapPath( "books.xml"); using (XmlReader reader = XmlReader.Create(booksFile, settings)) { while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element && "book" == reader.LocalName) { bookcount++; } } } Response.Write(String.Format("Found {0} books!", bookcount)); } void settings_ValidationEventHandler(object sender, System.Xml.Schema.ValidationEventArgs e) { Response.Write(e.Message); } When validating XML, the validator uses the schemaLocation hint found in the XML instance document. If an XML instance document does not contain enough information to find an XML Schema, the instance document expects an XmlSchemaSet object on the XmlReaderSettings object. In the interest of being explicit, Listing 10-6 shows this technique. The XmlReaderSettings object has a Schemas collection avail- able as a property and many overloads for the Add method. This listing passes null into the Add method as the first parameter, indicating that the targetNamespace is specified in the schema. Optionally, XML documents can also contain their schemas inline. The validator needs a way to let you know when validation problems occur. The XmlReaderSettings object has a validation event handler that notifies you as validation events occur. Listing 10-6 also includes a handler for the validation event that writes the message to the browser. Validating Against a Schema Using an XDocument Much of System.Xml.Linq is ‘‘bridged’’ to System.Xml by using extension methods. For example, the XDocument class has an extension Validate method that takes a standard System.Xml.Schema .XmlSchemaSet as a parameter, allowing us to validate an XDocument against an XML Schema. In Listing 10-6q, the XmlSchemaSet is loaded in the standard way, and then passed into the XDocument ’s validate method. 511 Evjen c10.tex V2 - 01/28/2008 2:13pm Page 512 Chapter 10: Working with XML and LINQ to XML Listing 10-6q: Validating XML with a LINQ XDocument against an XML Schema VB Imports System Imports System.Xml Imports System.Linq Imports System.Xml.Linq Imports System.Xml.Schema Imports <xmlns:b="http://example.books.com"> Partial Class _Default Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load Dim schemas = New XmlSchemaSet() schemas.Add(Nothing, XmlReader.Create(Server.MapPath("books.xsd"))) Dim booksXML = XDocument.Load(Server.MapPath("books.xml")) booksXML.Validate(schemas, AddressOf ValidationEventHandler, True) Dim books = From book In booksXML <b:book> _ Select book.<b:title>.Value Response.Write(String.Format("Found {0} books!", books.Count())) End Sub Sub ValidationEventHandler(ByVal sender As Object, _ ByVal e As System.Xml.Schema.ValidationEventArgs) Response.Write(e.Message) End Sub End Class C# using System; using System.Xml; using System.Xml.Linq; using System.Linq; using System.Xml.Schema; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string booksSchemaFile = Server.MapPath("books.xsd"); string booksFile = Server.MapPath("books.xml"); XmlSchemaSet schemas = new XmlSchemaSet(); schemas.Add(null, XmlReader.Create(booksSchemaFile)); XDocument booksXML = XDocument.Load(booksFile); booksXML.Validate(schemas, (senderParam, eParam) => { Response.Write(eParam.Message); }, true); 512 Evjen c10.tex V2 - 01/28/2008 2:13pm Page 513 Chapter 10: Working with XML and LINQ to XML XNamespace ns = "http://example.books.com"; var books = from book in booksXML.Descendants(ns + "book") select book.Element(ns + "title").Value; Response.Write(String.Format("Found {0} books!", books.Count())); } } Notice the unique syntax for an anonymous event handler in the C# example in Listing 10-6q. Rather than creating a separate method and passing it into the call to Validate , C# 3.0 programmers can pass the method body anonymously in as a parameter to the Validate method. The (param1, param2) = > { method } syntax can be a bit jarring initially, but it makes for much tidier code. Including NameTable Optimization XmlReader internally uses a NameTable that lists all the known elements and attributes with namespaces that are used in that document. This process is called atomization-literally meaning that the XML document is broken up into its atomic parts. There’s no need to store the string book more than once in the internal structure if you can make book an object reference that is held in a table with the names of other elements. Although this is an internal implementation detail, it is a supported and valid way that you can measur- ably speed up your use of XML classes, such as XmlReader and XmlDocument . You add name elements to the NameTable that you know will be in the document. Listings 9-5 and 9-6 use string comparisons to compare a string literal with reader.LocalName . These comparisons can also be optimized by turn- ing them into object reference comparisons that are many, many times faster. Additionally, an XML NameTable can be shared across multiple instances of System.Xml classes and even between XmlReaders and XmlDocuments . This topic is covered shortly. Because you are counting book elements, create a NameTable including this element ( book ), and instead of comparing string against string, compare object reference against object reference, as shown in Listing 10-7. Listing 10-7: Optimizing XmlReader with a NameTable VB Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load Dim bookcount As Integer = 0 Dim settings As New XmlReaderSettings() Dim nt As New NameTable() Dim book As Object = nt.Add("book") settings.NameTable = nt Dim booksSchemaFile As String = _ Path.Combine(Request.PhysicalApplicationPath, "books.xsd") settings.Schemas.Add(Nothing, XmlReader.Create(booksSchemaFile)) settings.ValidationType = ValidationType.Schema settings.ValidationFlags = _ XmlSchemaValidationFlags.ReportValidationWarnings AddHandler settings.ValidationEventHandler, _ AddressOf settings_ValidationEventHandler 513 Evjen c10.tex V2 - 01/28/2008 2:13pm Page 514 Chapter 10: Working with XML and LINQ to XML settings.IgnoreWhitespace = True settings.IgnoreComments = True Dim booksFile As String = _ Path.Combine(Request.PhysicalApplicationPath, "books.xml") Using reader As XmlReader = XmlReader.Create(booksFile, settings) While (reader.Read()) If (reader.NodeType = XmlNodeType.Element _ And book.Equals(reader.LocalName)) Then ’A subtle, but significant change! bookcount += 1 End If End While End Using Response.Write(String.Format("Found {0} books!", bookcount)) End Sub C# protected void Page_Load(object sender, EventArgs e) { int bookcount = 0; XmlReaderSettings settings = new XmlReaderSettings(); NameTable nt = new NameTable(); object book = nt.Add("book"); settings.NameTable = nt; string booksSchemaFile = Path.Combine(Request.PhysicalApplicationPath, "books.xsd"); settings.Schemas.Add(null, XmlReader.Create(booksSchemaFile)); settings.ValidationType = ValidationType.Schema; settings.ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings; settings.ValidationEventHandler += new ValidationEventHandler(settings_ValidationEventHandler); settings.IgnoreWhitespace = true; settings.IgnoreComments = true; string booksFile = Path.Combine(Request.PhysicalApplicationPath, "books.xml"); using (XmlReader reader = XmlReader.Create(booksFile, settings)) { while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element && book.Equals(reader.LocalName)) //A subtle, but significant change! { bookcount++; } } } Response.Write(String.Format("Found {0} books!", bookcount)); } 514 Evjen c10.tex V2 - 01/28/2008 2:13pm Page 515 Chapter 10: Working with XML and LINQ to XML The NameTable is added to the XmlSettings object and the Add method of the NameTable returns an object reference to the just-added atom that is stored, in this case, in an object reference named book . The book reference is then used later to make a comparison to the r eader.LocalName property. We specifically chose to use the Equals method that is present on all objects within that .NET Framework in order to emphasize that this is specifically an object identity check for equality. These two objects are either the same identical atoms or they are not. The book object that is returned from the Add method on the NameTable is the identical object that the reader uses when parsing the book element from the Books.xml XML document. In the example of Listing 10-7, in which you count a very small number of books, you probably won’t have a measurable performance gain. However, for larger XML documents that approach sizes of 1 MB, you may see performance gains of as much as 10 to 15 percent — especially for the involved calculations and manipulations of XmlReader . Additionally, because the NameTable is cached within the XmlRead- erSettings object, that NameTable is reused when the XmlReaderSettings object is reused for other System.Xml objects. This creates additional potential performance gains. Retrieving .NET CLR Types from XML It is considerably simpler to retrieve CLR types from an XmlReader than it was previously in the 1.x Framework. If you’ve used SQL Server data reader objects before, retrieving data types from XmlReader should feel very familiar. Previously the Framework used a helper class called XmlConvert .Whencom- bined with the ReadElementString method on XmlReader , this helper class retrieved a strong, simple type, as shown in the following code: //Retrieving a double from an XmlReader in the .NET Framework 1.1 Double price = XmlConvert.ToDouble(reader.ReadElementString()); //Has been replaced by and improved in the .NET Framework 2.0 Double price = reader.ReadElementContentAsDouble(); You can see the removal of the unnecessary double method call results in much cleaner and easier-to-read code. Listing 10-8 adds not only the counting of books but also prints the total price of all books using ReadElementContentAs when your XmlReader is currently on an element, or ReadContentAs if on text content. If schema information is available to the reader, ReadElementContentAsObject returns the value directly as, in this case, a decimal. If the reader does not have any schema information, it attempts to convert the string to a decimal. A whole series of ReadElementContentAs and ReadContentAs methods, including ReadElementContentAsBoolean and ReadElementContentAsInt , are available. Note that the code specific to XmlSchema has been removed from Listing 10-8 in the interest of brevity. Listing 10-8: Using XmlReader.ReadElementContentAs VB Dim bookcount As Integer = 0 Dim booktotal As Decimal = 0 Dim settings As New XmlReaderSettings() Dim nt As New NameTable() Dim book As Object = nt.Add("book") Dim price As Object = nt.Add("price") settings.NameTable = nt Dim booksFile As String = _ 515 Evjen c10.tex V2 - 01/28/2008 2:13pm Page 516 Chapter 10: Working with XML and LINQ to XML Path.Combine(Request.PhysicalApplicationPath, "books.xml") Using reader As XmlReader = XmlReader.Create(booksFile, settings) While (reader.Read()) If (reader.NodeType = XmlNodeType.Element _ And book.Equals(reader.LocalName)) Then bookcount += 1 End If If (reader.NodeType = XmlNodeType.Element _ And price.Equals(reader.LocalName)) Then booktotal += reader.ReadElementContentAsDecimal() End If End While End Using Response.Write(String.Format("Found {0} books that total {1:C}!", _ bookcount, booktotal)) C# int bookcount = 0; decimal booktotal = 0; XmlReaderSettings settings = new XmlReaderSettings(); string booksSchemaFile = Path.Combine(Request.PhysicalApplicationPath, "books.xsd"); NameTable nt = new NameTable(); object book = nt.Add("book"); object price = nt.Add("price"); settings.NameTable = nt; string booksFile = Path.Combine(Request.PhysicalApplicationPath, "books.xml"); using (XmlReader reader = XmlReader.Create(booksFile, settings)) { while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element && book.Equals(reader.LocalName))//A subtle, but significant change! { bookcount++; } if (reader.NodeType == XmlNodeType.Element && price.Equals(reader.LocalName)) { booktotal += reader.ReadElementContentAsDecimal (); } } } Response.Write(String.Format("Found {0} books that total {1:C}!", bookcount, booktotal)); The booktotal variable from Listing 10-8 is strongly typed as a decimal so that, in the String.Format call, it can be formatted as currency using the formatting string { 1:C } . This results in output from the browser similar to the following: Found 3 books that total $30.97! 516 . _ XmlSchemaValidationFlags.ReportValidationWarnings AddHandler settings.ValidationEventHandler, _ AddressOf settings_ValidationEventHandler 5 13 Evjen c10.tex V2 - 01/28/2008 2:13pm Page 51 4 Chapter 10: Working with XML and LINQ to. passed into the XDocument ’s validate method. 51 1 Evjen c10.tex V2 - 01/28/2008 2:13pm Page 51 2 Chapter 10: Working with XML and LINQ to XML Listing 10-6q: Validating XML with a LINQ XDocument against. books.Count())); } } In both the C# and VB examples, we take advantage of the implicit typing by not indicating the return type in the call to XDocument.Decendants .InVBweuse Dim books and in C# we use var

Ngày đăng: 05/07/2014, 18:20

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan