• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 340
  • Last Modified:

Parsing simple XML

Hi,
I am not very adapt with XML so please take that into consideration.

I am incorporating a CFHTTP call to the USPS to standardize a single address.

I will get back from the API (cfhttp.filecontent) an XML document that will look exactly like this:

<?xml version="1.0"?>
<AddressValidateResponse>
<Address ID="0">
<Address2>6406 IVY LN</Address2>
<City>GREENBELT</City>
<State>MD</State>
<Zip5>20770</Zip5>
<Zip4>1441</Zip4>
</Address>
</AddressValidateResponse>

There may be an optional <address1>apt 2</address1>

Could you please show me how to parse this in ColdFusion 8?  PLEASE dont send me to the docs as they are very confusing to me with much more complicated examples.  This will always be a single address returned with the fields above.

Any help will be appreciated.

Thanks in advance,
hefterr
0
hefterr
Asked:
hefterr
  • 10
  • 8
1 Solution
 
_agx_Commented:
Fortunately, it's easy.  But be sure to check the response code first. Because if an error occurred, the xmlParse will fail and you won't know why.  

Just parse it into an xml doc, then use xmlSearch to extract an array of addresses. Use elementName.xmlText to get the values of the address, city, etc...

<cfset xml = xmlParse(trim(cfhttp.filecontent))>
<cfset addressArr = xmlSearch(xml, "/AddressValidateResponse/Address")>
<cfloop array="#addressArr#" index="elem">
      <cfoutput>
      address2 = #elem.address2.xmlText#<br>
      city = #elem.city.xmlText#<br>
      state = #elem.state.xmlText#<br>
        ... other elements ....
      </cfoutput><hr>
</cfloop>
0
 
_agx_Commented:
    > There may be an optional <address1>apt 2</address1>

XML nodes are like structures. So you can use structKeyExists() inside your loop to test if that element exists.

...
<cfloop array="#addressArr#" index="elem">
    ...
    <cfif structKeyExists(elem, "address1")>
          address1 = #elem.address1.xmlText#<br>
    </cfif>
</cfloop>
0
 
hefterrAuthor Commented:
Hi  _agx_,
Thanks for the response!

Could you explain what is happening in the statement :
<cfset addressArr = xmlSearch(xml, "/AddressValidateResponse/Address")>

Particularly, the "/Address" ?

Thanks,
hefterr
0
Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

 
_agx_Commented:
xmlSearch let's you find and extract specific elements. The "/AddressValidateResponse/Address" is a path to the elements you want. It's just like a folder path or URL.  This "/" means the root, and the rest are like subfolders.

/                                                        <=== root of the xml document
/AddressValidateResponse                <===  child of root named "AddressValidateResponse"
/AddressValidateResponse/Address   <=== grand child named "Address"
0
 
hefterrAuthor Commented:
For extra credit :),
I may get an error XML message back that looks like this :

<AddressValidateResponse>
<Address ID="0">
<Error>
<Number>-2147219040</Number>
<Source>SOLServerTest;SOLServerTest.CallAddressDll</Source>
<Description>
This Information has not been included in this Test Server.
</Description>
<HelpFile/>
<HelpContext/>
</Error>
</Address>
</AddressValidateResponse>

How would I test to see if there is an <Error> note instead?  And then access the "Number", "Source" and "Description"?

Thanks so much for your help!!
hefterr
0
 
_agx_Commented:
<?xml version="1.0"?>                                          <=== root
---<AddressValidateResponse>                             <=== child element
--------<Address ID="0">....</Address>                 <=== these are all grandchildren
--------<Address ID="1">....</Address>                
--------<Address ID="3">....</Address>                


0
 
hefterrAuthor Commented:
Not sure if you saw my last question as our emails might have passed each other :)

Thanks!
0
 
_agx_Commented:
Oops. Small email collision ;-)

There's sexier ways to do it.  But something like this would work

