Results 1 to 10 of 10

Thread: Indent XML (without recursion)

  1. #1

    Thread Starter
    No place like 127.0.0.1 eyeRmonkey's Avatar
    Join Date
    Jul 2005
    Location
    Blissful Oblivion
    Posts
    2,306

    Indent XML (without recursion)

    This module allows you to indent XML before saving it to a file without using VB string functions. It does the work with an extremely small amount of code and does it properly using an XSL transformation instead of a recursive function.

    This module is the result of this thread. I want to extend a huge "thank you" to MartinLiss for reasearching XSL transformations and solving this issue.

    It will transform your XML from this:
    Code:
    <Values><FirstName>Rod</FirstName><LastName>Stephenshgsgfs</LastName><Street>1234 Programmer Place</Street><City>Bugsville</City><State>CO</State><Zip>80276</Zip></Values>
    to this:

    Code:
    <Values>
    	<FirstName>Rod</FirstName>
    	<LastName>Stephenshgsgfs</LastName>
    	<Street>1234 Programmer Place</Street>
    	<City>Bugsville</City>
    	<State>CO</State>
    	<Zip>80276</Zip>
    </Values>

    Attached is the module and a small sample project.

    To use it in any project all you have to do is add a reference to any version of MSXML.dll to your poject then call IndentXML() and pass a DOMDocument (or DOMDocument26, DOMDocument30 or DOMDocument40) object to the sub. After that the .xml property of that DOMDocument will be tabbed properly.

    UPDATES:
    11/16/2005 - Added 2 optional arguments to the IndentXML subroutine:
    1) bUnindent - Boolean value telling wether or to unindent the XMLDOM object passed into oXMLDoc.
    2) bLeaveHeader - Removes a problems from the last version where a tag was automatically added to the top of the document. If you want this tag back, set this parameter to true.
    Attached Files Attached Files
    Last edited by eyeRmonkey; Nov 16th, 2005 at 07:17 PM.
    Visual Studio 2005 Professional Edition (.NET Framework 2.0)
    ~ VB .NET Links: Visual Basic 6 to .NET Function Equivalents (Thread) | Refactor! (White Paper) | Easy Control for Wizard Forms | Making A Proper UI For WinForms | Graphics & GDI+ Tutorial | Websites For Free Icons
    ~ QUOTE: Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning. -Rich Cook

    ~ eyeRmonkey.com

  2. #2

    Thread Starter
    No place like 127.0.0.1 eyeRmonkey's Avatar
    Join Date
    Jul 2005
    Location
    Blissful Oblivion
    Posts
    2,306

    Re: Indent XML (without recursion)

    Updates made to remove the major problem (the XML tag that was forced onto the first line) and added the ability to unindent the XML.
    Visual Studio 2005 Professional Edition (.NET Framework 2.0)
    ~ VB .NET Links: Visual Basic 6 to .NET Function Equivalents (Thread) | Refactor! (White Paper) | Easy Control for Wizard Forms | Making A Proper UI For WinForms | Graphics & GDI+ Tutorial | Websites For Free Icons
    ~ QUOTE: Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning. -Rich Cook

    ~ eyeRmonkey.com

  3. #3
    New Member
    Join Date
    Feb 2005
    Posts
    5

    Exclamation Re: Indent XML (without recursion)

    Your XSL based code works great, but there is one change I'm having some trouble implementing. What needs to change in the XSL file in order to not add an end tag? For example,

    I don't want this:
    <element attribute="value"/>

    to be changed to this:
    <element attribute="value">
    </element>

    I know it's just a matter of figuring out the XSL syntax, but I don't have time to learn XSL just to make this one change. Thanks for the help.

  4. #4

    Thread Starter
    No place like 127.0.0.1 eyeRmonkey's Avatar
    Join Date
    Jul 2005
    Location
    Blissful Oblivion
    Posts
    2,306

    Re: Indent XML (without recursion)

    I really can't answer your question for sure, but all I know is that <element attribute="value"/> isn't proper syntax in some XHTML browsers. I know this is XML and not broswer specific, but it might work is you do this instead:

    <element attribute="value" />

    Just a though. Maybe Marty could give his input on th XLS side of things.
    Visual Studio 2005 Professional Edition (.NET Framework 2.0)
    ~ VB .NET Links: Visual Basic 6 to .NET Function Equivalents (Thread) | Refactor! (White Paper) | Easy Control for Wizard Forms | Making A Proper UI For WinForms | Graphics & GDI+ Tutorial | Websites For Free Icons
    ~ QUOTE: Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning. -Rich Cook

    ~ eyeRmonkey.com

  5. #5
    Addicted Member adamm83's Avatar
    Join Date
    Oct 2005
    Location
    Toronto, ON, Canada
    Posts
    180

    Re: Indent XML (without recursion)

    I'm just curious. Your code reads the XML and formats it properly then prints it into a textbox, but how can i get it to write back to the XML file properly formatted?
    Would I have to add code like the following after it runs the IndentXML function:
    VB Code:
    1. Open App.Path & "\my.xml" For Output As #1
    2.                 Print #1, oXMLDoc.xml
    3.             Close #1
    Or is there another way to do it?

    !!! EDIT !!!:
    Nevermind, I figured it out. Just do this after the IndentXML function is called, or whenever you want to save the xml to a file:
    VB Code:
    1. oXMLDoc.save App.Path & "\my.xml"



    !!! EDIT !!! :
    I also want to make a suggestion to add to the code (or rather to change something in the code). In the IndentXML function, where it removes the header, it did not work properly for me. I am using MSXML 6.0 and the header that gets printed is only <?xml version="1.0"?>, so what I did is this:

    original code:
    VB Code:
    1. ' Get rid of the added header line
    2.             If Not bLeaveHeader Then
    3.                 sResult = Replace$(sResult, "<?xml version=" & QT & "1.0" & QT & " encoding=" & QT & "UTF-16" & QT & "?>", vbNullString, , , vbTextCompare)
    4.             End If

    new code:
    Note: you will have to declare the following at the beginning of the function
    VB Code:
    1. Dim iStartHeader    As Integer
    2.     Dim iEndHeader      As Integer
    Replace the original code with the following:
    VB Code:
    1. ' Get rid of the added header line
    2.             iStartHeader = InStr(1, sResult, "<?xml")
    3.             If Not bLeaveHeader And iStartHeader > 0 Then
    4.                 iEndHeader = InStr(iStartHeader, sResult, "?>") + 2
    5.                 sResult = Replace$(sResult, Mid(sResult, iStartHeader, iEndHeader - iStartHeader), vbNullString, , , vbTextCompare)
    6.             End If

    This way, it searches for the beginning of the header and if that exists, it searches for the rest of the line and removes it.
    Last edited by adamm83; Dec 7th, 2006 at 04:32 PM.
    adamm
    ACM Designs

    Codebank:
    RegOpen - Open registry keys in Regedit quick & easily


    "A man who tries to catch two rabbits at the same time will catch neither."

  6. #6

    Thread Starter
    No place like 127.0.0.1 eyeRmonkey's Avatar
    Join Date
    Jul 2005
    Location
    Blissful Oblivion
    Posts
    2,306

    Re: Indent XML (without recursion)

    Good stuff, Adamm. I probably won't be modifying this code considering I am busy with many projects and college right now, but feel free to modify it as you see fit and upload a new zip and add your name to the credits. Just make sure you specify all the things that are different between the two versions.
    Visual Studio 2005 Professional Edition (.NET Framework 2.0)
    ~ VB .NET Links: Visual Basic 6 to .NET Function Equivalents (Thread) | Refactor! (White Paper) | Easy Control for Wizard Forms | Making A Proper UI For WinForms | Graphics & GDI+ Tutorial | Websites For Free Icons
    ~ QUOTE: Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning. -Rich Cook

    ~ eyeRmonkey.com

  7. #7
    New Member Jade Fox's Avatar
    Join Date
    Jan 2007
    Posts
    1

    Re: Indent XML (without recursion)

    Quote Originally Posted by takedownca
    Your XSL based code works great, but there is one change I'm having some trouble implementing. What needs to change in the XSL file in order to not add an end tag? For example,

    I don't want this:
    <element attribute="value"/>

    to be changed to this:
    <element attribute="value">
    </element>

    I know it's just a matter of figuring out the XSL syntax, but I don't have time to learn XSL just to make this one change. Thanks for the help.
    I changed the XSL_FILE to the following, and it appears to have solved the problem. I haven't debugged it, but it works for me. Not exactly sure why it works, though. My XPath and XSL is pretty rusty.

    VB Code:
    1. XSL_FILE = _
    2.             "<?xml version=" & QT & "1.0" & QT & " encoding=" & QT & "UTF-8" & QT & "?>" & vbCrLf & _
    3.             "<xsl:stylesheet version=" & QT & "1.0" & QT & " xmlns:xsl=" & QT & "http://www.w3.org/1999/XSL/Transform" & QT & ">" & vbCrLf & _
    4.             "     <xsl:output method=" & QT & "xml" & QT & " version=" & QT & "1.0" & QT & " encoding=" & QT & "UTF-8" & QT & " indent=" & QT & sIndent & QT & "/>" & vbCrLf & _
    5.             "     <xsl:template match=" & QT & "@* | node()[count(*)>0]" & QT & ">" & vbCrLf & _
    6.             "          <xsl:copy>" & vbCrLf & _
    7.             "               <xsl:apply-templates select=" & QT & "@* | node()" & QT & " />" & vbCrLf & _
    8.             "          </xsl:copy>" & vbCrLf & _
    9.             "     </xsl:template>" & vbCrLf & _
    10.             "     <xsl:template match=" & QT & "node()[count(*)=0]" & QT & ">" & vbCrLf & _
    11.             "          <xsl:copy>" & vbCrLf & _
    12.             "               <xsl:apply-templates select=" & QT & "@*" & QT & " />" & vbCrLf & _
    13.             "          </xsl:copy>" & vbCrLf & _
    14.             "     </xsl:template>" & vbCrLf & _
    15.             "</xsl:stylesheet>"

  8. #8
    New Member
    Join Date
    Aug 2013
    Posts
    2

    Re: Indent XML (without recursion)

    Great, very useful, many thanks.

    Simply changed DOMDocument to DOMDocument60 and it works with Microsoft XML v6.0.

  9. #9
    PowerPoster dilettante's Avatar
    Join Date
    Feb 2006
    Posts
    24,487

    Re: Indent XML (without recursion)

    While I can understand not wanting to roll your own code to accomplish this from dirt (i.e. String operations in VB code), this approach is probably worse than you could do in pure VB6. XSL is notoriously slow and the XSLT engine itself is tremendously bulky. Try testing with a 750KB XML document!


    An obvious alternative if you are already loading an MSXML library is to use the SAX2 objects. They can make this is trivial matter, and do the entire thing far faster using far less memory.

    Name:  sshot.png
