function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
TechEd_ProgrammerTechEd_Programmer 

Writing an Inbound Handler to read XML

All I am writing an Inbound Message Class to handle XML in the body of the e-mail. I am struggling with one error and I am not sure how to resolve it. Any assistance would be appreciated. Below is my class. My Error is on line 95. It is Method Does Not Exist or Incorrect Signature: getDecodedString(System.XMLStreamReader) I understand that it is missing an element before 'reader', btu honestly I am not sure what it should be.

 

/**
 * Email services are automated processes that use Apex classes
 * to process the contents, headers, and attachments of inbound
 * email.
 */
global class Email_CustomerRecord implements Messaging.InboundEmailHandler {

    global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email, Messaging.InboundEnvelope envelope) {
        Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();
		
		List<Account> act1;
		try
		{
			String messageBody = '';
			String action = '';
			String cust_num = '';
			String name = '';
			String cust_seq = '';
			String addr1 = '';
			String addr2 = '';
			String addr3 = '';
			String addr4 = '';
			String city = '';
			String CusCounty = '';
			String state = '';
			String country = '';
			String zip = '';
			
			messageBody = email.plainTextBody;
			messageBody = messageBody.trim();
			
			messageBody = messageBody.substring(messageBody.indexof('<?xml version="1.0" encoding="UTF-8"?>'),messageBody.indexOf('</CustomerRecord>')+12);
			
			Action = readXMLelement(messageBody,'action');
			
			String cn = readXMLelement(messageBody,'cust_num');
			List<Account> actl = [SELECT Id, Customer_Number__c, Name, BillingStreet, BillingCity, BillingState, BillingPostalCode, BillingCountry
									  FROM Account 
									  WHERE Customer_Number__c =: cn
									  ];
			
			if(Action == 'add')
			{
				Account act = new Account();
				
				act.Customer_Number__c = readXMLelement(messageBody,'cust_num');
				act.Name = readXMLelement(messageBody,'name');
				act.BillingStreet = readXMLelement(messageBody,'addr1') + readXMLelement(messageBody,'addr2') + readXMLelement(messageBody,'addr3') + readXMLelement(messageBody,'addr4');
				act.BillingCity  = readXMLelement(messageBody,'city');
				act.BillingState  = readXMLelement(messageBody,'state');
				act.BillingPostalCode  = readXMLelement(messageBody,'zip');
				act.BillingCountry  = readXMLelement(messageBody,'country');
				
				insert act;
			}
			if(Action == 'modify')
			{
				for(Account actList : act1)
				{
					actList.Name = readXMLelement(messageBody,'name');
					actList.BillingStreet = readXMLelement(messageBody,'addr1') + readXMLelement(messageBody,'addr2') + readXMLelement(messageBody,'addr3') + readXMLelement(messageBody,'addr4');
					actList.BillingCity  = readXMLelement(messageBody,'city');
					actList.BillingState  = readXMLelement(messageBody,'state');
					actList.BillingPostalCode  = readXMLelement(messageBody,'zip');
					actList.BillingCountry  = readXMLelement(messageBody,'country');
				}
				
				update act1;					
			}
			if(Action == 'delete')
			{
				delete act1;
			}
		}
		catch(exception e)
		{
			
		}
		
        return result;
    }
    public static String readXMLelement(String xml, String element)
    {
        String elementValue = 'NOT FOUND'; 
        
        try
        {
	        Xmlstreamreader reader = new Xmlstreamreader(xml);
	        while (reader.hasNext()) 
	        {
	            if (reader.getEventType() == XmlTag.START_ELEMENT && reader.getLocalName() == element)
	            {
	                System.debug('Found SID');
	                reader.next();
	                elementValue = getDecodedString(reader); <---Error Method Does Not Exist or Incorrect Signature: getDecodedString(System.XMLStreamReader)
	            }         
	            reader.next();
	        }
	        return elementValue;
        }
        catch(exception e)
        {
			String err;
			err = e.getMessage();
			return err;
        }
    }
}

 Thank you for your assistance.

Best Answer chosen by Admin (Salesforce Developers) 
vlachavlacha

Hi! I'm happy I can help :)

 

You are right, the problem is the list variable act1 ...it should be actl:

 

