Edit XSLT OutputSettings

sybe
sybe used Ask the Experts™
on
Is there any way to edit the Outputsetting of an XslCompiledTransform?

XslCompiledTransform.OutputSettings is readonly.

It is possible to generate an XmlWriterSettings from scratch, although I haven't gotten that far to use such an object, because

XmlWriterSettings.OutputMethod is in that case read-only, which makes it useless for my purpose.

The only way left to edit the outputsettings of an xslt, is to edit it as a string before loading it into an XslCompiledTransform. Can anyone confirm this?
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Top Expert 2009

Commented:
I cannot confirm it, because it is not correct. You can simply set any outputsettings directly on the XslCompiledTransform object (though the property itself is readonly, you can use its properties and methods).

However, the output settings are generally set in the XmlWriter and the XmlWriter gets its settings from the XmlWriterSettings. Though it may not be the first thing you'd think of (it takes just about anybody forever to find out this way of thinking of the object model ;-), the following should work for you:

// this enables document and script functions
XsltSettings xsltSettings = new XsltSettings(true, true);
 
// this sets the indent settings:
XmlWriterSettings xmlSettings = new XmlWriterSettings();
xmlSettings.Indent = true;
 
// this creates an XmlWriter
XmlWriter xmlWriter = XmlWriter.Create("output.xml", xmlSettings);
 
// this creates parameters to be passed to the XSLT:
XsltArgumentList xsltArgs = new XsltArgumentList();
xsltArgs.AddParam("someParameter", "", "SomeValue");
 
// this puts the above together, compiles the stylesheet and runs it
XslCompiledTransform compiledXsl = new XslCompiledTransform();
compiledXsl.Load("somefile.xsl", xsltSettings, null);
compiledXsl.Transform("input.xml", xsltArgs, xmlWriter);

Open in new window

Top Expert 2009

Commented:
My first paragraph above is correct in most scenarios, but not here. These settings are set when the stylesheet is compiled and are not meant to be overridden afterward. You can experiment with this using the following code to override the inner flag that is set once the XslCompiledTransform is created, but though it doesn't yield any errors anymore, the new settings are wisely ignored.

// force the readonly to be false using reflection:
compiledXslt.OutputSettings.GetType().InvokeMember("isReadOnly",
    BindingFlags.SetField 
    | BindingFlags.NonPublic
    | BindingFlags.Instance, null, 
    xslt.OutputSettings, new object[] { false });
 
// set these without raising a runtime error
compiledXslt.OutputSettings.Encoding = Encoding.Unicode;
compiledXslt.OutputSettings.OmitXmlDeclaration = true;
 
// but the effect will be void, you should still use the XmlWriterSettings instead.

Open in new window

Top Expert 2009

Commented:
Line 6 should read:

    compiledXslt.OutputSettings, new object[] { false });

JavaScript Best Practices

Save hours in development time and avoid common mistakes by learning the best practices to use for JavaScript.

Author

Commented:
That however still leaves no way to edit the OutputMethod

If this is in the XSL-file

<xsl:output method="xml" encoding="UTF-8"/>

It can not be changed on the fly to

<xsl:output method="html" encoding="UTF-8"/>


Such a change can only be done *before* loading the xsl into XslCompiledTransform.
The same counts for the "media-type" attribute in the xsl:output node. XmlWriterSettings does not even have a property for that

http://msdn.microsoft.com/en-us/library/system.xml.xmlwritersettings_members.aspx

http://www.w3.org/TR/xslt#output
Top Expert 2009

Commented:
> It can not be changed on the fly to
> Such a change can only be done *before* loading the xsl into XslCompiledTransform.

Actually it can. See my first example, the settings in the XmlWriter override this. And the XmlWriter is only added the moment you do Transform.

