Microsoft XML DOM output has no CR/LF

I am using the MS XML DOM to create a document, and am then saving it with a simple method to an XML file on disk. When the XML file is looked at in a text editor, I want to see something neat like this:


Instead, what the MS DOM is saving is this:


Everything is output on a single line with no CR/LF anywhere, making it totally unreadable - especially on a 20K XML file, where everything is crammed on a single line.

I've searched for a parameter to change this behavior, with no luck. Anyone have an idea?
Who is Participating?
Hi, this is what I use.

Btw, you should always aim at an A grade, and a "Points for ..." question resulting in a B is not quite common :-)


Public Function FormatXML(ByRef objNode As MSXML2.IXMLDOMNode, _
Optional ByVal intTabLevel As Integer = 0, _
Optional ByVal strLineBreakChars As String = vbCrLf) As String
    On Error GoTo ErrHandler
    Dim blnMixedTextNode As Boolean
    Dim blnHasATextNodeOnly As Boolean
    Dim i As Integer

    With objNode
        Select Case .nodeType
            ' all child nodes of the document should be at the same indent Level
            ' just iterate over them and recurse with 0 indent
            For i = 0 To .childNodes.length - 1
                FormatXML = FormatXML & FormatXML(.childNodes(i))
            Next i
        Case NODE_TEXT
            ' should render the same way the default IE5 stylesheet does for mixed content
            ' figure out if we're in some mixed content
            blnMixedTextNode = (.parentNode.childNodes.length > 1)  'if this text node has any siblings it's in mixed content

            ' if mixed indent this string
            If blnMixedTextNode Then FormatXML = String(intTabLevel, vbTab)
            ' we're gonna strip out any tabs and carriage returns from the Text
            FormatXML = FormatXML & Trim(Replace(Replace(.xml, strLineBreakChars, " "), vbTab, " "))
            ' if mixed add cariage return
            If blnMixedTextNode Then FormatXML = FormatXML & strLineBreakChars

        Case NODE_ELEMENT
            If .hasChildNodes Then
                ' if the node has only one child and that child is text we won't add carriage return after opening tag
                blnHasATextNodeOnly = (.childNodes(0).nodeType = NODE_TEXT) And (.childNodes.length = 1)
            End If
            ' open the start tag
            FormatXML = String(intTabLevel, vbTab) & "<" & .nodeName

            ' recurse over the attributes
            For i = 0 To .Attributes.length - 1
                FormatXML = FormatXML + FormatXML(.Attributes(i))
            Next i

            ' properly close the start tag based on node's contents
            If Not .hasChildNodes Then       ' no child nodes so it's an empty element
                FormatXML = FormatXML & "/>" & strLineBreakChars
                If blnHasATextNodeOnly Then    ' has only text for children - don't add carriage return
                    FormatXML = FormatXML & ">"
                Else                            ' has child elements - add carriage return
                    FormatXML = FormatXML & ">" & strLineBreakChars
                End If
                ' recurse if there's children
                For i = 0 To .childNodes.length - 1
                    FormatXML = FormatXML & FormatXML(.childNodes(i), intTabLevel + 1)
                Next i
                ' properly indent and add the end tag
                If Not blnHasATextNodeOnly Then FormatXML = FormatXML & String(intTabLevel, vbTab)
                FormatXML = FormatXML & "</" & .nodeName & ">" & strLineBreakChars
            End If
            ' if comment is on more than one line don't indent
            If InStr(1, .xml, vbCr) = 0 Then FormatXML = String(intTabLevel, vbTab)
            FormatXML = FormatXML & .xml & strLineBreakChars
            ' if there are double quotes in the attribute use single quotes to surrond the attr value
            If InStr(1, .Text, Chr(34)) > 0 Then
                FormatXML = " " & .nodeName & "='" & .Text & "'"
                FormatXML = " " & .nodeName & "=" & Chr(34) & .Text & Chr(34)
            End If
        Case NODE_ENTITY
            ' and we would never want entites expanded
        Case Else
            ' all other node types should just return their xml (properly indented)
            ' these include - entity refs, pi's, notations, doctypes
            FormatXML = String(intTabLevel, vbTab) & .xml & strLineBreakChars
        End Select
    End With
    ' the msxml parser is using vbCrLf as line separator...
    If strLineBreakChars <> vbCrLf Then
        FormatXML = Replace(FormatXML, vbCrLf, strLineBreakChars)
    End If
    Exit Function
    Err.Raise Err.Number, Err.Source, Err.Description
