Syntax Highlighter JS

Monday, September 17, 2012

USPS Address Standardization API Class

If you look around the app exchange for Salesforce, you can find many programs that will do address validation for you but almost all come at a price. Usually they charge per address transaction. Why pay this fee when you can do this for free?

In this post we are going to cover the main programming required to do this for free.  For now it is up to you to tie this to a GUI for use / testing but I hope to release a custom component to wrap all of this up for easier use.

UPDATE: Here is a link to the custom component.

On the USPS website you can find the E-Commerce APIs. One of those is the Address Information API. If you want to test the code below out, you will need to ahead and create a USPS API account and then request access to the address standardization API.  I recommend you do this upfront as it can sometimes take a couple of days to get access.

Note: this API is not meant for database cleaning but is instead meant for real time verification of addresses for items sent via USPS. You may also want to look at the Web Tools API Users Guide.

So lets start out with a class to hold all of our data:

public class USPSAddress { // in the USPS world, address 1 contains address 2 data (i.e. UNIT 101 or APT A) // So you will need to put the actual street address in address2 for proper parsing public string Address1 {get; set;} public string Address2 {get; set;} public string City {get; set;} public string State {get; set;} public string Zip5 {get; set;} public string Zip4 {get; set;} public boolean USPS_Returned_Error {get; set;} public string USPS_ERROR_CODE {get; set;} public string USPS_ERROR_DESC {get; set;} public string USPS_ERROR_SOURCE {get; set;} public USPSAddress() { Address1=''; Address2=''; City=''; State=''; Zip5=''; Zip4=''; USPS_Returned_Error = false; USPS_ERROR_CODE = ''; USPS_ERROR_DESC = ''; USPS_ERROR_SOURCE = ''; } // constructor public boolean HasData() { // this will return false if everything was defaulted. boolean ReturnValue = false; if (Address1 !='') { ReturnValue = true; } else if (Address1 !='') { ReturnValue = true; } else if (Address2 !='') { ReturnValue = true; } else if (City !='') { ReturnValue = true; } else if (State !='') { ReturnValue = true; } else if (Zip5 !='') { ReturnValue = true; } else if (Zip4 !='') { ReturnValue = true; } else if (USPS_Returned_Error !=false) { ReturnValue = true; } else if (USPS_ERROR_CODE !='') { ReturnValue = true; } else if (USPS_ERROR_DESC !='') { ReturnValue = true; } else if (USPS_ERROR_SOURCE !='') { ReturnValue = true; } return ReturnValue; } // HasData } // class USPSAddress

The code for the main class (including the test request #1 example) be sure to change your USER ID before using. Also, you will need to change the URL for initial testing and then back again for production access. public with sharing class USPS { private static final string USPS_UID = '<!-- INSERT YOUR ID HERE -->'; private static string BuildAddressQueryURLString(USPSAddress AddressToQuery) { // this function is coded to send only one address at a time // but it could be updated to support more (10 is the max ATM) by // iterating over addresses and incremending the address ID XML for each address // until you have one large URL as your query. // However, if you do modify this for mutiple addresses then you will need to // re-write the XML parse to handle that as well. String BaseURL = 'http://production.shippingapis.com/ShippingAPI.dll?API=Verify&XML='; String ReturnValue = '<AddressValidateRequest USERID="' + USPS_UID + '"><Address ID="0">'; ReturnValue += '<Address1>' + AddressToQuery.Address1 + '</Address1>'; ReturnValue += '<Address2>' + AddressToQuery.Address2 + '</Address2>'; ReturnValue += '<City>' + AddressToQuery.City + '</City>'; ReturnValue += '<State>' + AddressToQuery.State + '</State>'; ReturnValue += '<Zip5>' + AddressToQuery.Zip5 + '</Zip5>'; ReturnValue += '<Zip4>' + AddressToQuery.Zip4 + '</Zip4>'; ReturnValue += '</Address></AddressValidateRequest>'; ReturnValue = EncodingUtil.urlEncode(ReturnValue, 'UTF-8'); ReturnValue = BaseURL + ReturnValue; return ReturnValue; } // BuildAddressQueryURLString private static string GetStandardizedAddressFromUSPS(string USPSURLtoQuery) { string ReturnValue = ''; HttpRequest USPSRequest = new HttpRequest(); Http USPSHttp = new Http(); USPSRequest.setMethod('GET'); USPSRequest.setEndpoint(USPSURLtoQuery); HttpResponse USPSResponse = USPSHttp.send(USPSRequest); ReturnValue = USPSResponse.getBody(); system.debug('XML Response was: ' + ReturnValue); system.debug('-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-'); return ReturnValue; } // GetStandardizedAddressFromUSPS private static USPSAddress ParseUSPSResponseXML(String XMLToParse) { USPSAddress ReturnAddress = new USPSAddress(); // parse the response XMLStreamReader USPSXMLReader = new XMLStreamReader(XMLToParse); while (USPSXMLReader.hasNext()) { if (USPSXMLReader.getEventType() == XmlTag.START_ELEMENT) { if ('AddressValidateResponse' == USPSXMLReader.getLocalName()) { USPSXMLReader.next(); if ('Address' == USPSXMLReader.getLocalName()) { ReturnAddress = ParseUSPSAddressXML(USPSXMLReader); } // <Address ID="0"> tag } // <AddressValidateResponse> confirmation } // Starting tag USPSXMLReader.next(); } // loop thru UPS XML Reader if (ReturnAddress.HasData() == false) { // if parsing comes back totally blank then indicate an unknown / parsing error to the caller / requestor system.Debug('ReturnAddress.HasData() == false'); ReturnAddress.USPS_Returned_Error = true; ReturnAddress.USPS_ERROR_DESC = 'Unknown Error parsing XML Response'; ReturnAddress.USPS_ERROR_SOURCE = 'Salesforce XML Parsing'; ReturnAddress.USPS_ERROR_CODE = '-1'; } // ReturnAddress.HasData() == false return ReturnAddress; } // ParseAddressXML private static USPSAddress ParseUSPSAddressXML(XMLStreamReader USPSAddressXMLReader) { USPSAddress ReturnAddress = new USPSAddress(); while(USPSAddressXMLReader.hasNext()) { if (USPSAddressXMLReader.getEventType() == XmlTag.END_ELEMENT) { if ('Address' == USPSAddressXMLReader.getLocalName()) { // quit parsing when we hit the end of this record break; } // check for address ending tag } else if ('Error' == USPSAddressXMLReader.getLocalName()) { system.Debug('API Returned an error!'); ReturnAddress = ParseUSPSErrorXML(USPSAddressXMLReader); ReturnAddress.USPS_Returned_Error = true; } else if ('Address1' == USPSAddressXMLReader.getLocalName()) { USPSAddressXMLReader.next(); if (USPSAddressXMLReader.getEventType() == XmlTag.CHARACTERS) { system.Debug('Address1: ' + USPSAddressXMLReader.getText()); ReturnAddress.Address1 = USPSAddressXMLReader.getText(); } // check for data } else if ('Address2' == USPSAddressXMLReader.getLocalName()) { USPSAddressXMLReader.next(); if (USPSAddressXMLReader.getEventType() == XmlTag.CHARACTERS) { system.Debug('Address2: ' + USPSAddressXMLReader.getText()); ReturnAddress.Address2 = USPSAddressXMLReader.getText(); } // check for data } else if ('City' == USPSAddressXMLReader.getLocalName()) { USPSAddressXMLReader.next(); if (USPSAddressXMLReader.getEventType() == XmlTag.CHARACTERS) { system.Debug('City: ' + USPSAddressXMLReader.getText()); ReturnAddress.City = USPSAddressXMLReader.getText(); } // check for data } else if ('State' == USPSAddressXMLReader.getLocalName()) { USPSAddressXMLReader.next(); if (USPSAddressXMLReader.getEventType() == XmlTag.CHARACTERS) { system.Debug('State: ' + USPSAddressXMLReader.getText()); ReturnAddress.State = USPSAddressXMLReader.getText(); } // check for data } else if ('Zip5' == USPSAddressXMLReader.getLocalName()) { USPSAddressXMLReader.next(); if (USPSAddressXMLReader.getEventType() == XmlTag.CHARACTERS) { system.Debug('Zip5: ' + USPSAddressXMLReader.getText()); ReturnAddress.Zip5 = USPSAddressXMLReader.getText(); } // check for data } else if ('Zip4' == USPSAddressXMLReader.getLocalName()) { USPSAddressXMLReader.next(); if (USPSAddressXMLReader.getEventType() == XmlTag.CHARACTERS) { system.Debug('Zip4: ' + USPSAddressXMLReader.getText()); ReturnAddress.Zip4 = USPSAddressXMLReader.getText(); } // check for data } // check for end tags USPSAddressXMLReader.next(); } // loop thru XML reader return ReturnAddress; } // ParseUSPSAddressXML private static USPSAddress ParseUSPSErrorXML(XMLStreamReader USPSErrorXMLReader) { USPSAddress ReturnAddress = new USPSAddress(); while(USPSErrorXMLReader.hasNext()) { if (USPSErrorXMLReader.getEventType() == XmlTag.END_ELEMENT) { if ('Error' == USPSErrorXMLReader.getLocalName()) { // quit parsing when we hit the end of this record break; } } else if ('Number' == USPSErrorXMLReader.getLocalName()) { USPSErrorXMLReader.next(); if (USPSErrorXMLReader.getEventType() == XmlTag.CHARACTERS) { system.Debug('Error Number / Code: ' + USPSErrorXMLReader.getText()); ReturnAddress.USPS_ERROR_CODE = USPSErrorXMLReader.getText(); } // check for data } else if ('Source' == USPSErrorXMLReader.getLocalName()) { USPSErrorXMLReader.next(); if (USPSErrorXMLReader.getEventType() == XmlTag.CHARACTERS) { system.Debug('Error Source: ' + USPSErrorXMLReader.getText()); ReturnAddress.USPS_ERROR_SOURCE = USPSErrorXMLReader.getText(); } // check for data } else if ('Description' == USPSErrorXMLReader.getLocalName()) { USPSErrorXMLReader.next(); if (USPSErrorXMLReader.getEventType() == XmlTag.CHARACTERS) { system.Debug('Error Description: ' + USPSErrorXMLReader.getText()); ReturnAddress.USPS_ERROR_DESC = USPSErrorXMLReader.getText(); } // check for data } // check for ending element USPSErrorXMLReader.next(); } // loop thru XML reader return ReturnAddress; } // ParseUSPSErrorXML public static USPSAddress CheckAddress(USPSAddress Address2Check) { USPSAddress ResponseAddress = new USPSAddress(); // build the URL for the API call string USPSURL = BuildAddressQueryURLString(Address2Check); // call the API and pullback the XML as a string string XMLResponse = GetStandardizedAddressFromUSPS(USPSURL); // send the XML reponse to the parser ResponseAddress = ParseUSPSResponseXML(XMLResponse); return ResponseAddress; } // CheckAddress public static void RunUSPSTest1() { // create a new object to hold the data USPSAddress TestAddress = new USPSAddress(); // populate the data TestAddress.Address2='6406 Ivy Lane'; TestAddress.City='Greenbelt'; TestAddress.State='MD'; USPSAddress SearchResult = CheckAddress(TestAddress); } // Run USPS Test1 } // end USPS Class

No comments:

Post a Comment