0

I am trying to test a wrapper class for a platform event/platform event trigger flow and I am not sure how to add a contact record Id I created earlier in the test class to the JSON string.

Update The reason why Person Id and Volgistic Id are arrays is due to the platform event receiving the information from an automated data pipeline getting this data from external systems that do not have the same data quality standards as Salesforce. Salesforce is acting as the single source of truth so this singular contact has a field where we have the Person Ids and Volgistic Ids listed for the user of the system to see and determine which one is the correct one for this contact.

Also this was my first time using platform events and wrapper classes so my first order of business once this is working end-to-end is to rename the variables so they do not have the "__c".

Below is my JSON string I am sending to my wrapper class. I would be adding the contact record Id to the "Id" field.

'{\"CreatedById\": \"0052g000003G926AAC\",\"CreatedDate\": \"1693078705\",\"updatedContactsJson\":[{\"Id\":\"0032g00000Ukx9bAAB\",\"Person_ID__c\":[\"55298146\",\"55298147\"],\"First_volunteer_date__c\":\"2023-08-29\", \"Foster_End_Date__c\":\"2023-08-29\", \"Foster_Start_Date__c\":\"2023-08-29\", \"Last_volunteer_date__c\":\"2023-08-29\", \"Total_Volunteer_Hours__c\":\"106.5\", \"Volunteer_Status__c\":\"Active\", \"Volgistics_Id__c\":[\"9277188\",\"5555555\"]}]}'

Readable version

{
   "CreatedById":"0052g000003G926AAC",
   "CreatedDate":"1693078705",
   "updatedContactsJson":[
      {
         "Id":"0032g00000Ukx9bAAB",
         "Person_ID__c":[
            "55298146",
            "55298147"
         ],
         "First_volunteer_date__c":"2023-08-29",
         "Foster_End_Date__c":"2023-08-29",
         "Foster_Start_Date__c":"2023-08-29",
         "Last_volunteer_date__c":"2023-08-29",
         "Total_Volunteer_Hours__c":"106.5",
         "Volunteer_Status__c":"Active",
         "Volgistics_Id__c":[
            "9277188",
            "5555555"
         ]
      }
   ]
}

This is what my ContactWrapper Class looks like:

 * @description       : Wrapper class for the JSON response we receive from the pipeline
 * @author            : Diego Delgado
 * @group             : 
 * @last modified on  : 02-17-2024
 * @last modified by  : Diego Delgado
**/
public with sharing class ContactWrapper {
  
        public List<Contact> updatedContactsJson;

    /**
     * Parse method for ContactWrapper object
     * @param json 
     * @return  `ContactWrapper`
     */
    public static ContactWrapper parse(String json) {
        return (ContactWrapper) System.JSON.deserialize(json, ContactWrapper.class);
    }
}
2
  • Can you post your wrapper class and how you're sending your JSON string to it?
    – fred
    Commented Feb 16 at 23:47
  • Normally the easiest way to do this is to just have an Apex Type that conforms to the JSON structure and you then just instantiate it and then serialize to put into the Platform Event payload. But you have Json fields that are invalid apex variable names (they have double underscores) making the construction of the Apex Type "challenging" (not to mention JSON arrays as values for custom field names). Do you really need the JSON fields to match custom field names?
    – cropredy
    Commented Feb 17 at 0:39

1 Answer 1

1

As mentioned in a comment by @cropredy, it would be normal to have Apex classes that mimic the JSON structure and use these for typed deserialization and serialization. However, there are restrictions on what the properties in these classes can look like, and as mentioned you cannot include double underscores in Apex property names.

The solution, therefore, is to use so-called "untyped deserialization". This is where you call JSON.deserializeUntyped.

In essence you would:

  • Deserialize, untyped, the JSON string into a Map<String, Object>,
  • Get the updatedContactsJson Object from the map and cast this to a List<Object>,
  • Iterate the list casting each entry to Map<String, Object> and put the required Contact ID as the map's Id,
  • Serialize the outermost Map<String, Object> for use in your test.

