Skip to main content

How to Process JSON Payloads in Salesforce SOAP Web Services with Apex

In plain words: An Apex SOAP Web Service allows external legacy enterprise systems that communicate via XML/WSDL contracts to send flexible, stringified JSON payloads into Salesforce, where Apex can parse the data and create or update records.

While modern integrations typically favor REST architectures, many enterprise systems still require strict SOAP endpoints governed by WSDL files. Passing a JSON string inside a custom Apex SOAP endpoint bridges the gap between legacy XML transport protocols and flexible, modern JSON payloads.

Prerequisites

  • A Salesforce Developer Edition, Scratch Org, or Sandbox environment.
  • Basic understanding of custom Apex classes, global access modifiers, and DML operations.
  • Familiarity with JSON deserialization techniques and SOAP/WSDL integration tools (like Postman or SoapUI).

Step 1: Define the SOAP Endpoint & Data Structures

To expose an Apex method as a custom SOAP web service, the class must be declared as global and the method marked with the webservice keyword. We also define strongly-typed wrapper classes to deserialize the incoming JSON string efficiently:

global with sharing class JSONSOAPService {

    // Wrapper class matching the JSON payload structure
    public class ContactPayload {
        public String firstName;
        public String lastName;
        public String email;
        public String phone;
        public String companyName;
    }

    // Response structure returned to the SOAP client
    global class ServiceResponse {
        webservice Boolean isSuccess;
        webservice String recordId;
        webservice String message;
    }

    webservice static ServiceResponse processContactJSON(String jsonString) {
        ServiceResponse response = new ServiceResponse();

        if (String.isBlank(jsonString)) {
            response.isSuccess = false;
            response.message = 'Error: JSON payload cannot be empty.';
            return response;
        }

        try {
            // Deserialize JSON into the Apex ContactPayload object
            ContactPayload payload = (ContactPayload) JSON.deserialize(
                jsonString, 
                ContactPayload.class
            );

            // Basic field validation
            if (String.isBlank(payload.lastName)) {
                response.isSuccess = false;
                response.message = 'Validation Error: Last Name is required.';
                return response;
            }

            // Create and insert the Contact record
            Contact newContact = new Contact(
                FirstName = payload.firstName,
                LastName = payload.lastName,
                Email = payload.email,
                Phone = payload.phone
            );

            // Enforce user-mode security for DML operations
            Database.SaveResult sr = Database.insert(newContact, AccessLevel.USER_MODE);

            if (sr.isSuccess()) {
                response.isSuccess = true;
                response.recordId = sr.getId();
                response.message = 'Contact created successfully.';
            } else {
                response.isSuccess = false;
                response.message = 'DML Error: ' + sr.getErrors()[0].getMessage();
            }

        } catch (JSONException jsonEx) {
            response.isSuccess = false;
            response.message = 'Malformed JSON format: ' + jsonEx.getMessage();
        } catch (Exception ex) {
            response.isSuccess = false;
            response.message = 'Unexpected Server Error: ' + ex.getMessage();
        }

        return response;
    }
}
Warning Trap: A custom class exposing a webservice method must be defined as global. If you declare the class as public, Salesforce will prevent WSDL generation. Additionally, methods marked with webservice must be static.

Step 2: Generate the WSDL for External Systems

Steps to Export the Service WSDL:
  • Deploy JSONSOAPService.cls to your Salesforce org.
  • In Salesforce Setup, enter Apex Classes in the Quick Find box.
  • Locate JSONSOAPService and click the WSDL link next to the class name.
  • Save the generated XML file as JSONSOAPService.wsdl and import it into your SOAP integration tool (such as SoapUI or enterprise middleware like MuleSoft).

Step 3: Sample Request and Response Payloads

Here is how the SOAP XML envelope wraps the inner JSON string during transit:

Sample SOAP Request Envelope:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:json="http://soap.sforce.com/schemas/class/JSONSOAPService">
   <soapenv:Header>
      <json:SessionHeader>
         <json:sessionId>YOUR_SALESFORCE_SESSION_OR_OAUTH_TOKEN</json:sessionId>
      </json:SessionHeader>
   </soapenv:Header>
   <soapenv:Body>
      <json:processContactJSON>
         <json:jsonString><![CDATA[
            {
               "firstName": "Alex",
               "lastName": "Morgan",
               "email": "alex.morgan@example.com",
               "phone": "555-0199"
            }
         ]]></json:jsonString>
      </json:processContactJSON>
   </soapenv:Body>
</soapenv:Envelope>
360 Architecture Summary:
  • Structured Deserialization: Prefer JSON.deserialize() with dedicated Apex wrapper classes over low-level JSONParser token loops for maintainable, type-safe code.
  • CDATA Protection: Wrap raw JSON inside XML <![CDATA[ ... ]]> blocks to prevent special characters (&, <, >) from invalidating the SOAP XML envelope.
  • Security Enforcement: Always execute database operations with AccessLevel.USER_MODE to respect Field-Level Security and sharing settings.
  • SOAP vs. REST Choice: Use this pattern when external middleware cannot be upgraded to REST but demands flexible JSON payloads.
Core Takeaway: Embedding stringified JSON within a global Apex SOAP web service (webservice static) combines the strict contract requirements of enterprise SOAP protocols with the schema flexibility of JSON.