Views: 3840
Size:  26.7 KB

    Here is 100% of the code in this demo, and you can basically ignore everything outside of cmdReformat_Click:

    Code:
    Option Explicit
    
    Private Sub ManageUI()
        'Make UI changes based on the relationships of controls and their
        'curent values.
        chkStandalone.Enabled = chkOmitXMLDeclaration.Value <> vbChecked
    End Sub
    
    Private Sub chkOmitXMLDeclaration_Click()
        ManageUI
    End Sub
    
    Private Sub cmdReformat_Click()
        Dim rdrCompact As MSXML2.SAXXMLReader
        Dim wrtFormatted As MSXML2.MXXMLWriter
    
        Set wrtFormatted = New MSXML2.MXXMLWriter
        With wrtFormatted
            .omitXMLDeclaration = chkOmitXMLDeclaration.Value = vbChecked
            .standalone = chkStandalone.Value = vbChecked
            .indent = chkIndent.Value = vbChecked
            .output = "" 'Tells MXXMLWriter we want Unicode String output.
            Set rdrCompact = New MSXML2.SAXXMLReader
            With rdrCompact
                Set .contentHandler = wrtFormatted
                Set .dtdHandler = wrtFormatted
                Set .errorHandler = wrtFormatted
                .putProperty "http://xml.org/sax/properties/lexical-handler", _
                             wrtFormatted
                .putProperty "http://xml.org/sax/properties/declaration-handler", _
                             wrtFormatted
                .parse txtOriginal.Text
            End With
            txtPrettied.Text = .output
        End With
    End Sub
    
    Private Sub Form_Load()
        ManageUI
    End Sub
    Attached Files Attached Files
    Last edited by dilettante; Aug 31st, 2013 at 07:40 PM.

  10. #10
    New Member
    Join Date
    Aug 2013
    Posts
    2

    Re: Indent XML (without recursion)

    Good to know, thanks. I'll try to use your code instead. But in my applications the files are rarely larger than 10KB. And it's only temporary as I'm planning to start working in VSTO/C# soon.
    But anyway, always good to do things faster, thanks!


    Quote Originally Posted by dilettante View Post
    While I can understand not wanting to roll your own code to accomplish this from dirt (i.e. String operations in VB code), this approach is probably worse than you could do in pure VB6. XSL is notoriously slow and the XSLT engine itself is tremendously bulky. Try testing with a 750KB XML document!


    An obvious alternative if you are already loading an MSXML library is to use the SAX2 objects. They can make this is trivial matter, and do the entire thing far faster using far less memory.

    Name:  sshot.png
