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.clsto your Salesforce org. - In Salesforce Setup, enter Apex Classes in the Quick Find box.
- Locate
JSONSOAPServiceand click the WSDL link next to the class name. - Save the generated XML file as
JSONSOAPService.wsdland 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-levelJSONParsertoken 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_MODEto 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.