views:

1431

answers:

3

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?

+1  A: 

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.

John Saunders
Ah, OK I get it... if I remove the WriteStartDocument the standalone does go away. Still not sure how to remove the UTF bits.
Kyle Hodgson
Use WriteStartDocument(). Are you still getting UTF-16 with the code you posted?
John Saunders
Yes, now that I removed the writer.WriteStartDocument(True) we are down do this for a header: <?xml version="1.0" encoding="utf-8"?>
Kyle Hodgson
And is the file UTF-8? if it is, then the consumer may be broken, and you may need to convince them that they should accept it as it is valid XML.
Rowland Shaw
I assume it is; yes. I understand what you are saying; but I'm not sure we are in a position to dictate what they consider valid XML. We're a .NET shop, they're on Informix. There may be some difference enforced on them due to their environment. If there is no way to do this with XMLWriter I'll just hack something together.
Kyle Hodgson
There is an international standard about what constitutes XML they should follow it.
John Saunders
+1  A: 

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" :)

Dan Blanchard
Excellent thanks!
Kyle Hodgson
+1  A: 

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

nepaluz