VB.NET XMLWriter: How to change what's in the header?
Question
I have a requirement to make an XML file - and the partner is rather sticky about the header. Apparently, the header must be exactly this:
<?xml version="1.0"?>
But whenever I create an XML file I get extraneous properties like this:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
The hacker in me wants to stop using XMLWriter to make the file so that I have more control over the header; "no problem, I'll just write a loop that makes its own XML tags as a StreamWriter or something, forget this XMLWriter object..." but I must admit that XMLWriter has been rather elegant to use so far; surely there must be something where I can change the XMLWriterSettings object to say "stop putting your custom properties in to the XML header please", right?
Here's the relevant VB code:
Dim settings As New XmlWriterSettings()
settings.Indent = True
settings.IndentChars = " "
settings.NewLineChars = "\n"
Using writer As XmlWriter = XmlWriter.Create(strFileName, settings)
writer.WriteStartDocument(True)
For Each kvp As KeyValuePair(Of String, String) In dictArguments
Dim key As String = kvp.Key
Dim value As String = kvp.Value
writer.WriteStartElement(key)
writer.WriteString(value)
writer.WriteEndElement()
Next
End Using
Works perfectly; but I can't find a way to control the header. I can find a way to remove it entirely of course but that's not what we want to do.
Edit: thanks for the help; so far once we removed the WriteStartDocument it now no longer displays standalone = yes. I can't get it to stop adding the encoding however. Any ideas?
Solution
One way of doing this is to take control of the initial processing instruction yourself with the WriteProcessingInstruction method thus:
Dim settings As New XmlWriterSettings()
settings.Indent = True
settings.IndentChars = " "
Using writer As XmlWriter = XmlWriter.Create(strFileName, settings)
writer.WriteProcessingInstruction("xml", "version='1.0'")
writer.WriteStartElement("root")
For Each kvp As KeyValuePair(Of String, String) In dictArguments
Dim key As String = kvp.Key
Dim value As String = kvp.Value
writer.WriteStartElement(key)
writer.WriteString(value)
writer.WriteEndElement()
Next
writer.WriteEndElement()
End Using
Note that I've also added a "root" element in case your dictionary contains more than one element (and I'm guessing that none of the dictionary key values is "root" :)
OTHER TIPS
I know its been a few months since the question was asked, however I feel obliged to mention a (long-standing?) solution that I stumbled accross. It does do away with the entire xmldeclaration, and all you need to dois re-write just the declaration you need by writting a proccesing instruction.
XmlFragmentWriter - Omiting the Xml Declaration and the XSD and XSI namespaces
And here is the class in VB
Imports System.Xml
Imports System.IO
Imports System.Text
Class XmlFragmentWriter
Inherits XmlTextWriter
Public Sub New(ByVal w As TextWriter)
MyBase.New(w)
End Sub
Public Sub New(ByVal w As Stream, ByVal encoding As Encoding)
MyBase.New(w, encoding)
End Sub
Public Sub New(ByVal filename As String, ByVal encoding As Encoding)
MyBase.New(New FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None), encoding)
End Sub
Private _skip As Boolean = False
Public Overrides Sub WriteStartAttribute(ByVal prefix As String, ByVal localName As String, ByVal ns As String)
' STEP 1 - Omits XSD and XSI declarations.
' From Kzu - http://weblogs.asp.net/cazzu/archive/2004/01/23/62141.aspx
If prefix = "xmlns" AndAlso (localName = "xsd" OrElse localName = "xsi") Then
_skip = True
Return
End If
MyBase.WriteStartAttribute(prefix, localName, ns)
End Sub
Public Overrides Sub WriteString(ByVal text As String)
If _skip Then
Return
End If
MyBase.WriteString(text)
End Sub
Public Overrides Sub WriteEndAttribute()
If _skip Then
' Reset the flag, so we keep writing.
_skip = False
Return
End If
MyBase.WriteEndAttribute()
End Sub
Public Overrides Sub WriteStartDocument()
' STEP 2: Do nothing so we omit the xml declaration.
End Sub
End Class
and the usage here:
Dim f As New XmlSerializer(GetType(OFXg)) Dim w As New XmlFragmentWriter("c:\books1.xml", Nothing) w.Formatting = Formatting.Indented w.WriteProcessingInstruction("xml", "version=""1.0""") f.Serialize(w, RTofx) w.Close()
Of Course the OFXg class is an XMLSerializable
Why do you believe there are custom properties in the header?
WriteStartDocument writes the header with or without the standalone attribute. Your code adds the attribute you said your partner does not accept.
You did not show the code that used to produce the "utf-16", but I suspect it wrote to a StringWriter. Strings in .NET are always UNICODE, and you'll always get utf-16 when you write to a string. If you write to a stream, you get to control the encoding.
Necromancing.
You can create your own XmlTextWriter and override WriteStartDocument.
Example:
public class XmlTextWriterIndentedStandaloneNo : System.Xml.XmlTextWriter
{
public bool bStandAlone = false;
public bool bWriteStartDocument = true;
public bool bOmitEncodingAndStandAlone = true;
public XmlTextWriterIndentedStandaloneNo(System.IO.TextWriter w)
: base(w)
{
Formatting = System.Xml.Formatting.Indented;
} // End Constructor
public XmlTextWriterIndentedStandaloneNo(string strFileName, System.Text.Encoding teEncoding)
: base(strFileName, teEncoding)
{
Formatting = System.Xml.Formatting.Indented;
} // End Constructor
public XmlTextWriterIndentedStandaloneNo(System.IO.Stream w, System.Text.Encoding teEncoding)
: base(w, teEncoding)
{
Formatting = System.Xml.Formatting.Indented;
} // End Constructor
public override void WriteStartDocument(bool standalone)
{
if (bWriteStartDocument)
{
if (bOmitEncodingAndStandAlone)
{
this.WriteProcessingInstruction("xml", "version='1.0'");
return;
} // End if (bOmitEncodingAndStandAlone)
base.WriteStartDocument(bStandAlone);
}
} // End Sub WriteStartDocument
public override void WriteStartDocument()
{
// Suppress by ommitting WriteStartDocument
if (bWriteStartDocument)
{
if (bOmitEncodingAndStandAlone)
{
this.WriteProcessingInstruction("xml", "version='1.0'");
return;
} // End if (bOmitEncodingAndStandAlone)
base.WriteStartDocument(bStandAlone);
// False: Standalone="no"
} // End if (bWriteStartDocument)
} // End Sub WriteStartDocument
} // End Class XmlTextWriterIndentedStandaloneNo
VB.NET
Public Class XmlTextWriterIndentedStandaloneNo
Inherits System.Xml.XmlTextWriter
Public bStandAlone As Boolean = False
Public bWriteStartDocument As Boolean = True
Public bOmitEncodingAndStandAlone As Boolean = True
Public Sub New(w As System.IO.TextWriter)
MyBase.New(w)
Formatting = System.Xml.Formatting.Indented
End Sub
' End Constructor
Public Sub New(strFileName As String, teEncoding As System.Text.Encoding)
MyBase.New(strFileName, teEncoding)
Formatting = System.Xml.Formatting.Indented
End Sub
' End Constructor
Public Sub New(w As System.IO.Stream, teEncoding As System.Text.Encoding)
MyBase.New(w, teEncoding)
Formatting = System.Xml.Formatting.Indented
End Sub
' End Constructor
Public Overrides Sub WriteStartDocument(standalone As Boolean)
If bOmitEncodingAndStandAlone Then
Me.WriteProcessingInstruction("xml", "version='1.0'")
Return
End If
' End if (bOmitEncodingAndStandAlone)
If bWriteStartDocument Then
MyBase.WriteStartDocument(bStandAlone)
End If
End Sub
' End Sub WriteStartDocument
Public Overrides Sub WriteStartDocument()
If bOmitEncodingAndStandAlone Then
Me.WriteProcessingInstruction("xml", "version='1.0'")
Return
End If
' End if (bOmitEncodingAndStandAlone)
' Suppress by ommitting WriteStartDocument
If bWriteStartDocument Then
' False: Standalone="no"
MyBase.WriteStartDocument(bStandAlone)
End If
' End if (bWriteStartDocument)
End Sub
' End Sub WriteStartDocument
End Class
' End Class XmlTextWriterIndentedStandaloneNo
Usage example:
//using (System.Xml.XmlTextWriter wr = new System.Xml.XmlTextWriter(System.IO.Path.Combine(strBasePath, "TestFile.xml"), System.Text.Encoding.UTF8))
//using (System.Xml.XmlWriter wr = System.Xml.XmlWriter.Create(System.IO.Path.Combine(strBasePath, "TestFile.xml"), xwsSettings))
using (System.Xml.XmlWriter wr = new XmlTextWriterIndentedStandaloneNo(System.IO.Path.Combine(strBasePath, "TestFile.xml"), System.Text.Encoding.UTF8))
{
//wr.Formatting = System.Xml.Formatting.None; // here's the trick !
xdoc.Save(wr);
wr.Flush();
wr.Close();
} // End Using wr
This:
XmlWriterSettings xmlWriterSettings
= new XmlWriterSettings { OmitXmlDeclaration = true, };
will give you this:
<?xml version="1.0"?>