Save formatted XML
Posted: Mon Mar 16, 2009 8:06 pm
				
				Works also with PB 5.20
Hi all,
in the current PB version 4.30, FormatXML() does not work like several people expect, see e.g. this thread.
So here is an alternative way of saving formatted XML to a file.
The code of the prodedure SaveFormattedXml() was modified and improved by me after code by Thorsten.
SaveFormattedXml() preserves leading and trailing whitespace in the text of the XML nodes.
Enjoy!
Little John
//edit 2009-03-18:
- Added: Support for empty elements of the form <item/>.
- Added: Support for node type #PB_XML_Instruction.
- Changed: Simplified the code.
//edit 2009-08-09:
- Fixed: Leading or trailing whitespace is not removed from the text of the XML nodes anymore.
- Fixed: Code changed, so that it now also works as part of Unicode executables.
- Added: Improved error handling.
- Added: Option for saving the respective BOM to the output file.
- Changed: Demo code slightly altered.
The output is:
			Hi all,
in the current PB version 4.30, FormatXML() does not work like several people expect, see e.g. this thread.
So here is an alternative way of saving formatted XML to a file.
The code of the prodedure SaveFormattedXml() was modified and improved by me after code by Thorsten.
SaveFormattedXml() preserves leading and trailing whitespace in the text of the XML nodes.
Enjoy!
Little John
//edit 2009-03-18:
- Added: Support for empty elements of the form <item/>.
- Added: Support for node type #PB_XML_Instruction.
- Changed: Simplified the code.
//edit 2009-08-09:
- Fixed: Leading or trailing whitespace is not removed from the text of the XML nodes anymore.
- Fixed: Code changed, so that it now also works as part of Unicode executables.
- Added: Improved error handling.
- Added: Option for saving the respective BOM to the output file.
- Changed: Demo code slightly altered.
Code: Select all
Procedure.i SaveFormattedXml (xmlId, xmlFile$, flags=0, indentStep=3)
   Protected *buffer, encoding, size, ofn, Lpos, Rpos, indent=0
   Protected xml$, prevLeft$, prevRight$, txt$, curTag$
   ; Initialize
   If IsXML(xmlId) = 0
      ProcedureReturn 0                                                 ; error
   EndIf
   encoding = GetXMLEncoding(xmlId)
   size = ExportXMLSize(xmlId)
   *buffer = AllocateMemory(size)
   If *buffer = 0
      ProcedureReturn 0                                                 ; error
   EndIf
   If ExportXML(xmlId, *buffer, size) = 0
      FreeMemory(*buffer)
      ProcedureReturn 0                                                 ; error
   EndIf
   xml$ = PeekS(*buffer, -1, encoding)
   FreeMemory(*buffer)
   ofn = CreateFile(#PB_Any, xmlFile$)
   If ofn = 0
      ProcedureReturn 0                                                 ; error
   EndIf
   If flags & #PB_XML_StringFormat
      WriteStringFormat(ofn, encoding)
   EndIf
   ; Get and write XML declaration
   Lpos = FindString(xml$, "<", 1)
   Rpos = FindString(xml$, ">", Lpos) + 1
   curTag$ = Mid(xml$, Lpos, Rpos-Lpos)
   WriteString(ofn, curTag$, encoding)
   ; Get and write the other elements
   Lpos = FindString(xml$, "<", Rpos)
   While Lpos
      prevLeft$  = Left(curTag$, 2)
      prevRight$ = Right(curTag$, 2)
      txt$ = Mid(xml$, Rpos, Lpos-Rpos)
      If Mid(xml$, Lpos, 9) = "<![CDATA["
         Rpos = FindString(xml$, "]]>", Lpos) + 3
      Else
         Rpos = FindString(xml$, ">", Lpos) + 1
      EndIf
      curTag$ = Mid(xml$, Lpos, Rpos-Lpos)
      If FindString("</<!<?", prevLeft$, 1) = 0 And prevRight$ <> "/>"
         If Left(curTag$, 2) = "</"                                     ; <tag>text</tag>
            WriteString(ofn, txt$ + curTag$, encoding)
         Else                                                           ; <tag1><tag2>
            indent + indentStep
            WriteString(ofn, #LF$ + Space(indent) + curTag$, encoding)
         EndIf
      Else
         If Left(curTag$, 2) = "</"                                     ; </tag2>text</tag1>
            If Len(txt$)
               WriteString(ofn, #LF$ + Space(indent) + txt$, encoding)
            EndIf
            indent - indentStep
         EndIf
         WriteString(ofn, #LF$ + Space(indent) + curTag$, encoding)
      EndIf
      Lpos = FindString(xml$, "<", Rpos)
   Wend
   CloseFile(ofn)
   ProcedureReturn 1                                                    ; success
EndProcedureCode: Select all
;-- Demo
EnableExplicit
; XIncludeFile "XML.pbi"
XIncludeFile "neu.pbi"
Define Xml, Main, Item, SubItem
Xml = CreateXML(#PB_Any, #PB_UTF8)
Main = CreateXMLNode(RootXMLNode(Xml))
SetXMLNodeName(Main, "root")
SetXMLNodeText(Main, "XML zoo")
Item = CreateXMLNode(Main, -1, #PB_XML_CData)
SetXMLNodeText(Item, "Did you know: 5 > 3 ?")
Item = CreateXMLNode(Main)
SetXMLNodeName(Item, "item")
SetXMLAttribute(Item, "id", "1")
SetXMLNodeText(Item, "Cat")
Item = CreateXMLNode(Main)
SetXMLNodeName(Item, "item")
SetXMLAttribute(Item, "id", "3")
SetXMLNodeText(Item, "Bird")
SubItem = CreateXMLNode(Item)
SetXMLNodeName(SubItem, "subitem")
SetXMLAttribute(SubItem, "id", "4")
SetXMLNodeText(SubItem, "Cuckoo")
SubItem = CreateXMLNode(Item, -1, #PB_XML_Comment)
SetXMLNodeText(SubItem, " Lives in clocks in the Black Forest ")
Item = CreateXMLNode(Main)
SetXMLNodeName(Item, "item")
SetXMLAttribute(Item, "id", "5")
Item = CreateXMLNode(Main, -1, #PB_XML_Instruction)
SetXMLNodeName(Item, "php")
SetXMLNodeText(Item, "...")
Item = CreateXMLNode(Main)
SetXMLNodeName(Item, "item")
SetXMLAttribute(Item, "id", "6")
SetXMLNodeText(Item, " ")
If SaveFormattedXml(Xml, "animals.xml") = 0
   MessageRequester("Error", "Can't save XML file.")
EndIf
FreeXML(Xml)Code: Select all
<?xml version="1.0" encoding="UTF-8"?>
<root>
   <![CDATA[Did you know: 5 > 3 ?]]>
   <item id="1">Cat</item>
   <item id="3">
      <subitem id="4">Cuckoo</subitem>
      <!-- Lives in clocks in the Black Forest -->
      Bird
   </item>
   <item id="5"/>
   <?php ...?>
   <item id="6"> </item>
   XML zoo
</root>