Chapter 8. There, you saw how easy it is to perform an XSL or XSLT transformation against an XML document using the new
XslTransform object. However, we only applied it to two disk files (an XML document and a style sheet) by specifying the paths to these files.
You can use the
XslTransform object to perform transformations when the document is not actually a disk file. This could well be the case in an application that processes XML. For example, it could be referenced by an
XmlTextReader , or stored in the
XmlDocument object returned by a web service or business component, or even pointed to by an existing
XPathNavigator . And you might not want the results to be written to disk as a file – you might need them as a
String or a
StringBuilder object. The next example attempts to demonstrate several of these scenarios.
Note |
In version 1.1, Microsoft suggests an updated approach to loading stylesheets that are not fully trusted. See the Loading Stylesheets and Schemas with an XmlResolver section of this chapter for details. |
The Different ways to use the XslTransform object (
multi-xsl-transform.aspx ) example page is shown in Figure 11-23.
Figure 11-23:
It loads the XML document and the stylesheet from disk at the start of the page, but then references the XML document in a range of ways to demonstrate the possibilities. It also performs a transformation to a
String , and displays this in the page before writing it to disk separately – rather than directly through the
XslTransform object:
Notice that the XML string is not wrapped, and appears a single line in the page. However, if you open the hyperlink at the bottom of the example page (shown in Figure 11-24), you'll see the transformed result. Some of the nodes have been collapsed to reduce the overall size of the page in the screenshot:
Figure 11-24:
Note |
As with earlier examples, you must run this page in a browser on the web server itself to be able to open the transformed file using the physical path in the hyperlink at the bottom of the page. |
Figure 11-25 shows the simple stylesheet being used. It extracts the
<AuthorName> elements from the XML source document and generates a new XML document containing these – within a root element named
<AuthorList> :
Figure 11-25:
After creating the paths to the XML document and XSL stylesheet being used, the code in the page displays hyperlinks to these documents. Here, we're only interested in loading the documents and executing the transformation itself, and so haven't repeated the code.
We start by creating a new
XslTransform object and loading the stylesheet into it, using the
Load method with the path and filename of the stylesheet. To load the XML document in this example, an
XmlTextReader is used. This isn't the quickest or shortest way to do it, but it aims to give you some ideas about how you can use the various objects in your projects.
For example, by loading the XML document with an
XmlTextReader , you'd have the opportunity to validate it at the same time if this is a requirement. You would just need to assign an
XmlValidatingReader to the
XmlTextReader .
So, the code creates the
XmlTextReader for the XML document and then creates a new
XPathDocument from this
XmlTextReader . The constructor for the
XPathDocument automatically loads the XML from disk into the new
XPathDocument . You can then create a new
XPathNavigator based on the
XPathDocument by calling the
'create a new XslTransform object to do the transformation
Dim objTransform As New XslTransform()
'load the XSL stylesheet into the XslTransform object
objTransform.Load(strXSLPath)
'create a new XmlTextReader object to fetch XML document
Dim objXTReader As New XmlTextReader(strXMLPath)
'create a new XPathDocument object from the XmlTextReader
Dim objXPDoc As New XPathDocument(objXTReader)
'create a new XPathNavigator object from the XPathDocument
Dim objXPNav As XPathNavigator
objXPNav = objXPDoc.CreateNavigator()
The
Transform method of the
XslTransform object can output the result of a transformation to an
XmlReader object, a
TextReader object, or an
XmlWriter object. If you want the result to be available as a string, for use elsewhere in your applications, you can use an
XmlReader or a
TextReader object (depending on whether the result is XML that you want to parse as you use it, or some other format that can't be used with an
XmlReader ).
Our example transforms the XML into an
XmlReader object. We declare a variable to hold the object, and call the
Transform method of the
XslTransform object, passing it the
XPathNavigator created for the XML document. The second argument allows us to pass in an
XsltArgumentList object that can contain the parameters or arguments used by the stylesheet. As there are no parameters in our stylesheet, we use the value
Nothing for this argument.
Once we get back the
XmlReader object, we can display the contents – the result of the transformation. The easiest way is to use the
ReadOuterXml method of the reader:
'create a variable to hold the XmlReader object that is
'returned from the Transform method
Dim objReader As XmlReader
'perform the transformation using the XSL file in the
'XslTransform and the XML document referenced by the
'XPathNavigator. The result is in the XmlReader object
objReader = objTransform.Transform(objXPNav, Nothing)
'display the contents of the XmlReader object
objReader.MoveToContent()
outResults.InnerText = objReader.ReadOuterXml()
The alternative output device for the
Transform method of the
XslTransform object is an
XmlWriter object. This is ideal for piping the output back to a disk file.
In our example, we create an
XmlTextWriter object (a public class that inherits from
XmlWriter ), using a path and filename created earlier in the page. The second parameter to the constructor is the encoding to be used – if we specify
Nothing , it sets the encoding to the default
UTF-8 .
Now we can use the
XmlTextWriter to create the disk file. We start with an XML declaration (by simply calling the
WriteStartDocument method) and add a comment element. We can then perform the transformation, sending the results directly to the
XmlTextWriter – which writes them straight to the disk file.
We finish by calling the
WriteEndDocument method, to close any open elements and finalize the document, and then close it and display a hyperlink so that the results can be examined.
'create an XmlTextWriter object to write result to disk
Dim objWriter As New XmlTextWriter(strOutPath, Nothing)
'write the opening <?xml .. ?> declaration and a comment
objWriter.WriteStartDocument()
objWriter.WriteComment("List of authors created " & Now())
'transform the XML into the XmlTextWriter
objTransform.Transform(objXPNav, Nothing, objWriter)
'ensure that all open elements are closed and end the document
objWriter.WriteEndDocument()
'flush the buffer to disk and close the file
objWriter.Close()
outFile.InnerHtml = "<a href="" & strOutPath & "">" & strOutPath & "</a>"
One of the major changes to the
System.Xml namespace in version 1.1 is a revision of the best practice and the various approaches available for loading an XSLT stylesheet into instances of the
XslTransform class. These changes are associated with the percieved increasing risks from loading stylesheets that can contain script code, and references to other external resources. In version 1.0, once the stylesheet is loaded into the
XslTransform object, it runs as fully trusted, irrespective of the source. To some extent, the same issue applies when loading a schema, though this is less likely to be a security risk than a stylesheet.
When you call the
Load or
Transform method of the
XslTransform class, the
Add method of the
XmlSchemaCollection class, or the
Compile method of the
XmlSchema class, they automatically create an
XmlUrlResolver instance for the schemas or stylesheets you reference, and use these to resolve any references to external entities, DTDs, other schemas, and resources pointed to by
xsl:include and
xsl:import statements.
However, where you are not able to verify whether a stylesheet or schema is safe – for example, when loading it over the Web from an untrusted source – you should instead create instances of the
XmlUrlResolver or
XmlSecureResolver classes that apply the security constraints you want to apply.
The
XmlUrlResolver is the same as in verison 1.0 of the .NET Framework, but the
XmlSecureResolver class is new in version 1.1. It is used for the same purposes as the
XmlUrlResolver , but allows you to restrict the permissions available to the resources you use it to access.
The
XmlUrlResolver and
XmlSecureResolver classes expose a
Credentials property, which is a reference to a
CredentialCache instance that can hold one or more credentials. Each credential is itself an instance of the
NetworkCredential class. To create a
NetworkCredential , you provide the user name or ID, the matching password, and (optionally) the domain in which that password should be validated in a call to the constructor:
Dim objCred As New System.Net.NetworkCredential("userid", "password","domain")
The
CredentialCache can be used to associate
NetworkCredential instances with specific URLs so that the appropriate one is used when the stylesheet or schema is loaded.
To create a
CredentialCache that contains more than one
NetworkCredential , you create each one and add it to the cache, specifying the URL to which it applies in the
Add method:
' create two NetworkCredential objects
Dim objCred1 As New System.Net.NetworkCredential("bob", "fH8$o3")
Dim objCred2 As New System.Net.NetworkCredential("alice", "TR3$3aq")
' create and populate CredentialCache object
Dim objCC As New CredentialCache()
objCC.Add(New Uri("http://www.site1.com/"), "Basic", objCred1);
objCC.Add(New Uri("http://www.site2.com/"), "Basic", objCred1);
objCC.Add(New Uri("http://www.site3.com/"), "Digest", objCred2);
Once you've created your
NetworkCredential or
CredentialCache , you can assign it to the
Credentials property of a new
XmlUrlResolver . You can then specify the
XmlUrlResolver when calling the
Load or
Transform method of the
XslTransform class, the
Add method of the
XmlSchemaCollection class, or the
Compile method of the
XmlSchema class – for example, when you have a single
NetworkCredential :
Dim objResolver As New XmlUrlResolver()
objResolver.Credentials = objMyNetworkCredential
Dim objTransform As New XslTransform()
objTransform.Load(stylesheet-url, objResolver)
Or when you have a
CredentialsCache instance:
Dim objResolver As New XmlUrlResolver()
objResolver.Credentials = objMyCredentialsCache
Dim objTransform As New XslTransform()
objTransform.Load(stylesheet-url, objResolver)
As a result of this new approach, the recommended overloads of the
Load method of the
XslTransform class are now limited to these five (all others are obsolete). The first overload should only be used where you can verify the stylesheet is safe, or from a fully trusted source:
Load( stylesheet-url
) .
Load( stylesheet-url
, XmlResolver
) .
Load( XPathNavigator
, XmlResolver
, Evidence
) .
Load( XmlReader, XmlResolver
, Evidence
) .
Load( XPathNavigator, XmlResolver, Evidence).
The Evidence parameter required for the last three overloads is a reference to an instance of the
Evidence class. Microsoft's documentation covers four different scenarios, with different levels of security risk, when deciding how to manage the permissions that a stylesheet will have:
Where the stylesheet is self-contained, is located on a local disk, or comes from a source that you trust, and you want to allow it to execute with the same permissions as the code performing the transformation (the current assembly).
objTransform.Load("stylesheet-url", objResolver, _
Me.GetType().Assembly.Evidence)
Where the XSLT stylesheet comes from an outside source that is known, and is a verifiable URL,
Evidence can be created from that URL using the
CreateEvidenceForUrl method of the
XmlSecureResolver object.
Dim objEvidence As Evidence _
= XmlSecureResolver.CreateEvidenceForUrl("stylesheet-url")
objTransform.Load("stylesheet-url", objResolver, objEvidence)
Where the XSLT stylesheet comes from an outside source that is not known or not trusted, and you want to ensure that script blocks are not processed, the XSLT
document function is not supported, and privileged extension objects are disallowed, you can set the
Evidence parameter to
Nothing (
null in C#).
objTransform.Load("stylesheet-url", objResolver, Nothing)
Where the XSLT stylesheet comes from an outside source that is not known or not trusted, and in addition to these limitaions, you want to ensure that
xsl:import and
xsl:include elements are not processed, you can set the
XmlResolver parameter to
Nothing (
null in C#).
objTransform.Load("stylesheet-url", Nothing)
For the recommended overloads of the
XslTransform.Transform method, see the .NET SDK topic Reference | Class Library | System.Xml.Xsl | XslTransform Class | Methods | Transform Method.
For the recommended overloads of the
XmlSchemaCollection.Add method, see the .NET SDK topic Reference | Class Library | System.Xml.Schema | XslTransform Class | Methods | Transform Method.
For the recommended overloads of the
XmlSchemaCollection.Compile method, see the .NET SDK topic eference | Class Library | System.Xml.Schema | XmlSchemaCollection Class | Methods | Add Method.