14 Replies Latest reply on Mar 5, 2008 1:02 PM by coffeedrinker56

    Reverse CFXML ?

    coffeedrinker56 Level 1
      I've built a client-driven RIA using CF7 as the intermediary to a SQL Server 2005 database.

      Data are passed as URL parameters in the existing architecture. A new requirement has surfaced that will require more data to be passed to and stored in the database; URL encoding is not practical for this.

      The volume of the data being passed is mutable, ranging from approximately 80 bytes [stored as one table row] up to 24K [stored as (roughly) 300 rows in two tables]. The obvious output choice is XML.

      I can pass the data to CF by storing the data as an XML file on the server, then reading it into a CF application using CFFILE. It would be cleaner if there's another mechanism available that could populate a CFXML variable directly, but my research doesn't reveal an obvious choice. [That research has extended through CF8: "Adobe ColdFusion 8 Application Development" Volume 2 by Ben Forta and Raymond Camden.]

      Did I miss something? Does anyone out there have an alternative method?

        • 1. Re: Reverse CFXML ?
          Level 7
          coffeedrinker56 wrote:
          > It would be cleaner if there's another mechanism available that could populate a CFXML
          > variable directly

          Directly from what source? There are quite a few ways to get XML into
          and out of ColdFusion. But I am unclear on where you want to directly
          'populate a CFXML variable' from?
          • 2. Re: Reverse CFXML ?
            coffeedrinker56 Level 1
            What I'd like to see is an extension of the CFXML tab - or of the CFFILE tag - that would allow a direct import of client-originated XML without having a two-step process.

            Right now the closest method I can use is...
            (1) Client reads form data and formats it as XML;
            (2) Client sends XML (via Ajax) to the server, storing the data as "abc.xml";
            (3) Client initiates a CF process that reads "abc.xml" using the CFFILE action="read", then validates that it is XML [IsXmlDoc function], and finally parses the XML data into a local variable [XmlParse function].

            The process would be much cleaner as:
            <cfxml variable="myXmlVar" caseSensitive="yes" port="80" action="read" />

            ... or as a modification to the cffile tag:

            <cffile action="read" port="80" variable="myXmlText" />
            <cfset myXML = XmlParse(myXmlText) />

            [I would think that the latter would be the easier option to implement.]


            • 3. Re: Reverse CFXML ?
              Level 7
              Am I understanding correctly that you would like a user to send XML
              formated data as a form post?

              If so, is there something wrong with this simple process?

              <cfset xmlObj = xmlParse(form.xmlField)>
              • 4. Re: Reverse CFXML ?
                Level 7
                or, if the OP would like a user to upload an xml file to the server
                (port='80' made me think so), can cfhttp be used to read an xml doc
                into a variable?

                Azadi Saryev
                Sabai-dee.com
                http://www.sabai-dee.com/
                • 5. Re: Reverse CFXML ?
                  Level 7
                  Azadi wrote:
                  > or, if the OP would like a user to upload an xml file to the server
                  > (port='80' made me think so), can cfhttp be used to read an xml doc
                  > into a variable?
                  >
                  > Azadi Saryev
                  > Sabai-dee.com
                  > http://www.sabai-dee.com/

                  <cfhttp...> can be used to read an XML file into a variable from a URL.
                  But if the file has been uploaded to the server, then <cffile...> is
                  the proper tag to read its contents and process into a variable.

                  I presumed the OP wanted to get XML data to the server directly from a
                  user without them uploading a file.

                  I'm just a bit unclear what "1) reads form data and formats is as XML"
                  and "2) sends XML to server" is meant to describe. Is this the desired
                  process, the current process or just the only solution yet figured out?


                  • 6. Re: Reverse CFXML ?
                    Level 7
                    right you are re cffile.
                    i am confused by the OP's requirements/procedural logic, too...

                    why like that, coffeedrinker?

                    Azadi Saryev
                    Sabai-dee.com
                    http://www.sabai-dee.com/
                    • 7. Re: Reverse CFXML ?
                      coffeedrinker56 Level 1
                      "why like that, coffeedrinker?"

                      Prior to this new requirement coming in, the quantity of data being sent to the server at any one time was very limited - typically 100 bytes or less - so adding the data to the URL and sending it as a 'GET' via the XmlHttpRequest object made sense - and it works very well.

                      The application currently exchanges data via this method with 41 CF scripts that generate XML and 11 XSL files to format. Two other XML files with corresponding XSL files are imported/formatted using DOMDocument techniques. [These last four files are static, but are formatted differently depending upon the privileges assigned to the users' accounts.]

                      ONE page-length form within the XHTML script is used to contain all of the data as-and-when-needed imported from the server.

                      It sounds worse than it is: only 27 form fields need to be maintained across all of the functionality. The DOM is manipulated to add/modify/remove other fields as needed.

                      One short section of JavaScript is a "black hole" intended to keep the form data from ever being submitted to the server. It's there specifically to keep the form from ever being "submitted" to the server and thereby requiring the page to be reloaded.

                      Yes, it would be easy to read the data using FORM.variable constructs. But re-coding 5,200 lines of JavaScript to incorporate this new requirement makes less sense than sending the XML data as a file and having CFFILE read it.

                      Currently to accomplish the new requirement the XML data is sent via XmlHttpRequest.send(); the name of the file - it's dynamically generated to keep the user from overwriting his/her own work - is sent as part of the URL.

                      To answer another questioner: CFHTTP can be used to read the XML file once it's on the server, but CFFILE reads the data as well. So there's no real advantage to using a server-side ajax function for this purpose.

                      [Maintainability is an issue. I'm a contractor. Some day (presumably) I'll take a position elsewhere, but I'm the only one here with Ajax experience. Other CF experienced people here understand CFFILE since they've used it for ages, but this is the first RIA at this site - and probably the first RIA within this organization.]

                      The methods I'm using work, but it would be a lot cleaner if the CF process could read the XML directly.

                      Herb Spencer
                      • 8. Re: Reverse CFXML ?
                        Level 7
                        coffeedrinker56 wrote:
                        > The methods I'm using work, but it would be a lot cleaner if the CF process
                        > could read the XML directly.
                        >
                        > Herb Spencer

                        I still do not completely understand your difficulty? I can use CF to
                        read XML seven ways from Sunday. Do you or do you not want to work with
                        a file sent as a multi-part request?

                        Using AJAX does not change the underlining HTTP request-response nature
                        of web applications, RIA or not. Whether the request comes from a user
                        action directly from the browser, or inside the browser using
                        JavaScript, it is still a request no different from any other request to
                        the server. This request can be a get or a post at your choice. You
                        are currently using it to post a multi-part request with a file. If you
                        don't want to use a file, just post a form instead.

                        Then on the server side, use the URL scope, the FORM scope or
                        <cffile...> as relevant to how you sent the data.

                        • 9. Re: Reverse CFXML ?
                          coffeedrinker56 Level 1
                          Ian:
                          Thanks for responding.
                          Neither reading nor writing XML is a problem at either end of the conversation.
                          Every one of the 41 CF scripts I mentioned earlier either reads or writes XML. [Most read/manipulate the XML data then send - at least - response codes, messages, etc back to the browser as XML.]

                          If possible, I'd like to skip the generate-file/read-file/delete-file sequence completely. But in this case the volume of data makes the data stream too clumsy when put into the url?p1=v1&p2=v2 ... format.

                          I can easily use a single JS variable ("myXML" in this case) to pass XML data like this:
                          var myURL = 'abc.cfm?xmlcontent=' + myXML;
                          ajax('GET', myURL, true);

                          ["ajax()" is a multi-browser compatible JS function that I wrote specifically to send/receive XML to the server.]

                          Then, of course, CF can read the XML data as a URL variable, eg:
                          <cfif IsDefined("URL.xmlcontent") >
                          <cfset inXML = XmlParse("URL.xmlcontent") />
                          </cfif>

                          Now imagine that the "myXML" variable has 300 child nodes, each of which can have two children containing up to 2K (each) of text plus a dozen siblings of comparatively inconsequential sizes. [The actual average size is approximately 60K, but 700K has happened.]

                          Storing the XML as a file and then reading it into CF using CFFILE results could result in 1.4 Mb conversation; reading it directly - if possible - cuts that conversation by half.

                          That's my goal. It would be the exact opposite of a cfhttp tag.

                          Thanks.
                          • 10. Re: Reverse CFXML ?
                            Level 7
                            coffeedrinker56 wrote:
                            > Ian:
                            > Thanks for responding.
                            > Neither reading nor writing XML is a problem at either end of the
                            > conversation.
                            > Every one of the 41 CF scripts I mentioned earlier either reads or writes
                            > XML. [Most read/manipulate the XML data then send - at least - response codes,
                            > messages, etc back to the browser as XML.]
                            >
                            > If possible, I'd like to skip the generate-file/read-file/delete-file
                            > sequence completely. But in this case the volume of data makes the data stream
                            > too clumsy when put into the url?p1=v1&p2=v2 ... format.
                            >
                            > I can easily use a single JS variable ("myXML" in this case) to pass XML
                            > data like this:
                            > var myURL = 'abc.cfm?xmlcontent=' + myXML;
                            > ajax('GET', myURL, true);
                            >
                            > ["ajax()" is a multi-browser compatible JS function that I wrote
                            > specifically to send/receive XML to the server.]
                            >
                            > Then, of course, CF can read the XML data as a URL variable, eg:
                            > <cfif IsDefined("URL.xmlcontent") >
                            > <cfset inXML = XmlParse("URL.xmlcontent") />
                            > </cfif>
                            >
                            > Now imagine that the "myXML" variable has 300 child nodes, each of which can
                            > have two children containing up to 2K (each) of text plus a dozen siblings of
                            > comparatively inconsequential sizes. [The actual average size is approximately
                            > 60K, but 700K has happened.]
                            >
                            > Storing the XML as a file and then reading it into CF using CFFILE results
                            > could result in 1.4 Mb conversation; reading it directly - if possible - cuts
                            > that conversation by half.
                            >
                            > That's my goal. It would be the exact opposite of a cfhttp tag.
                            >
                            > Thanks.
                            >
                            >

                            What happens if you POST the data, rather then GET the data. I.E.
                            something like: 'ajax('POST',myURL,true)'. I have no idea what your
                            home grown 'ajax()' function exactly does. But I know it is trival to
                            have ajax submit a post request rather then a get request.

                            My current applicaiton does it in this manner:

                            <cfajaxproxy cfc="services.purDAO" jsclassname="purDAO">

                            ...

                            var updatePUR = function()
                            {
                            var purDAOproxy = new purDAO();
                            purDAOproxy.setHTTPMethod('post');
                            purDAOproxy.setCallbackHandler(populateValidateDiv);
                            purDAOproxy.setErrorHandler(myErrorHandler);

                            purDAOproxy.setForm('edit');
                            purDAOproxy.updatePUR('#form.use_Number#',purYear,currentPUR,false);

                            return false;
                            }

                            This relies on CF8's ajax framework, but it should not be hard to
                            replicate if you do not want to use the built in one.

                            • 11. Re: Reverse CFXML ?
                              coffeedrinker56 Level 1
                              > What happens if you POST the data, rather then GET the data. I.E.
                              > something like: 'ajax('POST',myURL,true)'. I have no idea what your
                              > home grown 'ajax()' function exactly does. But I know it is trivial to
                              > have ajax submit a post request rather then a get request.

                              "ajax()" as a cross-browser, multi-purpose XmlHttpRequest function. Its primary purpose is to limit thread use due to bandwidth issues. [It's single-threaded.]

                              A 4th parameter - not shown above - routes the output from the function to other purpose-specific JS functions most of which parse different XML streams. It probably equates to your "setCallbackHandler".

                              Anyway...

                              Constructs not available in CF7 are - at least for now - not allowed. CF8 is currently being tested on the development server, but production is CF7.

                              I do like your solution, though. I'll see if I can replicate something similar in CF7.

                              Thanks.




                              • 12. Re: Reverse CFXML ?
                                Level 7
                                coffeedrinker56 wrote:
                                > I do like your solution, though. I'll see if I can replicate something similar
                                > in CF7.

                                A suggestion, in case you do not think of it yet. If you have access to
                                the CF8 server, you could inspect the javascript created by the
                                <cfajaxproxy...> tag.

                                It just creates some 'out of the box' JS in the response that can be
                                inspected in the browser. I suspect somebody experience in JS and AJAX
                                would be able to use it as a template if not a whole sale copy and paste.

                                • 13. Re: Reverse CFXML ?
                                  Level 7
                                  you can also use (rob gonda's?) ajaxCFC with cfmx 7 to achieve similar
                                  functionality.
                                  many recent js frameworks support/include such functionality, too.


                                  Azadi Saryev
                                  Sabai-dee.com
                                  http://www.sabai-dee.com/
                                  • 14. Re: Reverse CFXML ?
                                    coffeedrinker56 Level 1
                                    Ian, et al:

                                    Writing a custom Ajax function in JS is simple as well. This one is pretty "generic" excepting that it always expects incoming responses as XML.

                                    Note: The "setPostData" function is a form scraper. It retrieves all non-zero, non-null form value pairs from the RIA - reflecting the curent DOM status - and formats it as XML.

                                    function ajax( method, url, async, filetype) {
                                    var myMethod = method; // 'GET', 'POST', or null
                                    var myURL = url; // script to be executed
                                    var myAsync = async; // true=asynchronous, false=synchronous
                                    var myType = filetype; // flag used for routing after data are loaded
                                    var mySend = null; // default value is null - for GET method
                                    ajaxAvailable = false; //global flag stops process interruption
                                    if (myMethod == "") {
                                    myMethod = 'GET'; // set default method
                                    } else if (myMethod == 'POST') {
                                    mySend = setPostData(); // If post, load data to be posted
                                    }
                                    if (myAsync == "") {
                                    myAsync = true; // default is asynchronous
                                    }
                                    if (myType == "") {
                                    myType = 'SA'; // default data type used for data parsing
                                    }

                                    // Instantiate XmlHttpRequest object. ("ajaxHttp" is global)
                                    try { ajaxHttp = new XMLHttpRequest();
                                    } catch ( err ) {
                                    try { ajaxHttp = new ActiveXObject("Microsoft.XMLHTTP");
                                    } catch ( err ) {
                                    try { ajaxHttp = new ActiveXObject("MSXML4.XMLHTTP");
                                    } catch ( err ) {
                                    try { ajaxHttp = new ActiveXObject("MSXML3.XMLHTTP");
                                    } catch ( err ) {
                                    // reroute for non-ajax-capable browsers
                                    document.location.href = 'wit2_bad_browser.cfm';
                                    }
                                    }
                                    }
                                    }
                                    try { ajaxHttp.overridemimetype('text/xml'); // forced mime type
                                    ajaxHttp.onerror = function() {
                                    document.getElementById("message").innerHTML = 'Load Error';
                                    }
                                    } catch ( err ) {};
                                    ajaxHttp.onreadystatechange = function ( aEvt ) {
                                    window.status = 'Loading ' + filetype + ' data...';
                                    // If process is not complete ...
                                    if ( ajaxHttp.readystate < 4 ) {
                                    return;
                                    } else {
                                    window.status = 'Done.';
                                    if (ajaxHttp.status == 200) {
                                    // loads global "ajaxDoc" with XML
                                    ajaxDoc = ajaxHttp.responseXML;
                                    // route to the appropriate XML data handler
                                    loadDocs( myType );
                                    } else {
                                    // Otherwise, report error
                                    myTarget.setAttribute("style", "color:#ff0000");
                                    myTarget.innerHTML = 'Error code ' + ajaxHttp.status + ': Unable to load the ' + filetype + ' data file.';
                                    }
                                    }
                                    }

                                    // initiate the request
                                    ajaxHttp.open( myMethod, myURL, myAsync );
                                    // sends null for GET method, data for POST method
                                    ajaxHttp.send( mySend );
                                    }