"Gordon Moore" <go***********@operamail.com> wrote in message news:eb**************************@posting.google.c om...
I'm new to using xml/xslt and although I can create an xml document
using the dataset.WriteXml statement, and I have created an xslt to
transform the xml into the output I want, I have to manually add the
: : <?xml:stylesheet type="text/xsl" href="Questions.xsl"?>
I think the processing instruction you're meaning to write is xml-stylesheet
(with a dash, not xml:stylesheet with a colon), per the recommendation
described here,
http://www.w3.org/TR/xml-stylesheet/
Back to answering your original question, yes, it is possible to get WriteXml( )
to emit this processing instruction automatically. The design necessary involves
intercepting calls to the XmlWriter (or TextWriter, or Stream) and injecting the
processing instruction immediately after the XML declaration (alternately, before
the document element). This interception technique is applicable to any of the
TextWriter or a Stream overloads for WriteXml( ), but to keep this all in the
XML family, let's use the XmlWriter overload, OK? :-)
At first, I wasn't sure which overload of WriteXml( ) you were using since you
were getting an XML declaration with a standalone attribute. I've since learned
that WriteXml( ) has a quirk in it, and I now think I know which overload you're
using. Needless to say, I'll demonstrate how to easily overcome this quirk, so
you can use the XmlWriter solution as well.
First, let's create an XmlTextWriter subclass that you can include in your project.
It's a relatively simple matter to wrap a TextWriter around a FileStream using the
string filename I think you were using in the call to WriteXml( ). I'll explain how
this all works together momentarily, but here's the code:
- - - MagnumWriter.cs
using System;
using System.IO;
using System.Xml;
/// <summary>
/// Magnum Writer injects an xml-stylesheet processing instruction (PI) into the
/// XML serialization from any method that accepts an XmlWriter argument.
/// </summary>
public class MagnumWriter : XmlTextWriter
{
private string xslStylesheetFilename;
private bool xmlDeclWritten;
public MagnumWriter( TextWriter writer, string stylesheetFilename) : base( writer)
{
xslStylesheetFilename = stylesheetFilename;
xmlDeclWritten = false;
}
public override void WriteStartDocument( bool standalone)
{
xmlDeclWritten = true;
base.WriteStartDocument( standalone);
base.WriteProcessingInstruction( "xml-stylesheet", String.Format( "type=\"text/xsl\" href=\"{0}\"",
xslStylesheetFilename) );
}
public override void WriteStartElement( string prefix, string localName, string nsUri)
{
if ( !xmlDeclWritten )
this.WriteStartDocument( true);
base.WriteStartElement( prefix, localName, nsUri);
}
}
- - -
The constructor creates a MagnumWriter, which is really just an XmlTextWriter that
I've overriden a few methods on. In order to create an XmlTextWriter that will send
XML content someplace, I need my constructor to at least support a parameter that
the XmlTextWriter's constructor can accept. In this case, that's the writer argument,
which gets passed to my base( ) (MyBase( writer) for those following along in VB.NET)
and does all the initializations underneath in the XmlTextWriter base class I've derived
from. Additionally, since MagnumWriter is "adding value," it needs to accept the
file name or URL you want to have appear in the href pseudo attribute, so that's the
second argument to it's constructor which gets saved into the xslStylesheetFilename
field for when I write the processing instruction later.
Next is WriteStartDocument( ). Normally when there are a number of overloads and
you're asking yourself which one should you override to get the most bang-for-the-buck,
the answer will be the overload with the most parameters. WriteStartDocument( ) would
just call the more specific WriteStartDocument( bool) anyway, so I only have to override
it once and not override both of them.
WriteStartDocument( ) is normally the place you want to be in most XmlWriter applications
that need supplementary processing instructions added up in the document's prolog, although
processing instructions may also appear anyplace within the document as well (they aren't
restricted to preceding the start element, although in xml-stylesheet's case it does).
As I've implemented WriteStartDocument( ), first I call the base class' WriteStartDocument( )
so it can go ahead with writing the XML declaration, and then I call the WriteProcessing-
Instruction( ) to immediately succeed it with our xml-stylesheet PI.
This might ordinarily be enough, but there's a quirk in some of the WriteXml( ) methods of
DataSet by which they won't necessarily call WriteStartDocument( ). It only writes out the
XML declaration (calling WriteStartDocument( )) when it knows it's writing out an entire
document: start, middle and end. This is the case with the WriteXml( ) overload taking a
string filename, but is not presumed with many of the other overloads. This is probably to
support writing multiple data sets out within the context of a larger containing document
element -- but for this solution it's a quirk that I'll show you how to overcome.
The solution is where the xmlDeclWritten field comes into the picture. Whenever a call
is made to WriteStartDocument( ), this flag is raised and remains raised for the duration.
If the DataSet::WriteXml( ) instead calls WriteStartElement( ) first, instead of WriteStart-
Document( ), meaning that we're going ahead without an XML declaration, then what
I've implemented for WriteStartElement( ) checks the xmlDeclWritten flag to see if it
hasn't been raised yet, and if it hasn't I call WriteStartDocument( ) to raise it (and thereby
inject an XML declaration and the xml-stylesheet PI into the stream).
WriteStartElement( ) then does what any good intercepter method should do, it calls
it's base method implementation as if it weren't even there.
I know any client that produces XML through an XmlWriter will use either WriteStart-
Document( ) or WriteStartElement( ) first, so this ensures that whichever path the code
takes you have an XML declaration and PI emitted when you pipe it through Magnum-
Writer.
- - - InjectingProcInstDemo.cs (except)
using System;
using System.Data;
using System.IO;
using System.Xml;
// . . .
TextWriter streamToFile = new StreamWriter( xmlFilename);
MagnumWriter magnumPI = new MagnumWriter( streamToFile, "Questions.xsl");
magnumPI.Formatting = Formatting.Indented; // optionally set indented formatting of output here..
dataSet1.WriteXml( magnumPI);
magnumPI.Flush( );
magnumPI.Close( ); // close underlying file stream, forgetting this may leave exclusive locks on the file..
// . . .
- - -
This final piece of code demonstrates, for completeness, how you can use a TextWriter
instead of a file name, so that you can call the overload of WriteXml( ) on your DataSet
that accepts XmlWriter-subclasses like the MagnumWriter.
Derek Harmon