if(Action == 'modify'
			{
				for(Account actList : actl)/* use actl instead of act1 */
				{
					actList.Name = readXMLelement(messageBody,'name');
					actList.BillingStreet = readXMLelement(messageBody,'addr1') + readXMLelement(messageBody,'addr2') + readXMLelement(messageBody,'addr3') + readXMLelement(messageBody,'addr4');
					actList.BillingCity  = readXMLelement(messageBody,'city');
					actList.BillingState  = readXMLelement(messageBody,'state');
					actList.BillingPostalCode  = readXMLelement(messageBody,'zip');
					actList.BillingCountry  = readXMLelement(messageBody,'country');
				}

 

 

In order to get 100 % coverage you can add something like this to your test methods:

 

static testMethod void actUnitTest4() {

		Messaging.InboundEmail emailAct = new Messaging.InboundEmail();
        Messaging.Inboundenvelope envAct = new Messaging.Inboundenvelope();
        Email_CustomerRecord CRTest = new Email_CustomerRecord();
        emailAct.plainTextBody = '<?xml version="1.0" encoding="UTF-8"?><CustomerRecord><action>delete</action><cust_num>CUSTOMER NUMBER</cust_num><name>CUSTOMER NAME</name><cust_seq>SITE ADDRESS (0 for now)</cust_seq><addr1>ADDRESS FIELD 1</addr1><addr2>ADDRESS FIELD 2</addr2><addr3>ADDRESS FIELD 3</addr3><addr4>ADDRESS FIELD 4</addr4><city>CITY</city><county>COUNTY</county><state>STATE</state><country>COUNTRY</country><zip>ZIPCODE</zip>';
        
		try {
    		String s = null;
    		CRTest.handleInboundEmail(emailAct, envAct);  // This will generate a System.XmlException...
			} catch (System.NullPointerException e) {
       			System.Assert(e.getMessage().contains('XML document structures must start and end within the same entity.'));
     }
    }

 

Cheers!

 

All Answers

vlachavlacha

Hi! I assume you are using the approach here: http://salesforceapexcodecorner.blogspot.de/2011/11/read-xml-and-insert-data-in-to.html

 

It seems that you have to implement an additional method called getDecodedString: 

 

   String getDecodedString(Xmlstreamreader reader)
      {
        return EncodingUtil.urlDecode(reader.getText(), 'UTF-8').trim();
      }
 
 
Cheers!
TechEd_ProgrammerTechEd_Programmer

That solved my error, however I am not sure that the XML is reading into the system properly. I wrote a test class for this and it is not reading in all of the elements. Ex: not reading action in.

 

Here is my test class:

/**
 * This class contains unit tests for validating the behavior of Apex classes
 * and triggers.
 *
 * Unit tests are class methods that verify whether a particular piece
 * of code is working properly. Unit test methods take no arguments,
 * commit no data to the database, and are flagged with the testMethod
 * keyword in the method definition.
 *
 * All test methods in an organization are executed whenever Apex code is deployed
 * to a production organization to confirm correctness, ensure code
 * coverage, and prevent regressions. All Apex classes are
 * required to have at least 75% code coverage in order to be deployed
 * to a production organization. In addition, all triggers must have some code coverage.
 * 
 * The @isTest class annotation indicates this class only contains test
 * methods. Classes defined with the @isTest annotation do not count against
 * the organization size limit for all Apex scripts.
 *
 * See the Apex Language Reference for more information about Testing and Code Coverage.
 */
@isTest
private class Email_TestInboundMessaging {

    static testMethod void actUnitTest1() {
        Messaging.InboundEmail emailAct = new Messaging.InboundEmail();
        Messaging.Inboundenvelope envAct = new Messaging.Inboundenvelope();
                
        emailAct.plainTextBody = '<?xml version="1.0" encoding="UTF-8"?><CustomerRecord><action>add</action><cust_num>CUSTOMER NUMBER</cust_num><name>CUSTOMER NAME</name><cust_seq>SITE ADDRESS (0 for now)</cust_seq><addr1>ADDRESS FIELD 1</addr1><addr2>ADDRESS FIELD 2</addr2><addr3>ADDRESS FIELD 3</addr3><addr4>ADDRESS FIELD 4</addr4><city>CITY</city><county>COUNTY</county><state>STATE</state><country>COUNTRY</country><zip>ZIPCODE</zip></CustomerRecord>';
        Account Act1 = new Account();
        
        Act1.Customer_Number__c = '123456';
        Act1.Name = 'Test';
        Act1.BillingStreet = 'Test';
        Act1.BillingCity = 'Test';
        Act1.BillingState = 'Test';
        Act1.BillingCountry = 'Test';
        
        insert Act1;
        
        Email_CustomerRecord CRTest = new Email_CustomerRecord();
        CRTest.handleInboundEmail(emailAct, envAct);
    }
    
    static testMethod void actUnitTest2() {
        Messaging.InboundEmail emailAct = new Messaging.InboundEmail();
        Messaging.Inboundenvelope envAct = new Messaging.Inboundenvelope();
                
        emailAct.plainTextBody = '<?xml version="1.0" encoding="UTF-8"?><CustomerRecord><action>modify</action><cust_num>123456</cust_num><name>CUSTOMER NAME</name><cust_seq>SITE ADDRESS (0 for now)</cust_seq><addr1>ADDRESS FIELD 1</addr1><addr2>ADDRESS FIELD 2</addr2><addr3>ADDRESS FIELD 3</addr3><addr4>ADDRESS FIELD 4</addr4><city>CITY</city><county>COUNTY</county><state>STATE</state><country>COUNTRY</country><zip>ZIPCODE</zip></CustomerRecord>';
        Account Act1 = new Account();
        
        Act1.Customer_Number__c = '123456';
        Act1.Name = 'Test';
        Act1.BillingStreet = 'Test';
        Act1.BillingCity = 'Test';
        Act1.BillingState = 'Test';
        Act1.BillingCountry = 'Test';
        
        insert Act1;
        
        Email_CustomerRecord CRTest = new Email_CustomerRecord();
        CRTest.handleInboundEmail(emailAct, envAct);
    }
    
    static testMethod void actUnitTest3() {
        Messaging.InboundEmail emailAct = new Messaging.InboundEmail();
        Messaging.Inboundenvelope envAct = new Messaging.Inboundenvelope();
                
        emailAct.plainTextBody = '<?xml version="1.0" encoding="UTF-8"?><CustomerRecord><action>delete</action><cust_num>CUSTOMER NUMBER</cust_num><name>CUSTOMER NAME</name><cust_seq>SITE ADDRESS (0 for now)</cust_seq><addr1>ADDRESS FIELD 1</addr1><addr2>ADDRESS FIELD 2</addr2><addr3>ADDRESS FIELD 3</addr3><addr4>ADDRESS FIELD 4</addr4><city>CITY</city><county>COUNTY</county><state>STATE</state><country>COUNTRY</country><zip>ZIPCODE</zip></CustomerRecord>';
        Account Act1 = new Account();
        
        Act1.Customer_Number__c = '123456';
        Act1.Name = 'Test';
        Act1.BillingStreet = 'Test';
        Act1.BillingCity = 'Test';
        Act1.BillingState = 'Test';
        Act1.BillingCountry = 'Test';
        
        insert Act1;
        
        Email_CustomerRecord CRTest = new Email_CustomerRecord();
        CRTest.handleInboundEmail(emailAct, envAct);
    }
}

 

vlachavlacha

it's a small mistake in the indexation (should be 17 instead of 12) :

 

messageBody = messageBody.substring(messageBody.indexof('<?xml version="1.0" encoding="UTF-8"?>'),messageBody.indexOf('</CustomerRecord>')+17);

 

TechEd_ProgrammerTechEd_Programmer

First off thank you for that. Your help is really appreciated! One more thing to get my code covereage to 100%. The last thing I do not have covered is part of the the 'modify' section:

if(Action == 'modify')
{
for(Account actList : act1)
{
actList.Name = readXMLelement(messageBody,'name');
actList.BillingStreet = readXMLelement(messageBody,'addr1') + readXMLelement(messageBody,'addr2') + readXMLelement(messageBody,'addr3') + readXMLelement(messageBody,'addr4');
actList.BillingCity = readXMLelement(messageBody,'city');
actList.BillingState = readXMLelement(messageBody,'state');
actList.BillingPostalCode = readXMLelement(messageBody,'zip');
actList.BillingCountry = readXMLelement(messageBody,'country');

}

update act1;
}

 

I know there is only something small but I cannot seem to get it.

vlachavlacha

Hi! I'm happy I can help :)

 

You are right, the problem is the list variable act1 ...it should be actl:

 

if(Action == 'modify'
			{
				for(Account actList : actl)/* use actl instead of act1 */
				{
					actList.Name = readXMLelement(messageBody,'name');
					actList.BillingStreet = readXMLelement(messageBody,'addr1') + readXMLelement(messageBody,'addr2') + readXMLelement(messageBody,'addr3') + readXMLelement(messageBody,'addr4');
					actList.BillingCity  = readXMLelement(messageBody,'city');
					actList.BillingState  = readXMLelement(messageBody,'state');
					actList.BillingPostalCode  = readXMLelement(messageBody,'zip');
					actList.BillingCountry  = readXMLelement(messageBody,'country');
				}

 

 

In order to get 100 % coverage you can add something like this to your test methods:

 

static testMethod void actUnitTest4() {

		Messaging.InboundEmail emailAct = new Messaging.InboundEmail();
        Messaging.Inboundenvelope envAct = new Messaging.Inboundenvelope();
        Email_CustomerRecord CRTest = new Email_CustomerRecord();
        emailAct.plainTextBody = '<?xml version="1.0" encoding="UTF-8"?><CustomerRecord><action>delete</action><cust_num>CUSTOMER NUMBER</cust_num><name>CUSTOMER NAME</name><cust_seq>SITE ADDRESS (0 for now)</cust_seq><addr1>ADDRESS FIELD 1</addr1><addr2>ADDRESS FIELD 2</addr2><addr3>ADDRESS FIELD 3</addr3><addr4>ADDRESS FIELD 4</addr4><city>CITY</city><county>COUNTY</county><state>STATE</state><country>COUNTRY</country><zip>ZIPCODE</zip>';
        
		try {
    		String s = null;
    		CRTest.handleInboundEmail(emailAct, envAct);  // This will generate a System.XmlException...
			} catch (System.NullPointerException e) {
       			System.Assert(e.getMessage().contains('XML document structures must start and end within the same entity.'));
     }
    }

 

Cheers!

 

This was selected as the best answer
TechEd_ProgrammerTechEd_Programmer

Once again thank you for all of your help!

kevin.chileskevin.chiles

Hello,

 

I am doing something very similar.  In my case I am writing an outbound BOD to fire my xml and pick up an object and its related objects (related list records).  To this end I am writing my class to handle the lifting but I am running into a few walls.  For one, I keep getting an error message stating that my field does not exist, but in this case I am not sure why.  The items do reside object.    Here is my posted code:

 

public with sharing class CustomSPAPartyMasterHandler {
    public static final String relRegexName = '[<][a-zA-Z0-9\\s="^#!{}_.]*';
    public static final String endChild = '</';
    public static final String endNode = '>';
    public static final String firstMatchedChild = '(.)*?';
    public static final String regexObjField = '[{][!][\\w.]+[}]';
    public static final String emptyString = '';
    
    //Empty Constructor
    public CustomSPAPartyMasterHandler() {
        
    }
    
    //Generate XML for multiple SPA Discounts
    public static String handleSPADiscount() {
        String relName = 'relationName="#SPA_Discount__r:SPA_Discount__c#"';
        String message = '<?xml version="1.0"?><ProcessSPA><SPA><SPA_Discount relationName="#SPA_Discount__r:SPA_Discount__c#"><Name>{!SPA_Discount__r.Name}</Name><Catalog>{!SPA_Discount__r.Catalog__c}</Catalog><AWC Free Freight>{!SPA_Discount__r.AWC_F_Frt__c}</AWC Free Freight><Reason>{!SPA_Discount__r.Reason__c}</Reason><CompetitorName>{!SPA_Discount__r.Competitor_Name__c}</CompetitorName><SalesCommisionSplit>{!SPA_Discount__r.Sales_Commision_Split__c}</SalesCommisionSplit></Optionforsplitchoice>{!SPA_Discount__r.Option_For_SPlit_Choice__c}</Optionforsplitchoice><Shiptorepfirstname>{!SPA_Discount__r.Ship_To_Rep_First_Name__c}</Shiptorepfirstname><Shiptolastname>{!SPA_Discount__r.Ship_To_Rep_Last_Name__c}</Shiptolastname><Discounttype>{!SPA_Discount__r.Discount_Type__c}</Discounttype><Netprice>{!SPA_Discount__r.Net_Price__c}</Netprice><Discountlevel>{!SPA_Discount__r.Discount_Level__c}</Discountlevel><Spiff>{!SPA_Discount__r.Spiff__c}</Spiff><Netexpectedordervalue>{!SPA_Discount__r.Net_Expected_Order_Value__c}</Netexpectedordervalue><Minimumorderquantity>{!SPA_Discount__r.Minimum_Order_Quantity}</Minimumorderquantity><PartNumber>{!SPA_Discount__r.Part_Number__c}</PartNumber></SPAdiscount></ProcessSPA>';
        String SPADiscountXML = getChildXml(message, relName);
        List<SPA_Discount__c> SPADiscountLst = null;
    
        String spQuery= 'Select  sp.Id, ' +
                    ' (Select Name, SPA_Agreement__c,Catalog__c, Reason__c, Competitor_Name__c, Sales_Commision_Split__c, Option_For_Split_Choice__c, Ship_To_Rep_First_Name__c, Ship_To_Rep_Last_Name__c, AWC_F_Frt__c, Part_Number__c,Minimum_Order_Quantity,Net_Expected_Order_Value__c,Spiff__c,Discount_Level__c,Net_Price__c,Discount_Type__c,Ship_To_Rep_Last_Name__c' + 
                    ' From SPA_Discount__r) From SPA__c sp';
        list<SPA__c> lstSPA = database.query(spQuery);
        if(lstSPA != null && lstSPA .size()  > 0 ){
            SPA__c spd= lstSPA [0];
            SPADiscountLst = lstSPA[0].SPA_Discount__r; 
        }
        if(SPADiscountLst !=null && !SPADiscountLst.isEmpty()) {
            List<String> fields = getFieldsFromXmlPart(SPADiscountXML);
            if(fields != null && !fields.isEmpty()) {
                String childXml;
                String allChildXml = '';
                for(SPA_Discount__c spadiscount: SPADiscountLst){
                    childXml = SPADiscountXML;
                    for(Integer cnt=0;cnt<fields.size();cnt++){
                        String objName = fields[cnt].split('\\.')[0];
                        string fldname = fields[cnt].split('\\.')[1];       
                        childXml = childXml.replace('{!'+fields[cnt]+'}',
                            htmlEncode(String.valueOf(personBillingAddress.get(fldname))));
                    }
                    allChildXml = allChildXml + childXml;
                }
                
                if(allChildXml!='')
                {
                    allChildXml = allChildXml.replace(' ' + relName, emptyString);
                    message = message.replace(SPADiscountXML, allChildXml);
                }
            }
        }
        return message;
    }
    
    //get SPA_Discount from the main XML
    public static String getChildXml(String message, String searchString) {
        Pattern patt;
        Matcher mat;
        string regex = relRegexName+searchString+endNode;
        patt =Pattern.compile(regex);
        system.debug('--- search string>>' + regex);
        mat=patt.matcher(message);
        String elm;
        if(mat.find()){
            elm = mat.group(0);
        }
        System.debug('mat.grp>> '+ elm);
        if(elm!=null && elm!=''){
            elm=elm.replace('>','/>');
            DOM.Document domDoc = new DOM.Document();
            domDoc.load(elm);
            Dom.XmlNode rootXmlNode = domDoc.getRootElement();
            String rootNodeName = rootXmlNode.getName();
            System.debug(rootNodeName);
            String endTag = endChild + rootNodeName + endNode;
            regex = relRegexName+searchString+endNode+firstMatchedChild+endTag;
            System.debug('>>>full regex: '+regex);
            patt =Pattern.compile(regex);
            mat=patt.matcher(message);
            if(mat.find()){
                elm = mat.group(0);
            }
            System.debug('mat.grp>> '+ elm);
        }
        return elm;
    }
    
    //get fields from xml
    public static List<String> getFieldsFromXmlPart(String xml) {
        Pattern patt;
        Matcher mat;
        List<String> fields = new List<String>();
        patt = Pattern.compile(regexObjField);
        System.debug('xml>>>>' + xml);
        if(xml != '' && xml != null) {
            mat = patt.matcher(xml);
            while(mat.find()){
                String fieldName =  mat.group();
                fieldName = fieldName.replace('{!','');
                fieldName = fieldName.replace('}','');
                fields.add(fieldName);
            }
        } 
        return fields;
    }
    
    public static string htmlEncode(String str) {
        if(!StringUtil.isNull(str))
            return str.Replace('&', '&amp;').Replace('<', '&lt;').Replace('>', '&gt;').Replace('\"', '&quot;').Replace('\'', '&apos;');
        else return str;
    }
}