ibmi-brunch-learn

Announcement

Collapse
No announcement yet.

Attributes Pointer Null in Start Subprocedure using HTTP_API's http_parse_xml_stmf

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Attributes Pointer Null in Start Subprocedure using HTTP_API's http_parse_xml_stmf


    TL;DR: When in the start subprocedure specified in the call to http_parse_xml_stmf, the attributes pointer seems to be null and I can't retrieve attributes until the end subprocedure runs.

    Background: I am using version 1.39 of Scott Klements HTTP_API open source project to parse an XML document. The XML document is downloaded via a third party product and not via HTTP_API. The vendor is working on adding support for the contents of this particular XML document but it won't be ready in time for a quickly approaching deadline. Additionally, the vendor does all the heavy lifting regarding authentication and writing to the IFS during the download request. In the meantime, I am using http_parse_xml_stmf to parse the XML document. The only thing I modified regarding the attached XML document is I made it much smaller and changed the contents since it did contain sensitive information. Other than that, it is a straight download from the IBM i using the IBM i Navigator and then a save using Notepad++.

    Explanation and Issue: I need to get the attributes from the Request element in the attached XML document but it appears as though the attributes pointer is null when the StartOfElement subprocedure runs. However, when the EndOfElement subprocedure runs, I can retrieve the attributes. Scott's Example11 also shows retrieving attributes in the end subprocedure. However, on PDF page 15 of the handout for a retired presentation of Scott's titled XML From RPG Using Free Tools it says in part: "Usually, the Start Element Handler has two important goals: • To keep track of the position in the document (create an Xpath) • To parse the attributes". I realize this is a retired presentation but I am making an assumption that what was said still holds true because the presentation talks about Expat which is still used in HTTP_API from what I understand. Perhaps that is a false assumption and my understanding of HTTP_API is incorrect.

    My Procedure Interface for StartOfElement looks like this and was modeled after a combination of his Example10 and Example14:
    Code:
    D StartOfElement  PI
         D   xmlDataIn                         likeds(xmlData)
         D   depth                       10I 0 value
         D   name                      1024A   varying const
         D   path                     24576A   varying const
         D   value                    65535A   varying const
         D   attrs                         *   dim(32767)
         D                                     const options(*varsize)
    My Procedure Interface for EndOfElement looks the same:
    Code:
    D EndOfElement    PI
         D   xmlDataIn                         likeds(xmlData)
         D   depth                       10I 0 value
         D   name                      1024A   varying const
         D   path                     24576A   varying const
         D   value                    65535A   varying const
         D   attrs                         *   dim(32767)
         D                                     const options(*varsize)
    If I debug and show the value of attrs in StartOfElement, this is what I see:
    Code:
    EVAL attrs          
    ATTRS(1) = SPP:*NULL
    ATTRS(2) = SPP:*NULL
    ATTRS(3) = SPP:*NULL
    ATTRS(4) = SPP:*NULL
    ATTRS(5) = SPP:*NULL
    ATTRS(6) = SPP:*NULL
    ATTRS(7) = SPP:*NULL
    ATTRS(8) = SPP:*NULL
    ATTRS(9) = SPP:*NULL
    ATTRS(10) = SPP:*NULL
    ATTRS(11) = SPP:*NULL
    ATTRS(12) = SPP:*NULL
    ATTRS(13) = SPP:*NULL
    If I debug and show the value of attrs in EndOfElement, this is what I see:
    Code:
    EVAL attrs                      
    ATTRS(1) = SPP:E7EB0DEE7B03FA60
    ATTRS(2) = SPP:E7EB0DEE7B03FA90
    ATTRS(3) = SPP:E7EB0DEE7B03FAD0
    ATTRS(4) = SPP:E7EB0DEE7B03FB00
    ATTRS(5) = SPP:E7EB0DEE7B03FB40
    ATTRS(6) = SPP:E7EB0DEE7B03FB70
    ATTRS(7) = SPP:E7EB0DEE7B03FBA0
    ATTRS(8) = SPP:E7EB0DEE7B03FBE0
    ATTRS(9) = SPP:E7EB0DEE7B03FC30
    ATTRS(10) = SPP:E7EB0DEE7B03FC60
    ATTRS(11) = SPP:E7EB0DEE7B03FC80
    ATTRS(12) = SPP:E7EB0DEE7B03FCC0
    ATTRS(13) = SPP:E7EB0DEE7B03FD10
    In both cases, the path variable contains /Report/Requests/Request. This is the code in my StartOfElement and EndOfElement for retrieving the attributes:
    StartOfElement
    Code:
    if path = '/Report/Requests/Request';
      count = 1;
      dow http_nextXmlAttr(attrs: count: attrName: attrVal);
         select;
            when attrName = 'RequestID';
               attribute = attrVal;
               xmlRequestID = attrVal;
               xmlDataIn.requestID = attrVal;        
            when attrName = 'MerchantID';
               attribute = attrVal;
               xmlMerchantId = attrVal;
               xmlDataIn.merchantId = attrVal;
            when attrName = 'MerchantReferenceNumber';
               attribute = attrVal;
               xmlMerchRefCode = attrVal;
               xmlDataIn.merchRefCode = attrVal;
         endsl;
      enddo;
    endif;
    EndOfElement
    Code:
    if path = '/Report/Requests/Request';
      count = 1;
      dow http_nextXmlAttr(attrs: count: attrName: attrVal);
         select;
            when attrName = 'RequestID';
               attribute = attrVal;
               xmlRequestID = attrVal;
               xmlDataIn.requestID = attrVal;        
            when attrName = 'MerchantID';
               attribute = attrVal;
               xmlMerchantId = attrVal;
               xmlDataIn.merchantId = attrVal;
            when attrName = 'MerchantReferenceNumber';
               attribute = attrVal;
               xmlMerchRefCode = attrVal;
               xmlDataIn.merchRefCode = attrVal;
         endsl;
      enddo;
    endif;
    I promise you that there is no voodoo or witchcraft or wizardry. That's what it looks like in both subprocedures yet I can only retrieve the attributes in EndOfElement.

    When invoking the subprocedure, http_parse_xml_stmf, this is my code:
    Code:
    if (http_parse_xml_stmf( %trim(FilepathSv)
                         : HTTP_XML_CALC
                         : %paddr(StartOfElement)
                         : %paddr(EndOfElement)
                         : %addr(xmlData)) < 0 );
     errorMessage = http_error();
     return;
    endif;
    If someone can please help me figure out what I'm doing wrong or correct me on my assumptions, I would appreciate it. If there is additional code or information I need to provide, please let me know.
    Attached Files
    Part of the inhumanity of the computer is that, once it is competently programmed and working smoothly, it is completely honest. - Isaac Asimov

  • #2
    Above you pozted this line, which puzzles me:

    Code:
     
      : %addr(xmlData)) < 0 );
    What is that meant to do?

    Comment


    • Vectorspace
      Vectorspace commented
      Editing a comment
      My guess would be it's a copy/paste error, and &lt; would be the < character in the actual program (&lt; being the html escape code for < )
      In which case, the code is merely checking if http_parse_xml_stmf() failed by testing if it returned less than zero 0

  • #3
    Originally posted by Scott Klement View Post
    Above you pozted this line, which puzzles me:

    Code:
    : %addr(xmlData)) &lt; 0 );
    What is that meant to do?
    Vectorspace is correct. I didn't notice that after I posted it. It should be
    Code:
    : %addr(xmlData)) < 0 );
    Part of the inhumanity of the computer is that, once it is competently programmed and working smoothly, it is completely honest. - Isaac Asimov

    Comment


    • #4
      Ok, thank you... I knew what &lt; meant, of course, but I misread the code the first time.

      I spent about 90 minutes converting the code you posted into a runnable program. This wasn't easy because you didn't provide all of the details, you only provided little bits of code, not all of it. And I haven't worked much with XML in recent years, which made it worse. (I did a lot of Expat 10-15 years ago, but have done very little recently. I almost never use XML anymore, everything is JSON)

      But, after I got that working, it was pretty easy to find two problems in your code.

      First problem is this PI
      Code:
           D StartOfElement  PI
           D   xmlDataIn                         likeds(xmlData)
           D   depth                       10I 0 value
           D   name                      1024A   varying const
           D   path                     24576A   varying const
           D   value                    65535A   varying const          <== THIS IS WRONG
           D   attrs                         *   dim(32767)
           D                                     const options(*varsize)
      There is no "value" parameter on the "Start" routine -- the only time you're passed the value is on the "End" routine. This causes your whole parameter list to be wrong... HTTPAPI is passing the attributes arrau in the 5th parameter (where you've coded 'value'), but you are trying to receive it in the 6th parameter.

      To fix that, simply remove the 'value' parameter from this PI (and also from its corresponding prototype) so that it looks like this:

      Code:
           D StartOfElement  PI
           D   xmlDataIn                         likeds(xmlData)
           D   depth                       10I 0 value
           D   name                      1024A   varying const
           D   path                     24576A   varying const
           D   attrs                         *   dim(32767)
           D                                     const options(*varsize)
      The other problem is the 'path' parameter. When using HTTPAPI, the 'path' parameter ONLY includes the parent element, not the element name itself. To get the element name itself, you need to look at the 'name' parameter.

      Your code looks like this:

      Code:
        if path = '/Report/Requests/Request';
      Assuming you want the attributes on the 'Request' element, this is NOT correct... this only gets the attributes for any tags that have 'Request' as a PARENT. If you want the attributes for the 'Request' tag itself, you need to code it like this:

      Code:
            if path = '/Report/Requests' and name ='Request';
      After making these changes to your code, it worked properly for me.


      Comment


      • #5
        Originally posted by Scott Klement View Post
        Ok, thank you... I knew what &lt; meant, of course, but I misread the code the first time.

        I spent about 90 minutes converting the code you posted into a runnable program. This wasn't easy because you didn't provide all of the details, you only provided little bits of code, not all of it. And I haven't worked much with XML in recent years, which made it worse. (I did a lot of Expat 10-15 years ago, but have done very little recently. I almost never use XML anymore, everything is JSON)

        But, after I got that working, it was pretty easy to find two problems in your code.

        First problem is this PI
        Code:
        D StartOfElement PI
        D xmlDataIn likeds(xmlData)
        D depth 10I 0 value
        D name 1024A varying const
        D path 24576A varying const
        D value 65535A varying const <== THIS IS WRONG
        D attrs * dim(32767)
        D const options(*varsize)
        There is no "value" parameter on the "Start" routine -- the only time you're passed the value is on the "End" routine. This causes your whole parameter list to be wrong... HTTPAPI is passing the attributes arrau in the 5th parameter (where you've coded 'value'), but you are trying to receive it in the 6th parameter.

        To fix that, simply remove the 'value' parameter from this PI (and also from its corresponding prototype) so that it looks like this:

        Code:
        D StartOfElement PI
        D xmlDataIn likeds(xmlData)
        D depth 10I 0 value
        D name 1024A varying const
        D path 24576A varying const
        D attrs * dim(32767)
        D const options(*varsize)
        The other problem is the 'path' parameter. When using HTTPAPI, the 'path' parameter ONLY includes the parent element, not the element name itself. To get the element name itself, you need to look at the 'name' parameter.

        Your code looks like this:

        Code:
        if path = '/Report/Requests/Request';
        Assuming you want the attributes on the 'Request' element, this is NOT correct... this only gets the attributes for any tags that have 'Request' as a PARENT. If you want the attributes for the 'Request' tag itself, you need to code it like this:

        Code:
        if path = '/Report/Requests' and name ='Request';
        After making these changes to your code, it worked properly for me.

        1. Thank you very much for spending so much time on this. I'm sure you had better things to do and I am very grateful.
        2. I wasn't sure what you meant when you asked what that piece of code with the '&lt;' was doing. My apologies. I thought it was odd that you wouldn't know what it meant and I should have asked for clarification.
        3. Regarding not providing all my code: I am never sure whether to provide everything or just bits and pieces and then let people ask if they want to see the whole thing. It's kind of like walking a fine line between providing enough information for questions without providing too much that isn't needed. In the future, I will provide all the code to make yours, and others, lives easier.
        4. I agree about JSON but unfortunately this particular organization does not send back JSON for this particular endpoint at this time. For some reason, they have two options: XML or CSV. They just recently...and by recently I mean this year...started using REST APIs and JSON payloads for other end points and are slowly migrating everything.
        5. I figured it was something simple that I was missing. I poured over my code and compared it to the presentation I mentioned and never even saw that I included an extra parameter. It's that whole can't see the forest through the trees thing I think. I got so focused on trying to figure it out.
        6. Thanks for the tip on the path. I thought you had to go to the level you wanted to get the attributes.
        Part of the inhumanity of the computer is that, once it is competently programmed and working smoothly, it is completely honest. - Isaac Asimov

        Comment

        Working...
        X