End Function
load the file into CUESoft's Exml Editor (freeware) - it will structure the file in the "source view".

you can get it at:

Hope this helps

ScottNeroAuthor Commented:
That's great for viewing it - I can do the same thing by opening it in IE. But what I need to do is to have the MS DOM write it out in a readable format in the first place.
Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

we're using version 3 of the parser and we don't have that problem ....
xmldoc.preserveWhiteSpace = True

I think you need to set it to true right after creating the object but before adding anything
ScottNeroAuthor Commented:
No, I tried using the preserveWhiteSpace parameter, with no effect. That parameter is used to preserve white space inside the tags, I'm looking to create white space between different tags.

Here is some VB code to demonstrate what is happening. I have referenced MSXML3.DLL in the project.

  Dim XMLdoc As New MSXML2.DOMDocument30
  Dim root As IXMLDOMElement
  Dim newNode As IXMLDOMNode
  Dim Config As IXMLDOMNode
  Dim docFragment As IXMLDOMDocumentFragment
  Dim CfgVal As IXMLDOMElement
  Set root = XMLdoc.documentElement
  Set newNode = XMLdoc.createNode("processinginstruction", "xml", "")
  Set Config = XMLdoc.appendChild(newNode)

  XMLdoc.appendChild XMLdoc.createElement("Main")
  Set Config = XMLdoc.createNode(NODE_ELEMENT, "Node1", "")
  XMLdoc.lastChild.appendChild Config

  Set docFragment = XMLdoc.createDocumentFragment
  Set CfgVal = XMLdoc.createElement("Element1")
  CfgVal.Text = "This is element 1"
  docFragment.appendChild CfgVal
  Set CfgVal = XMLdoc.createElement("Element2")
  CfgVal.Text = "This is element 2"
  docFragment.appendChild CfgVal
  Config.appendChild docFragment "c:\temp\test.xml"

The XML file created by this code looks like the following. Everything except the version is crammed onto one line:

<?xml version="1.0"?>
<Main><Node1><Element1>This is element 1</Element1><Element2>This is element 2</Element2></Node1></Main>

Put a line

  <xsl:output method="xml" indent="yes" />

or in your case it might be

  <xsl:output method="html" indent="yes" />
Just after the namespace declarations.
Sorry, didnt read the question properly - that only works when you use XSL to create the document (since it's an XSL command!)
I think then you'll have to write a function that writes the DOM object to a textfile trough the filesystem object.


does my comment correspond to your question ?

ScottNeroAuthor Commented:
Your idea, and the code above basically circumvents the Microsoft DOM "save" method and does it itself, iterating through the tree and writing it out manually. I could do that, but I was kind of hoping for a kind of "xmldoc.crlf = True" thing. All of the MS examples show the code written out as formatted and indented nicely, so I'm wondering if there is still something I'm missing.
> All of the MS examples show the code written out as formatted and indented nicely, so I'm wondering if there is still something I'm missing.

Sure. You're missing that MSXML doesn't attempt to make the XML it produces readable. You need to make it human-readable, yourself. And please, give an A next time.
It's time to clean up this topic area and that means taking care of this question. Your options at this point are:

1. Award points to the Expert who provided an answer, or who helped you most. Do this by clicking on the "Accept Comment as Answer" button that lies above and to the right of the appropriate expert's name.

2. PAQ the question because the information might be useful to others, but was not useful to you. To use this option, you must state why the question is no longer useful to you, and the experts need to let me know if they feel that you're being unfair.

3.  Ask Community Support to help split points between participating experts.  Just comment here with details.

4.  Delete the question because it is of no value to you or to anyone else.  To use this option, you must state why the question is no longer useful to you, and the experts need to let me know if they feel that you're being unfair.

If you elect for option 2, 3 or 4, just post comment with details here and I'll take it from there.  We also request that you review any other open questions you might have and update/close them.  Display all your question history from your Member Profile to view details.

---------->  Hi Experts:

In the event that the Asker does not respond, I would very much appreciate your opinions as to which Expert ought to receive points (if any) as a result of this question.  Likewise, you can also suggest that I PAQ or delete the question.

------------->  The ALL TOPICS link has been updated today to reflect all the new TAs.

Thank you everyone.

Moondancer :)
Community Support Moderator @ Experts Exchange

award points to robbert and grade A

any objection should be raised within 7 days


ScottNeroAuthor Commented:
Not quite what I was looking for, but the end result is the same.
thank you for finalising the question, Scott =)
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.