How to Import Salesforce Custom Metadata Records using CSV/JSON
Tags: salesforce, apex, json, metadata
Published: 2020-08-27 13:17:10

Importing Salesforce Custom Metadata Records can be really tricky. Forget about using import wizard or dataloader to do that. These tools are amazing but simply do not support operations on Custom Metadata Records.

If you perform a quick search on Google about this theme, you probably will, at certain point of your search, get into an guide that will tell you to use the **Custom Metadata Loader Loader**. This tool may be useful if you have just s few records to import, and none of your records has an special character on it, otherwise you certainly will fail after a lot of bugs and errors.

I've developed an class using the official Salesforce Documentation as guidelines to achieve that requirement, and I'll help you to import your Salesforce Custom Metadata Records following steps below:

##1:Prepare your Data##
You can use any online service that converts **csv** into **json**. I recommend the service below:
https://csvjson.com/csv2json
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/sl3kjvgn3yg27iyz57ct.jpg)

Simply upload your **CSV** file containing the records you want to upload, click on **CONVERT** and copy the generated **JSON**.

##1.1:Make your JSON inline##
What we have to do next, is to parse that **JSON** into a single line, you can easily do that accessing the following link:
https://jsonformatter.curiousconcept.com/

Simply paste your **JSON**, select **COMPACT** on the **JSON Template Option** and click **PROCESS**. Copy the generated compact **JSON** and keep that safe for later steps.

##2:Create the mdtImport Apex Class using the code below##
```
//author: Roger Rosset
//description: Upload custom metadata records using CSV/JSON

public class mdtImport  {    
    public static void insertMetadata(String metaDataTypeName, String jsonString){
        try{
            Integer count = 1;
            Metadata.DeployContainer mdContainer = new Metadata.DeployContainer();
            JSONCsvTemplate csv = (JSONCsvTemplate)JSON.deserialize(jsonStringMdt(jsonString), JSONCsvTemplate.class);
            for(JSONCsvTemplate.mdtRecords item : csv.data.mdtRecordsList){
            //Sets the custom metadata type you'll insert your records on
            
            //If you're using namespaces on your org set it here
            String nameSpacePrefix ='';                                     
            Metadata.CustomMetadata rec =  new Metadata.CustomMetadata();
            String label = 'Record '+count;                      
            rec.fullName = nameSpacePrefix+metaDataTypeName+'.'+label;
            rec.label = label; 
            
            //Sets the custom metadata custom fields to be inserted
            /*
            * Use the template below to setup any fields you want to:
            *    
            Metadata.CustomMetadataValue fieldX = new Metadata.CustomMetadataValue();     //New instance
            fieldX.field = 'Custom_Field_Name__c';              //Custom Metadata Field you want to fill
            field1.value = item.JSON_Matching_Key_Value;        //The matching key value on the wrapper
            rec.values.add(fieldX);                             //adds the value and the matching field
            *
            */
            
            Metadata.CustomMetadataValue field1 = new Metadata.CustomMetadataValue();
            field1.field = 'SubGroup__c';
            field1.value = item.SubGroup;
            rec.values.add(field1);            
            Metadata.CustomMetadataValue field2 = new Metadata.CustomMetadataValue();
            field2.field = 'Description__c';
            field2.value = item.Description;
            rec.values.add(field2);                       
            Metadata.CustomMetadataValue field3 = new Metadata.CustomMetadataValue();
            field3.field = 'keyId__c';
            field3.value = item.keyId;
            rec.values.add(field3);                        
            mdContainer.addMetadata(rec);  
            count++;
    } 
            Id jobId = Metadata.Operations.enqueueDeployment(mdContainer, null);
            system.debug('Container>>'+mdContainer);
            system.debug('Id>>'+jobId);
            return;
        }
        
         	catch(Exception ex){             
            system.debug('Error on insert');
            system.debug('Error:'+ex.getMessage());
        }

    }
    private static String jsonStringMdt(String jsonString){
        String resultJson = '{"data":{"mdtRecordsList":'+jsonString+'}}';
        return resultJson;
    }

    private class JSONCsvTemplate{
        private class Data{
            private List<mdtRecords> mdtRecordsList;
    }
        private class mdtRecords{
            private String keyId;
            private String SubGroup;
            private String Description;
        }
        private Data data;
    }
}

```
As you can see you can execute the methods **insertMetaData** passing the **metaDataTypeName** and **jsonString** as attributes, where **metaDataTypeName** is the API Name of your Custom Metadata Type (***ending with __mdt***) and **jsonString** is your inline JSON that you've copied on the step 1.1, But first you have to adjust the attributes on the JSONCsvTemplate>mdtRecords to fit like your csv database.

##3:Adjust the Wrapper class to your requirements##

```
    private class JSONCsvTemplate{
        private class Data{
            private List<mdtRecords> mdtRecordsList;
    }
        private class mdtRecords{
            private String keyId;
            private String SubGroup;
            private String Description;
        }
        private Data data;
    }
}
```
As you can see above, we are uploading records that has 3 columns:
 -keyId
 -SubGroup
 -Description

These values matches exactly as my csv database headers, and you can add, delete or modify the template to meet your criteria. Keep in mind that your csv database must fit the wrapper class, not only about the field names, but also the primitive data types (Integer, String, Etc.)

**Csv Database used:**
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ruf4a7c9bv3c5xrum0u6.jpg)

##4:Execute the method##
After following all the previous steps and making sure you've created and saved the class on your Salesforce Org, all you have to do next is to execute the **insertMetadata**
Get your Custom Metadata Type API Name (ending with __mdt) and your JSON String generated in the step 1.1, and execute the method on the Apex Anonymous Window inside the developer console (CTRL+E)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/l6jmkf5h31pi5wzgzvgi.jpg)

Keep in mind that your JSON String may be really big, and to avoid problems, it's highly recommended you to place the method template and quotes before pasting the "super string" on the parameter inside the method execution, as below:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/w8r365lj7ab0pfxssje4.jpg)
After preparing the the method, simply paste your big JSON String inside the quotes, and click execute.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/vjqv50nioanayas6io2l.jpg)

If you have checked the Open Log option, you should be taken to the log of this execution, where you can find the ID of your enqueued deploy, and check it's status on the deployment status of Salesforce on:
**SETUP > DEPLOY > DEPLOYMENT STATUS**

##Attention##
I've already tested the deploy of 600 records per execution, try to respect that limit to avoid errors.

Feel free to modify the code as you need to, keeping the credits if possible.

Hope it be useful.

Regards,
Roger Rosset

Based documentation:
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_class_Metadata_Operations.htm