// Use XmlBuilder.nui // cXmlBuilder class // Part of VDFQuery by Sture ApS //> pkgdoc.begin //> The XmlBuilder.nui packages defines three classes for facilitating the building of XML documents: //> //> 1. The cXmlBuilder class for the actual building //> 2. The cXmlSequence class that is capable of defining a sequence of XML for adding to a document multiple times //> 3. The cXmlParamArray class that defines a set of parameters for use with an object of the cXmlSequence class. //> //> The cXmlBuilder class may be used for adding elements to an XML DOM tree in a way that relieves //> you of having to deal with a myriad of handle type variables in your code. This is not to mention //> the house keeping of destroying all the element objects again. //> //> As an alternative it may be used to write directly to a sequential file without building //> a DOM object in memory. It is a question of which method you use for initiating the //> XML builing (either use begin_xml_add_to_dom or begin_xml_write_seq) //> //> //> If used in the sequential file output mode, all output is written by the write method. I use this //> when I write XTHML directly from within a WebApp application because I can augment just one //> method and have it write to the WebApp output stream.. //> //> //> This piece of code: //> //> use Files.nui // Utilities for handling file related stuff (No User Interface) //> object oTestBuilder is a cXmlBuilder //> procedure DoTest //> integer liChannel //> get SEQ_DirectOutput "test.xml" to liChannel //> if (liChannel>=0) begin //> // Manually write this line: //> write channel liChannel '' //> send begin_xml_write_seq liChannel // //> send add_open_element "response" //> send add_attribute "xmlns:service" "http://www.sture.dk/2004/service/" //> send add_open_element "function" //> send add_attribute "name" "login" //> send add_open_element "result" //> send add_open_element "fieldset" //> send add_closed_element "legend" "Login" //> send add_closed_element "span" "UserName" //> send add_open_element "span" //> send add_closed_element "input" "" //> send add_attribute "type" "text" //> send close_element // "span" //> send add_closed_element "span" "Password" //> send add_open_element "span" //> send add_closed_element "input" "" //> send add_attribute "type" "password" //> send close_element // span //> send close_element // fieldset //> send close_element // result //> send close_element // function //> send close_element // response //> send end_xml //> send SEQ_CloseOutput liChannel //> end //> end_procedure //> send DoTest //> end_object //> //> //> will write this to a file called test.xml: //> //> //> //> //> //> //>
//> Login //> Username //> //> Password //> //>
> //>
//>
//>
//>
//> //> (line feeds and indentation has been added for readability) //> //> //> This class is not able to handle mixed-content elements except by the use of the //> pbProtectValueData property. //> //> //> pkgdoc.end //> //> And here it comes: Use Base.nui // Item_Property command, Various macros (FOR_EX...), cArray, cSet and cStack classes (No User Interface) Use Dates.nui // Date routines (No User Interface) Use FleXML.pkg Use ExtendedItemProp.nui // cExtendedItemPropertyArray class class cXmlBuilder is a cStack procedure construct_object forward send construct_object property integer priv.pbWriteModeSeq false property integer priv.piChannel false property handle priv.phCurrentNode 0 property string priv.psWriteBuffer "" property integer priv.pbOpen false property integer priv.pbCData false property integer priv.ClosedElement false property integer priv.pbWebAppStream false //> This property has been introduced because the textarea element in (X)HTML is not allowed to be closed //> in this way "". property integer pbTextAreaException false //> This property has been introduced because the script element in (X)HTML is not allowed to be closed //> in this way "". IE 6 problem property integer pbScriptException false //> The pbProtectValueData property controls whether <, > and & characters written to the XML as data //> is HTML encoded ("<", ">" and "&") or not. Its default value is to do this encoding (true). //> The reason for (temporarily) setting it to false when generating XHTML code is making it able to handle //> when the data itself represents XHTML code. In this case it is not OK to encode these characters. property integer pbProtectValueData true object oArray is a cArray // This array is used when building XML in a string end_object end_procedure //> If object is initiated using the begin_xml_write_seq method all output will be sent to this procedure. procedure write string lsValue integer liChannel lhArr get priv.piChannel to liChannel if (liChannel>=0) write channel (priv.piChannel(self)) lsValue else begin if (priv.pbWebAppStream(self)) begin #IFDEF IS$WebApp send OutputString To ghInetSession lsValue #ENDIF end else begin move (oArray(self)) to lhArr set value of lhArr item (item_count(lhArr)) to lsValue end end end_procedure procedure flush_buffer // Private handle hoNode integer lbCDATA lbException string lsBuffer lsValue lsElement if (priv.pbWriteModeSeq(self)) begin get priv.psWriteBuffer to lsBuffer if (lsBuffer<>"") begin // If there's anything in the buffer if (priv.pbOpen(self)) send write (lsBuffer+">") // If we're writing an opening tag else begin // We're dealing with writing a data element (including a closing tag) get iPop to lbCDATA get sPop to lsElement get sPop to lsValue if (lsValue="" and ((lowercase(lsElement)<>"textarea" or pbTextAreaException(self)=0) and (lowercase(lsElement)<>"script" or pbScriptException(self)=0) )) send write (lsBuffer+" />") // If no value, we're writing the shorthand open-close tag. else begin if lbCDATA begin send write (lsBuffer+">") end else begin if (pbProtectValueData(self)) begin move (replaces("&",lsValue,"&")) to lsValue move (replaces("<",lsValue,"<")) to lsValue move (replaces(">",lsValue,">")) to lsValue end send write (lsBuffer+">"+lsValue+"") end end end set priv.psWriteBuffer to "" set priv.pbCData to false end end else begin if (priv.ClosedElement(self)) begin send destroy of (priv.phCurrentNode(self)) set priv.phCurrentNode to (iPop(self)) set priv.ClosedElement to false end end end_procedure procedure stream string lsValue if (priv.pbWriteModeSeq(self)) begin send flush_buffer send write lsValue end end_procedure //> Adds an element that contains other elements procedure add_open_element string lsElement handle hoParentNode lhChildNode if (priv.pbWriteModeSeq(self)) begin send flush_buffer set priv.psWriteBuffer to ("<"+lsElement) send push.s lsElement set priv.pbOpen to true end else begin send flush_buffer get priv.phCurrentNode to hoParentNode // get AddElementNS of hoParentNode "" lsElement "" to lhChildNode get AddElement of hoParentNode lsElement "" to lhChildNode send push.i hoParentNode set priv.phCurrentNode to lhChildNode end end_procedure //> Adds a data element (an element with no children) procedure add_closed_element string lsElement string lsValue handle hoParentNode lhChildNode if (priv.pbWriteModeSeq(self)) begin send flush_buffer set priv.psWriteBuffer to ("<"+lsElement) send push.s lsValue send push.s lsElement send push.i 0 // not cdata set priv.pbOpen to false end else begin send flush_buffer get priv.phCurrentNode to hoParentNode // get AddElementNS of hoParentNode "" lsElement "" to lhChildNode get AddElement of hoParentNode lsElement lsValue to lhChildNode send push.i hoParentNode set priv.phCurrentNode to lhChildNode set priv.ClosedElement to true end end_procedure //> Same as add_closed_element but marks up the data in lsValue as CDATA. procedure add_closed_element_cdata string lsElement string lsValue handle hoParentNode lhChildNode if (priv.pbWriteModeSeq(self)) begin send flush_buffer set priv.psWriteBuffer to ("<"+lsElement) send push.s lsValue send push.s lsElement send push.i 1 // cdata set priv.pbOpen to false end else begin send flush_buffer get priv.phCurrentNode to hoParentNode get AddElement of hoParentNode lsElement "" to lhChildNode send AddCDataSection of lhChildNode lsValue send push.i hoParentNode set priv.phCurrentNode to lhChildNode set priv.ClosedElement to true end end_procedure //> Close an element previously added by the add_open_element method procedure close_element // Public string lsElement if (priv.pbWriteModeSeq(self)) begin send flush_buffer get sPop to lsElement send write ("") end else begin send flush_buffer send destroy of (priv.phCurrentNode(self)) set priv.phCurrentNode to (iPop(self)) end end_procedure //> Add an attribute to the element that was most recently added (whether it's an open or a closed element) procedure add_attribute string lsAttr string lsValue string lsBuffer if (priv.pbWriteModeSeq(self)) begin get priv.psWriteBuffer to lsBuffer set priv.psWriteBuffer to (lsBuffer+' '+lsAttr+'="'+lsValue+'"') end else begin // send AddAttributeNS of (priv.phCurrentNode(self)) "" lsAttr lsValue send AddAttribute of (priv.phCurrentNode(self)) lsAttr lsValue end end_procedure //> Send this to initiate the process of writing XML to the sequential channel procedure begin_xml_write_seq integer liChannel send delete_data // clear the stack set priv.pbOpen to false set priv.pbWriteModeSeq to true set priv.piChannel to liChannel set priv.phCurrentNode to -1 set priv.psWriteBuffer to "" set pbProtectValueData to true end_procedure //> procedure begin_xml_write_seq_custom send delete_data // clear the stack set priv.pbOpen to false set priv.pbWriteModeSeq to true set priv.piChannel to -1 set priv.phCurrentNode to -1 set priv.psWriteBuffer to "" set pbProtectValueData to true set priv.pbWebAppStream to false end_procedure // Only use this from within an webapp procedure begin_xml_write_webapp_stream send begin_xml_write_seq_custom set priv.pbWebAppStream to true #IFNDEF IS$WebApp error 323 "Must be a WebApp (XmlBuilder.pkg)" #ENDIF end_procedure //> Send this to initiate the process of adding to XML DOM node procedure begin_xml_add_to_dom integer hoNode send delete_data // clear the stack set priv.pbOpen to false set priv.pbWriteModeSeq to false set priv.piChannel to -1 set priv.phCurrentNode to hoNode set priv.psWriteBuffer to "" set priv.ClosedElement to false set pbProtectValueData to true end_procedure //> If you want the XML to be returned as a string you should use this //> method to initate the process. procedure begin_xml_build_string_value send delete_data of (oArray(self)) send begin_xml_write_seq_custom end_procedure //> Send this to conclude the process of building the XML sequence. procedure end_xml send flush_buffer end_procedure //> If theXML building was initiated by the begin_xml_build_string_value //> message you can use this function to retrieve the value. Calling this //> function resets the content of the internal array. function sXmlValue returns string integer lhArr liMax liItm string lsValue move "" to lsValue move (oArray(self)) to lhArr get item_count of lhArr to liMax decrement liMax for liItm from 0 to liMax move (lsValue+value(lhArr,liItm)) to lsValue loop send delete_data of lhArr // You can only get its value once! function_return lsValue end_function //> Same as add_closed_element but formats the number with "." as decimal separator procedure add_closed_element_number string lsElement number lnValue // Public string lsValue move lnValue to lsValue move (replace(",",lsValue,".")) to lsValue send add_closed_element lsValue end_procedure //> Same as add_closed_element but formats the date according to ISO 8601 (YYYY-MM-DD) procedure add_closed_element_date string lsElement date ldValue string lsValue get DateToStr ldValue DF_DATE_MILITARY true "-" to lsValue // International Standard ISO 8601 send add_closed_element lsValue end_procedure end_class // cXmlBuilder