1. Trang chủ
  2. » Công Nghệ Thông Tin

C# 3.0 Cookbook phần 8 pdf

88 454 0

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

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 88
Dung lượng 423,51 KB

Nội dung

Validating Modified XML Documents Without Reloading | 591 One item to be aware of when dealing with multiple XmlDocuments is that when you take a node from one XmlDocument, you cannot just append it as a child to a node in a different XmlDocument because the node has the context of the original XmlDocument.If you try to do this, you will get the following exception message: The node to be inserted is from a different document context. To fix this, use the XmlDocument.ImportNode method, which will make a copy (deep when the second parameter is true, or shallow when the second parameter is false) of the node you are bringing over to the new XmlDocument. For instance, when you add the shipping information like so: invoice.DocumentElement.AppendChild(invoice.ImportNode(shipInfo,true)); this line takes the shipInfo node, clones it deeply, then appends it to the main invoice node. See Also The “XElement Class,” “The Three Parts of a LINQ Query,” “XmlDocument Class,” “XmlElement Class,” and “XmlAttribute Class” topics in the MSDN documentation. 15.11 Validating Modified XML Documents Without Reloading Problem You are using the XDocument or the XmlDocument to modify an XML document loaded in memory. Once the document has been modified, the modifications need to be ver- ified, and schema defaults need to be enforced. Solution Use the XDocument.Validate method to perform the validation and apply schema defaults and type information. Create an XmlSchemaSet with the XML Schema document (book.xsd) and an XmlReader and then load the book.xml file using XDocument.Load: // Create the schema set XmlSchemaSet xmlSchemaSet = new XmlSchemaSet( ); // add the new schema with the target namespace // (could add all the schema at once here if there are multiple) xmlSchemaSet.Add("http://tempuri.org/Book.xsd", XmlReader.Create(@" \ \Book.xsd")); XDocument book = XDocument.Load(@" \ \Book.xml"); 592 | Chapter 15: XML Set up a ValidationEventHandler to catch any errors and then call XDocument. Validate with the schema set and the event handler to validate book.xml against the book.xsd schema: ValidationHandler validationHandler = new ValidationHandler( ); ValidationEventHandler validationEventHandler = validationHandler.HandleValidation; // validate after load book.Validate(xmlSchemaSet, validationEventHandler); The ValidationHandler class holds the current validation state in a ValidXml property and the code for the ValidationEventHandler implementation method HandleValidation: public class ValidationHandler { private object _syncRoot = new object( ); public ValidationHandler( ) { lock(_syncRoot) { // set the initial check for validity to true this.ValidXml = true; } } public bool ValidXml { get; private set; } public void HandleValidation(object sender, ValidationEventArgs e) { lock(_syncRoot) { // we got called, so this isn't valid ValidXml = false; Console.WriteLine("Validation Error Message: {0}", e.Message); Console.WriteLine("Validation Error Severity: {0}", e.Severity); if (e.Exception != null) { Console.WriteLine("Validation Error Line Number: {0}", e.Exception.LineNumber); Console.WriteLine("Validation Error Line Position: {0}", e.Exception.LinePosition); Console.WriteLine("Validation Error Source: {0}", e.Exception.Source); Console.WriteLine("Validation Error Source Schema: {0}", e.Exception.SourceSchemaObject); Console.WriteLine("Validation Error Source Uri: {0}", e.Exception.SourceUri); Console.WriteLine("Validation Error thrown from: {0}", e.Exception.TargetSite); Console.WriteLine("Validation Error callstack: {0}", e.Exception.StackTrace); } } Validating Modified XML Documents Without Reloading | 593 } } Add a new element node that is not in the schema into the XDocument and then call Validate again with the schema set and event handler to revalidate the changed XDocument. If the document triggers any validation events, then the ValidationHandler.ValidXml property is set to false in the ValidationHandler instance: // add in a new node that is not in the schema // since we have already validated, no callbacks fire during the add book.Root.Add(new XElement("BogusElement","Totally")); // now we will do validation of the new stuff we added book.Validate(xmlSchemaSet, validationEventHandler); if (validationHandler.ValidXml) Console.WriteLine("Successfully validated modified LINQ XML"); else Console.WriteLine("Modified LINQ XML did not validate successfully"); Console.WriteLine( ); You could also use the XmlDocument.Validate method to perform the validation in a similar fashion to XDocument: string xmlFile = @" \ \Book.xml"; string xsdFile = @" \ \Book.xsd"; // Create the schema set. XmlSchemaSet schemaSet = new XmlSchemaSet( ); // Add the new schema with the target namespace // (could add all the schema at once here if there are multiple). schemaSet.Add("http://tempuri.org/Book.xsd", XmlReader.Create(xsdFile)); // Load up the XML file. XmlDocument xmlDoc = new XmlDocument( ); // Add the schema. xmlDoc.Schemas = schemaSet; Load the book.xml file into the XmlDocument, set up a ValidationEventHandler to catch any errors, and then call Validate with the event handler to validate book.xml against the book.xsd schema: // validate after load xmlDoc.Load(xmlFile); ValidationHandler handler = new ValidationHandler( ); ValidationEventHandler eventHandler = handler.HandleValidation; xmlDoc.Validate(eventHandler); Add a new element node that is not in the schema into the XmlDocument and then call Validate again with the event handler to revalidate the changed XmlDocument. If the document triggers any validation events, then the ValidationHandler.ValidXml prop- erty is set to false: 594 | Chapter 15: XML // Add in a new node that is not in the schema. // Since we have already validated, no callbacks fire during the add XmlNode newNode = xmlDoc.CreateElement("BogusElement"); newNode.InnerText = "Totally"; // Add the new element. xmlDoc.DocumentElement.AppendChild(newNode); // Now we will do validation of the new stuff we added. xmlDoc.Validate(eventHandler); if (handler.ValidXml) Console.WriteLine("Successfully validated modified XML"); else Console.WriteLine("Modified XML did not validate successfully"); Discussion One advantage to using XmlDocument over XDocument is that there is an override to the XmlDocument.Validate method that allows you to pass a specific XmlNode to validate. This fine grain of control is not present on XDocument. If the XmlDocument is large, this override to Validate should be used: public void Validate( ValidationEventHandler validationEventHandler, XmlNode nodeToValidate ); One other approach to this problem is to instantiate an instance of the XmlNodeReader with the XmlDocument and then create an XmlReader with validation settings as shown in Recipe 15.4. This would allow for continual validation while the reader navigated through the underlying XML. The output from running the code is listed here: Validation Error Message: The element 'Book' in namespace 'http://tempuri.org/Book. xsd' has invalid child element 'BogusElement'. List of possible elements expected: 'Chapter' in namespace 'http://tempuri.org/Book.xsd'. Validation Error Severity: Error Validation Error Line Number: 0 Validation Error Line Position: 0 Validation Error Source: Validation Error Source Schema: Validation Error Source Uri: file:///C:/PRJ32/Book_2_0/C%23Cookbook2/Code/ CSharpRecipes/Book.xml Validation Error thrown from: Validation Error callstack: Modified XML did not validate successfully Notice that the BogusElement element that you added was not part of the schema for the Book element, so you got a validation error along with the information about where the error occurred. Finally, you got a report that the modified XML did not validate correctly. Extending Transformations | 595 See Also Recipe 15.3; the “XDocument Class,” and the “XmlDocument.Validate” topics in the MSDN documentation. 15.12 Extending Transformations Problem You want to perform operations that are outside the scope of the transformation technology to include data in the transformed result. Solution If you are using LINQ to XML, you can call out to a function directly when trans- forming the result set, as shown here by the call to GetErrata: XElement publications = XElement.Load(@" \ \publications.xml"); XElement transformedPublications = new XElement("PublishedWorks", from b in publications.Elements("Book") select new XElement(b.Name, new XAttribute(b.Attribute("name")), from c in b.Elements("Chapter") select new XElement("Chapter", GetErrata(c)))); Console.WriteLine(transformedPublications.ToString( )); Console.WriteLine( ); The GetErrata method used in the above sample is listed here: private static XElement GetErrata(XElement chapter) { // In here, we could go do other lookup calls (XML, database, web service) // to get information to add back in to the transformation result string errata = string.Format("{0} has {1} errata", chapter.Value, chapter.Value.Length); return new XElement("Errata", errata); } If you are using XSLT, you can add an extension object to the transformation that can perform the operations necessary based on the node it is passed. This can be accomplished by using the XsltArgumentList.AddExtensionObject method. This object you’ve created ( XslExtensionObject) can then be accessed in the XSLT and a method called on it to return the data you want included in the final transformed result: string xmlFile = @" \ \publications.xml"; string xslt = @" \ \publications.xsl"; //Create the XslTransform and load the stylesheet. // This is not XslCompiledTransform because it gives a different empty node. 596 | Chapter 15: XML //Create the XslCompiledTransform and load the stylesheet. XslCompiledTransform transform = new XslCompiledTransform( ); transform.Load(xslt); // Load the XML. XPathDocument xPathDoc = new XPathDocument(xmlFile); // Make up the args for the stylesheet with the extension object. XsltArgumentList xslArg = new XsltArgumentList( ); // Create our custom extension object. XSLExtensionObject xslExt = new XSLExtensionObject( ); xslArg.AddExtensionObject("urn:xslext", xslExt); // Send output to the console and do the transformation. using (XmlWriter writer = XmlWriter.Create(Console.Out)) { transform.Transform(xPathDoc, xslArg, writer); } Note that when the extension object is added to the XsltArgumentList, it supplies a namespace of urn:xslext. This namespace is used in the XSLT stylesheet to refer- ence the object. The XSLExtensionObject is defined here: // Our extension object to help with functionality public class XslExtensionObject { public XPathNodeIterator GetErrata(XPathNodeIterator nodeChapter) { // In here, we could go do other lookup calls // (XML, database, web service) to get information to // add back in to the transformation result. string errata = string.Format("<Errata>{0} has {1} errata</Errata>", nodeChapter.Current.Value, nodeChapter.Current.Value.Length); XmlDocument xDoc = new XmlDocument( ); xDoc.LoadXml(errata); XPathNavigator xPathNav = xDoc.CreateNavigator( ); xPathNav.MoveToChild(XPathNodeType.Element); XPathNodeIterator iter = xPathNav.Select("."); return iter; } } The GetErrata method is called during the execution of the XSLT stylesheet to pro- vide data in XPathNodeIterator format to the transformation. The xmlns:xslext namespace is declared as urn:xslext, which matches the namespace value you passed as an argument to the transformation. In the processing of the Book template for each Chapter,anxsl:value-of is called with the select criteria containing a call to the xslext:GetErrata method. The stylesheet makes the call, as shown here: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xslext="urn:xslext"> <xsl:template match="/"> Extending Transformations | 597 <xsl:element name="PublishedWorks"> <xsl:apply-templates/> </xsl:element> </xsl:template> <xsl:template match="Book"> <Book> <xsl:attribute name ="name"> <xsl:value-of select="@name"/> </xsl:attribute> <xsl:for-each select="Chapter"> <Chapter> <xsl:value-of select="xslext:GetErrata(/)"/> </Chapter> </xsl:for-each> </Book> </xsl:template> </xsl:stylesheet> Discussion Using LINQ to XML, you can extend your transformation code to include addi- tional logic simply by adding method calls that know how to operate and return XElements. This is simply adding another method call to the query that contributes to the result set, and no additional performance penalty is assessed just by the call. Cer- tainly if the operation is expensive it could slow down the transformation, but this is now easily located when your code is profiled. The ability to call custom code from inside of an XSLT stylesheet is a very powerful one, but one that should be used cautiously. Adding code like this into stylesheets usually renders them less useful in other environments. If the stylesheet never has to be used to transform XML in another parser, this can be a good way to offload work that is either difficult or impossible to accomplish in regular XSLT syntax. The sample data used in the Solution is presented here: <?xml version="1.0" encoding="utf-8"?> <Publications> <Book name="Subclassing and Hooking with Visual Basic"> <Chapter>Introduction</Chapter> <Chapter>Windows System-Specific Information</Chapter> <Chapter>The Basics of Subclassing and Hooks</Chapter> <Chapter>Subclassing and Superclassing</Chapter> <Chapter>Subclassing the Windows Common Dialog Boxes</Chapter> <Chapter>ActiveX Controls and Subclassing</Chapter> <Chapter>Superclassing</Chapter> <Chapter>Debugging Techniques for Subclassing</Chapter> <Chapter>WH_CALLWNDPROC</Chapter> <Chapter>WH_CALLWNDPROCRET</Chapter> <Chapter>WH_GETMESSAGE</Chapter> <Chapter>WH_KEYBOARD and WH_KEYBOARD_LL</Chapter> <Chapter>WH_MOUSE and WH_MOUSE_LL</Chapter> <Chapter>WH_FOREGROUNDIDLE</Chapter> 598 | Chapter 15: XML <Chapter>WH_MSGFILTER</Chapter> <Chapter>WH_SYSMSGFILTER</Chapter> <Chapter>WH_SHELL</Chapter> <Chapter>WH_CBT</Chapter> <Chapter>WH_JOURNALRECORD</Chapter> <Chapter>WH_JOURNALPLAYBACK</Chapter> <Chapter>WH_DEBUG</Chapter> <Chapter>Subclassing .NET WinForms</Chapter> <Chapter>Implementing Hooks in VB.NET</Chapter> </Book> <Book name="C# Cookbook"> <Chapter>Numbers</Chapter> <Chapter>Strings and Characters</Chapter> <Chapter>Classes And Structures</Chapter> <Chapter>Enums</Chapter> <Chapter>Exception Handling</Chapter> <Chapter>Diagnostics</Chapter> <Chapter>Delegates and Events</Chapter> <Chapter>Regular Expressions</Chapter> <Chapter>Collections</Chapter> <Chapter>Data Structures and Algorithms</Chapter> <Chapter>File System IO</Chapter> <Chapter>Reflection</Chapter> <Chapter>Networking</Chapter> <Chapter>Security</Chapter> <Chapter>Threading</Chapter> <Chapter>Unsafe Code</Chapter> <Chapter>XML</Chapter> </Book> <Book name="C# Cookbook 2.0"> <Chapter>Numbers and Enumerations</Chapter> <Chapter>Strings and Characters</Chapter> <Chapter>Classes And Structures</Chapter> <Chapter>Generics</Chapter> <Chapter>Collections</Chapter> <Chapter>Iterators and Partial Types</Chapter> <Chapter>Exception Handling</Chapter> <Chapter>Diagnostics</Chapter> <Chapter>Delegates, Events, and Anonymous Methods</Chapter> <Chapter>Regular Expressions</Chapter> <Chapter>Data Structures and Algorithms</Chapter> <Chapter>File System IO</Chapter> <Chapter>Reflection</Chapter> <Chapter>Web</Chapter> <Chapter>XML</Chapter> <Chapter>Networking</Chapter> <Chapter>Security</Chapter> <Chapter>Threading and Synchronization</Chapter> <Chapter>Unsafe Code</Chapter> <Chapter>Toolbox</Chapter> </Book> </Publications> Getting Your Schemas in Bulk from Existing XML Files | 599 See Also The “LINQ, transforming data” and “XsltArgumentList Class” topics in the MSDN documentation. 15.13 Getting Your Schemas in Bulk from Existing XML Files Problem You have come on to a new project in which XML was used for data transmission, but the programmers who came before you didn’t use an XSD for one reason or another. You need to generate beginning schema files for each of the XML examples. Solution Use the XmlSchemaInference class to infer schema from the XML samples. The GenerateSchemasForDirectory function in Example 15-12 enumerates all of the XML files in a given directory and processes each of them using the GenerateSchemasForFile method. GenerateSchemasForFile uses the XmlSchemaInference.InferSchema method to get the schemas for the given XML file. Once the schemas have been determined, GenerateSchemasForFile rolls over the collection and saves out each schema to an XSD file using a FileStream. Example 15-12. Generating an XML Schema public static void GenerateSchemasForFile(string file) { // set up a reader for the file using (XmlReader reader = XmlReader.Create(file)) { XmlSchemaSet schemaSet = new XmlSchemaSet( ); XmlSchemaInference schemaInference = new XmlSchemaInference( ); // get the schema schemaSet = schemaInference.InferSchema(reader); string schemaPath = string.Empty; foreach (XmlSchema schema in schemaSet.Schemas( )) { // make schema file path and write it out schemaPath = Path.GetDirectoryName(file) + @"\" + Path.GetFileNameWithoutExtension(file) + ".xsd"; using (FileStream fs = new FileStream(schemaPath, FileMode.OpenOrCreate)) { schema.Write(fs); 600 | Chapter 15: XML The GenerateSchemasForDirectory method can be called like this: // Get the directory two levels up from where we are running. DirectoryInfo di = new DirectoryInfo(@" \ "); string dir = di.FullName; // Generate the schema. GenerateSchemasForDirectory(dir); Discussion Having an XSD for the XML files in an application allows for a number of things: 1. Validation of XML presented to the system 2. Documentation of the semantics of the data 3. Programmatic discovery of the data structure through XML reading methods Using the GenerateSchemasForFile method can jump-start the process of developing schema for your XML, but each schema should be reviewed by the team member responsible for producing the XML. This will help to ensure that the rules as stated in the schema are correct and also to make sure that additional items such as schema default values and other relationships are added. Any relationships that were not present in the example XML files would be missed by the schema generator. See Also The “XmlSchemaInference Class” and “XML Schemas (XSD) Reference” topics in the MSDN documentation. } } } } public static void GenerateSchemasForDirectory(string dir) { // make sure the directory exists if (Directory.Exists(dir)) { // get the files in the directory string[] files = Directory.GetFiles(dir, "*.xml"); foreach (string file in files) { GenerateSchemasForFile(file); } } } Example 15-12. Generating an XML Schema (continued) [...]... Console.WriteLine(we.ToString( )); return; } Console.WriteLine("Downloaded {0} to {1}", uri, tempFile); } This will produce the following output: Retrieving file from http://localhost:4 088 /CSCBWeb/DownloadData16_4.aspx Downloaded http://localhost:4 088 /CSCBWeb/DownloadData16_4.aspx to C:\Documents a nd Settings\Jay Hilyard\Local Settings\Temp\tmp6F0.tmp To upload a file to a URL, use UploadFile like so: // Make a client... specific to the save Response.Write("Failed to save file with error: " + hex.Message); } } } } This will produce the following output: Uploading to http://localhost:4 088 /CSCBWeb/UploadData16_4.aspx Uploaded successfully to http://localhost:4 088 /CSCBWeb/UploadData16_4.aspx Discussion WebClient simplifies downloading of files and bytes in files, as these are common tasks when dealing with the Web The more... documentation 6 18 | Chapter 16: Networking 16.4 Transferring Data via HTTP Problem You need to download data from or upload data to a location specified by a URL; this data can be either an array of bytes or a file Solution Use the WebClient.UploadData or WebClient.DownloadData methods to transfer data using a URL To download the data for a web page, do the following: Uri uri = new Uri("http://localhost:4 088 /CSCBWeb/DownloadData16_4.aspx");... 101; LEFT: 142px; POSITION: absolute; TOP: 164px">This is downloaded html! You can also download data to a file using DownloadFile: Uri uri = new Uri("http://localhost:4 088 /CSCBWeb/DownloadData16_4.aspx"); // Make a client using (WebClient client = new WebClient( )) { // go get the file Console.WriteLine("Retrieving file from {0} \r\n", uri); // get file and put it in... version="1.0" encoding="utf -8" ?> The ParameterExample.xslt file contains the following: . ns.ReadTimeout = 600 00; // Loop to receive all the data sent by the client. int bytesRead = 0; do { // read the data try { bytesRead = ns.Read(bytes, 0, bytes.Length); if (bytesRead > 0) { . Error Line Number: 0 Validation Error Line Position: 0 Validation Error Source: Validation Error Source Schema: Validation Error Source Uri: file:///C:/PRJ32/Book_2 _0/ C%2 3Cookbook2 /Code/ CSharpRecipes/Book.xml Validation. the following: <?xml version="1 .0& quot; encoding="UTF -8& quot; ?> <xsl:stylesheet version="1 .0& quot; xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output

Ngày đăng: 12/08/2014, 09:22

TỪ KHÓA LIÊN QUAN