> The same counts for the "media-type" attribute in the xsl:output node.
That is correct. The common method for changing the XSLT itself before applying it is using XSLT (or DOM methods, for that matter) to change these values prior to loading. In this case, you cannot change it after loading. But that's logical if you consider that the media type is something that has no meaning in offline transforms, you don't really need to set it (if you set the media type, you must not set the encoding, and vice versa). The encoding, however, can be set through the xml settings.
Top Expert 2009

Commented:
Perhaps it is good to take a step back and ask you what your actual goal is. For instance, in XSLT 2.0, you can set all these properties through AVTs and you can control it completely from either the inside or the outside. But it may just as well be that you don't actually need control over these properties to achieve your goal.

Author

Commented:
So I write this in VS 2005.

    Dim oSettings As XmlWriterSettings = New XmlWriterSettings
    oSettings.OutputMethod = XmlOutputMethod.Xml

The last line is marked as an error "Property 'OutputMethod' is 'ReadOnly'"
When I run the file anyway, it produces this error:
    Compiler Error Message: BC30526: Property 'OutputMethod' is 'ReadOnly'.

Searching on that error message produces a earlier report (2005) where it was said to be "It was incorrectly documented" (as not read-only).
 
http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=95159

I do not see in your example how you set the outputmethod to "xml" or "html".

Top Expert 2009
Commented:
> I do not see in your example how you set the outputmethod to "xml" or "html".

ah, I see what you mean. Any settings can be changed, but not that one (or mediatype). The reason for that is actually in that other question, see the part on the xslt compilation.

However, you can trigger this in another way. The HTML output is automatically set when the output starts with <html> as the root node (only whitespace before it), has no namespace and has children. It is trivial to set the rootnode through a parameter.

I know, this all may sound cumbersome and other processors have chosen a different path (Saxon for instance, and any XSLT 2.0 processor, allows all this at a later stage, like you wish), but it is done with a reason in mind (even though that is debatable...).

<!-- do not specify method here -->
<xml:output indent="yes" />
 
<!-- triggers html if html, otherwise triggers xml -->
<xsl:param name="rootnode" value="html" />
 
<xsl:template match="/">
   <xsl:element name="{$rootnode}">
      ... your code starts here
   </xsl:element>
</xsl:template>

Open in new window

Author

Commented:
Concerning my goal: actually I am simply avoiding XslCompiledTransform until the last step before transformation.

Loading the xsl-file as XmlDocument, do the necessary processing in that, then load the resulting XmlDocument in a XslCompiledTransform object.

It works of course, but it feels not totally right. Either I am missing a point, or Microsoft is. Chances are bigger that I am missing a point. It seems only logical that it is possible to create and edit XSLT's on the fly using XSLT-objects. But I am fine using String and/or XmlDocument.

(Sometimes I can not neglect the feeling that Microsoft itself has lost its way in the multitude of dot.net objects.)

Anyway I asked the question before I decided to stay away from XslCompiledTransform as long as possible. I am still interested in the issue, although it does not block the progress of my projects.

Author

Commented:
Thanks for explaining.
Top Expert 2009

Commented:
Thanks for the explanation, that puts this in the right perspective :)

If this eases your mind: you're on the right track. The way to deal with changes in the XSLT has traditionally evolved around either applying XSLT on XSLT (auto generation, large changes, functional programming) and DOM on XSLT. Changing the xml through string manipulation is something you should stay far away from though...

Having XslCompiledTransform as late as possible is good. However, this is a very expensive step. If you know that between calls the XML of the XSLT does not change, you should try to reuse the XslCompiledTransform objects.

There are several ways of late-applying certain settings as you have seen here. If performance is important, you can apply them to your code to prevent having to create a new compiled transform each time.

Indeed: Microsoft sometimes seems lost in the woods. In this case, they deliberately made trade-offs in favor of performance. I believe that was done for the need of XSLT in many of their enterprise solutions like BizTalk, which rely heavily on XSLT.

-- Abel --
Top Expert 2009

Commented:
and thanks for the grade + points, I missed it while typing :)

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial