2

I have a few lines of text containing street address information from an HTML textarea input. In my application I can't break this up into five separate form input fields (MailingStreet, MailingCity, MailingState, MailingPostalCode and MailingCountry).

Is there a parser that is available in Apex to break this down for me in the five address components? Salesforce must be doing this when they parse the web-to-lead address textarea to populate the Lead address field.

  • String Methods looking for certain delimiters and or Pattern Matcher with RegEx. – crmprogdev Aug 25 '15 at 19:06
  • 1
    @crmprogdev if the answer is "No" then, yes I will have to roll my own parser, but someone has already done the hard work and I'm hoping to avoid this very picky and error-prone process. – JannieT Aug 25 '15 at 19:16
3

I have provided a similar solution in this (SFSE) forum link. I have used googleapis as parser to parse the text area into different text fields. It is a complete example to start with. You can update further as per your requirement. Hope this helps.

  • Very clever solution! I didn't think about parsing in JavaScript like that. I slightly prefer not to be dependent on another client side service (I already use two other ones on my page), but if there are no better answers in the next three days, I'll accept your excellent solution. – JannieT Aug 26 '15 at 6:15
1

You can parse it by writing your own code in trigger if address is separated by any delimiter like ',' and the address is in fixed order like house no., city, state, postal code, country.

You can use following code in your before insert/update trigger:

List<String> splittedAddress = TextAreaField__c.split(',');
String street = splittedAddress.get(0);
String city = splittedAddress.get(1);
String state = splittedAddress.get(2);
String postalCode = splittedAddress.get(3);
String country = splittedAddress.get(4);

Hope it helps. Thanks

1

I know this is pretty late, but maybe someone else will see this and find it useful.

I wrote my own class to do just this. It assumes that the addresses are primarily US addresses (as is the case for the org I wrote it in), and then made adjustments for Canadian and UK zip codes. I know that it won't work for Somalia or Montserrat because of their zip codes.

It's a surprisingly difficult thing to do because of the varied ways that addresses are written. But this has worked correctly for every address in my org.

public class addressParser {

    public static Map<string, string> parseAddress(String address){
        address.replace('\r\n', '\n').replace('\r', '\n');

        Map<String, String> addressMap = new Map<String, String>();
        String tempAddress = address.trim();
        Boolean International = false;
        String Country = '';
        if (checkForCountryString(address) != ''){
            country = checkForCountryString(address).trim();
            tempAddress = address.removeEnd(country).trim();
        }
        String zipAddress = tempAddress;//zip should be after a comma or after a line break if there is none
        if (tempaddress.contains(','))
             zipAddress = tempAddress.remove(tempAddress.split(',')[0]); 
        else if (tempaddress.contains('\n'))
            zipAddress = tempAddress.remove(tempAddress.split('\n')[0]);

        Pattern zipPattern = Pattern.compile('\\d{5}(?:[-\\s]\\d{4})?'); //US Zip code pattern, accounts for 12345, 12345-6789 or 12345 6789 patterns at the end of the address string
        Matcher zipMatcher = zipPattern.matcher(zipAddress);
        String zip = '';
             while (zipMatcher.find()){ 
                if (zipMatcher.group() != null && zipMatcher.group() != '')
                zip = zipMatcher.group(); //zip code that matches us pattern
                }
            if (zip == null || zip == ''){
                International = true;
                addressMap = getInternationalZip(zipAddress);
                zip = addressMap.get('Postal Code');
                if (addressMap.containsKey('Country'))//&& tempAddress.contains(addressMap.get('Country'))
                    tempAddress = address.removeEnd(addressMap.get('Country')).trim();
            }
            else
                addressMap.put('Postal Code', zip);

            if (country != ''){
                addressMap.put('Country', country);
                tempAddress = tempAddress.remove(Country).trim();
            }

            tempAddress = tempAddress.trim().remove(zip).trim();
                if (tempAddress.substringAfterLast(',').trim() == '' )//If comma is between state & zip or zip and country, remove it
                    tempAddress = tempAddress.removeEnd(',');

            String state = tempAddress.substringAfterLast(',').trim(); // state is block after comma that isn't zip

            if ((zip != null  && zip != '') && (state == null || state == '') && International == false){
                state = tempAddress.substringAfterLast(' ').trim(); //if user didn't add comma, only space
                String temp2 = tempAddress.removeEnd(state).trim();
                String stringBeforeState = temp2.substringAfterLast(' ').trim();
                if (stringBeforeState.contains('New') || stringBeforeState.contains('North') || stringBeforeState.contains('South') || stringBeforeState.contains('East') || stringBeforeState.contains('West'))
                state = stringBeforeState + ' '+ state;
            }

            addressMap.put('State', state);

            tempAddress = tempAddress.removeEnd(state);
            tempAddress = tempAddress.trim().removeEnd(',');

            String city = tempAddress.trim().substringAfterLast('\n').trim();//in between line break and comma
            if (city == null || city == '')
                city = tempAddress.substringAfterLast(','); //If comma was used in place of a line break

            addressMap.put('City', city);
            tempAddress = tempAddress.remove(city);
            String street = tempAddress;
            addressMap.put('Street', street);

            if (addressMap.get('Country') != '' && !International)
                addressMap.put('Country', 'USA');
            return addressMap;
    }


    public static Map<String, String> getInternationalZip(String Address){
        Map<String, String> addressMap = new Map<String, String>();
        String zip = '';
        Map<string, string> regexMap = new Map<String, String>();
        regexMap.put('Canada', '[ABCEGHJKLMNPRSTVXY]{1}\\d{1}[A-Z]{1} *-* *\\d{1}[A-Z]{1}\\d{1}');
        regexMap.put('UK', '(GIR 0AA)|((([A-Z-[QVX]][0-9][0-9]?)|(([A-Z-[QVX]][A-Z-[IJZ]][0-9][0-9]?)|(([A-Z-[QVX]][0-9][A-HJKPSTUW])|([A-Z-[QVX]][A-Z-[IJZ]][0-9][ABEHMNPRVWXY])))) [0-9][A-Z-[CIKMOV]]{2})');// Official UK Regex
        regexMap.put('Netherlands', '[0-9]{4}\\s*[A-Za-z]{2}');
        for (String s: regexMap.keyset()){
            Pattern zipMatcher = Pattern.compile(regexMap.get(s));
            Matcher zipMatch = zipMatcher.matcher(Address);
                while (zipMatch.find())
                    zip = zipMatch.group();
                if (zip != '' && zip != null){
                    addressMap.put('Postal Code', zip);
                    addressMap.put('Country', s);
                    return addressMap;
                }
        }
        if (zip == '' || zip == null)
            addressMap = getZip(address);
        //Not included -- Somalia   AA NNNNN      Montserrat    MSR 1110-1350
        return addressMap;
    }


public static Map<String, String> getZip(String address){ //If all else fails, get the string containing numbers from the lowest line available
        Map<String, String> addressMap = new Map<String, String>();
        Boolean foundZip = false;
        Pattern containsNumbers = Pattern.compile('.*[0-9].*');
        String zip = '';

        List<String> lines = address.split('\r\n');
        for(Integer i = (lines.size()-1); i >= 0; i--){
            String s = lines[i].trim();
            Matcher mixedMatcher = containsNumbers.matcher(s);
            if (mixedMatcher.matches()){
                List<String> tokens = s.split(' ');
                for (string token: tokens){
                    Matcher zipMatcher = containsNumbers.matcher(token);
                    if (zipmatcher.matches())
                        zip += token;
                }
                break;
            }
        }
        addressMap.put('Postal Code', zip);
        return addressMap;
    }
    public static string checkForCountryString(String address){ //We're going to assume that it's a country string if the last line is all characters and there are at least 2 preceding lines
        String country = '';
        Pattern onlyLetters = Pattern.compile('^[a-zA-Z\\s]*$');

        List<String> lines = address.split('\r\n');
        if (address.contains('<br/>'))
            lines = address.split('<br/>');
            if (lines.size() > 2){
                String lastLine = lines[(lines.size()-1)];
                Matcher lettersMatcher = onlyLetters.matcher(lastLine.trim());
                if (lettersMatcher.matches())
                    country = lastLine;
            }
            return country;

    } 
}
0

@brezotom, that is fantastic. Will need a little tweaking, but a great start!

For anybody reading this, I'm starting to put this into a trigger, and calling it like this:

trigger beforeAccountActions on Account (before insert, before update) {
    for(Account a:Trigger.New) {
        if(String.isBlank(a.BillingStreet) && !String.isBlank(a.BillingAddressParse__c)) {
            Map<String,String> bAdd = addressParser.parseAddress(a.BillingAddressParse__c);
            if(!bAdd.isEmpty()) {
                if(bAdd.containsKey('Street')) a.BillingStreet = bAdd.get('Street');
                if(bAdd.containsKey('City')) a.BillingCity = bAdd.get('City');
                if(bAdd.containsKey('State')) a.BillingState = bAdd.get('State');
                if(bAdd.containsKey('Postal Code')) a.BillingPostalCode = bAdd.get('Postal Code');
                if(bAdd.containsKey('Country')) a.BillingCountry = bAdd.get('Country');
            }
        }
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.