Views: 3840
Size:  26.7 KB

    Here is 100% of the code in this demo, and you can basically ignore everything outside of cmdReformat_Click:

    Code:
    Option Explicit
    
    Private Sub ManageUI()
        'Make UI changes based on the relationships of controls and their
        'curent values.
        chkStandalone.Enabled = chkOmitXMLDeclaration.Value <> vbChecked
    End Sub
    
    Private Sub chkOmitXMLDeclaration_Click()
        ManageUI
    End Sub
    
    Private Sub cmdReformat_Click()
        Dim rdrCompact As MSXML2.SAXXMLReader
        Dim wrtFormatted As MSXML2.MXXMLWriter
    
        Set wrtFormatted = New MSXML2.MXXMLWriter
        With wrtFormatted
            .omitXMLDeclaration = chkOmitXMLDeclaration.Value = vbChecked
            .standalone = chkStandalone.Value = vbChecked
            .indent = chkIndent.Value = vbChecked
            .output = "" 'Tells MXXMLWriter we want Unicode String output.
            Set rdrCompact = New MSXML2.SAXXMLReader
            With rdrCompact
                Set .contentHandler = wrtFormatted
                Set .dtdHandler = wrtFormatted
                Set .errorHandler = wrtFormatted
                .putProperty "http://xml.org/sax/properties/lexical-handler", _
                             wrtFormatted
                .putProperty "http://xml.org/sax/properties/declaration-handler", _
                             wrtFormatted
                .parse txtOriginal.Text
            End With
            txtPrettied.Text = .output
        End With
    End Sub
    
    Private Sub Form_Load()
        ManageUI
    End Sub

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width