...
      <cfif arrayLen(elem.xmlChildren) and elem.xmlChildren[1].xmlname eq "error">
            Number = #elem.error.Number.xmlText#<br>
            source = #elem.error.source.xmlText#<br>
      </cfif>
...
0
 
_agx_Commented:
Or this

<cfloop array="#addressArr#" index="elem">
        <!--- find "Error" nodes WITHIN the current Address node --->
      <cfset errArray = xmlSearch(elem, "//Error")>
        <!--- one or more errors was found ...--->
      <cfif arrayLen(errArray)>
               <!--- output the 1st error ...--->
            <cfoutput>
                  Number = #errArray[1].Number.xmlText#<br>
                  source = #errArray[1].Source.xmlText#<br>
            </cfoutput>
         <cfelse>
                no errors. do something else...
      </cfif>
</cfloop>
0
 
_agx_Commented:
Gah... hit return too soon. Meant to add :

    > I may get an error XML message back

Ok. But check the cfhttp status code first, because if cfhttp couldn't connect, you won't get that far ;)
0
 
hefterrAuthor Commented:
I'm still a little confused on your search syntax:
 <cfset errArray = xmlSearch(elem, "//Error")>

It didn't seem to fit your previous explanation.


Also, should the array index be hard coded as [1] or should it be [elem]?  This is somewhat theoretical as I will only be dealing with 1 address/response at a time.  But your code  seems to allow for multiple responses (the USPS does allow for multiple address validations in one request and will have the responses mixed).

You don't have to bother with this as it is only theoretical at this time for me.

hefterr


0
 
hefterrAuthor Commented:
we may have crossed emails again :)
0
 
_agx_Commented:
    > I'm still a little confused on your search syntax:
     > <cfset errArray = xmlSearch(elem, "//Error")>

In the 2nd example, you're passing in the Address node. So you're not searching from the root anymore.  Also xpath syntax differs a little ;) The "//" means search anywhere in the node, rather than "/" from the root.

     > Also, should the array index be hard coded as [1] or should it be [elem]?

xmlSearch returns an array. So you have to use an index like [1] , [2],... to access the contents. You can't use [elem] because it's an object not a number.
0
 
_agx_Commented:
     >  The "//" means search anywhere in the node, rather than "/" from the root.

You could use a full path instead if that makes more sense to you. I just find it too verbose ;)

ie
<cfset errArray = xmlSearch(elem, "/AddressValidateResponse/Address/Error")>

     ... is the same as

<cfset errArray = xmlSearch(elem, "//Error")>
0
 
hefterrAuthor Commented:
xmlSearch returns an array. So you have to use an index like [1] , [2],... to access the contents. You can't use [elem] because it's an object not a number.

But you are using "elem" as an index number:

<cfloop array="#addressArr#" index="elem">      <cfoutput>
      address2 = #elem.address2.xmlText#<br>
      city = #elem.city.xmlText#<br>
      state = #elem.state.xmlText#<br>
        ... other elements ....
      </cfoutput><hr>
</cfloop>
0
 
hefterrAuthor Commented:
Sorry, nevermind.   I had the array loop syntax confused:

 <cfloop index="x" array="#arr#">

2   <cfoutput>#x#<br /></cfoutput>

3  </cfloop>
0
 
hefterrAuthor Commented:
Thanks again for your expertise!!
0
 
_agx_Commented:
> Sorry, nevermind.   I had the array loop syntax confused:

I may have confused things with the different syntax. My thinking was you might get back multiple "address" elements. So using a dynamic <cfloop array"..."> made more sense.  I didn't think there'd be more than 1 error, so I hard coded it to [1].  If there can be more than one, you could always use <cfloop array"...">  instead.

          ...
          <cfloop array="#errArray#" index="theError">
              <cfoutput>
                  Number = #theError.Number.xmlText#<br>
                  ....
            </cfoutput>
         </cfloop>

Hope that clears up any confusion :)
0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 10
  • 8
Tackle projects and never again get stuck behind a technical roadblock.
Join Now