The following illustrates what you might do:

String jsonString = '{"CreatedById":"0052g000003G926AAC","CreatedDate":"1693078705","updatedContactsJson":[{"Id":"0032g00000Ukx9bAAB","Person_ID__c":["55298146","55298147"],"First_volunteer_date__c":"2023-08-29","Foster_End_Date__c":"2023-08-29","Foster_Start_Date__c":"2023-08-29","Last_volunteer_date__c":"2023-08-29","Total_Volunteer_Hours__c":"106.5","Volunteer_Status__c":"Active","Volgistics_Id__c":["9277188","5555555"]}]}';

// Deserialize JSON string into Map<String, Object>
Map<String, Object> jsonMap = (Map<String, Object>) JSON.deserializeUntyped(jsonString);

// Get the 'updatedContactsJson' object from the map
List<Object> updatedContacts = (List<Object>) jsonMap.get('updatedContactsJson');

// Iterate over the list and update each entry
for (Object contact : updatedContacts) {
    Map<String, Object> contactMap = (Map<String, Object>) contact;
    // Replace the 'Id' value with the defined Contact ID
    contactMap.put('Id', 'your_defined_contact_id_here');
}

// Reserialize the outermost Map<String, Object>
String updatedJsonString = JSON.serialize(jsonMap);
System.debug(updatedJsonString);

Obviously, replace 'your_defined_contact_id_here' with the actual Contact ID you want.

Note that the whole JSON will retain its structure though the properties at each level will change order due to the way the JSON API works.

Update:

With the addition of your "wrapper class" in the question, the following points must be noted:

  1. While you can include SObjects in the deserialization process, these need to have valid content.
  2. Salesforce does not support array-based fields (such as your Person_ID__c and Volgistics_Id__c values in your JSON).

For this to be valid, the "Person ID array" would actually need to be a related list of other SObjects, and be of the form:

"Person_IDs__r": [
  {
    "Id": "..."
  },
  {
    "Id": "..."
  }
]

I have assumed the relationship name, and also that your values for the person IDs would actually be valid SObject IDs (unlike the values you currently show in your example).

If the updatedContactsJson cannot be valid Contact SObject data you cannot rely on using SObjects in your wrapper and, indeed, cannot use typed deserialization at all.

6
  • Thank you @PhilW !. When I try and pass the updatedJSONString to my wrapper class I get this error "System.JSONException: Cannot deserialize instance of string from START_ARRAY value [line:1, column:26]". When I log the updatedJsonString everything looks correct just that the CreatedById and CreatedDate sections are at the bottom of the payload when before they were at the beginnning. Commented Feb 17 at 17:19
  • This is what my ContactWrapper looks like: public with sharing class ContactWrapper { public List<Contact> updatedContactsJson;public static ContactWrapper parse(String json) { return (ContactWrapper) System.JSON.deserialize(json, ContactWrapper.class); } } Commented Feb 17 at 17:21
  • If the outer most JSON is to be deserialized into your wrapper, the wrapper needs the CreatedById and CreatedDate properties as well. The next problem is you have treated Person_ID__c as an array (the same for Volgistics_Id__c) but SObjects do not have array-based fields. Related lists use different naming for the relationships, such as Person_IDs__r, and then these must be arrays of SObjects, not strings.
    – Phil W
    Commented Feb 17 at 18:00
  • This platform event is digesting information from an automated data pipeline that is pulling data from two external CRM applications. Salesforce is acting as the single source of truth. These applications are not as polished as Salesforce when it comes to data integrity standards so there can, and most likely will, be duplicate Ids for the same person. That is why those two Id fields are arrays. In the flow that is processing this platform event we are dumping the Id's into a text field so the end user can see possible duplicate Ids in other systems for the Contact. Commented Feb 18 at 14:56
  • And yes once this works end-to-end I am going to change the variable names so they don't have the "__c". Commented Feb 18 at 15:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .