[SalesForce] Convert SObject.toString output to a CSV value line (Regexp..)

I am running into ToManyStatements Limits while creating a CSV blob for with the following code

for(SObject row: list) {
  for(String fieldName : allFields) {
    line += fieldName + ',';
  }
  csvAsString += '\n' + line;
}

What I need is probably a clever regexp that replaces Salesforce SObject String format like:

CustomObject__c: {field__c=value, field__c=value,...}

to a csv value line like:

value,value,value,....

Then I could change my code like this:

for(SObject row: list) {
  csvAsString += '\n' + row.doRegExpMagic();
}

To create the first line of the CSV I probably need another regexp to extract the field list

field1__c,field2_c,...

from my SOQL query

SELECT field1__c,field2_c,... FROM CustomObject__c ...

All you Regexp gurus out there.. please help 😉

Best Answer

Creating a CSV file blob in Apex

My recommendations:

  1. use Batch Apex, passing in the initial Lists as stateful variables using Database.Stateful, and then creating a CSV row out of each SObject during the Batch Apex execute method. In the finish method of your Batch Apex, send an email containing the generated CSV. Using Batch Apex to do the CSV row generation will avoid the Too Many Script Statements error, since Batch Apex limits are reset for each Batch Apex execute method invocation. The resultant CSV file can be stored as a String instance variable, and converted to a Blob in the finish method, then emailed to the user requesting the export.
  2. Create the blob client-side using JavaScript (no script statement limits there!), and sent to Apex using one of the following
    • JS Remoting
    • VF ActionFunction
    • HTTP POST Request

Example of Batch Apex

Here is some sample code for the JavaScript method:

  1. To create CSV fields in JavaSCript, here's some a method that will work:

    // Escapes a String such that it can be used in a CSV cell
    // Based on Apache Commons StringEscapeUtils library
    // (see Wikipedia and RFC 4180)
    // Parameters:
    //  input - the input CSV column String, may be null
    //  Returns:
    //  the input String, enclosed in double quotes 
    //      if the value contains a comma, newline or double quote, 
    //      '' if null string input.
    //  Also, any double quote characters in the value
    // are escaped with another double quote.
    
    var QUOTE = '"',
        ESCAPED_QUOTE = "\"\"",
        CHARS_THAT_MUST_BE_QUOTED = [ ',' , '"' , '\n' ];
    
    var escapeCSVCell = function(c) {
       if ( c.indexOf( QUOTE ) > -1 ) c = c.replace(/"/g, ESCAPED_QUOTE );
          $.each(CHARS_THAT_MUST_BE_QUOTED,function(i,char){
             if (c.indexOf(char) > -1) {
                c = QUOTE + c + QUOTE;
                return false;
             }
          });
       return c;
    }
    
    // Call escapeCSVCell on each CSV field
    

Parsing CSV files in Apex

Number One: Do not roll your own CSV Parser: use someone else's! There are a lot of complexities to parsing IETF RFC 4180 - compliant CSV files that you will uncover, one after another, if you try to do roll your own parser. Trust me, not worth it.

And guess what? You don't have to!

Marty Chang has done the Salesforce/Apex developer commmunity a huge service by writing a rock-solid, IETF RFC 4180 compliant CSV Parser FOR APEX!!!

Here is the link to download the 2 files he wrote for this purpose, CSVParser and CSVReader. I've used it before, trust me, it handles everything, even newlines embedded in your CSV fields (try writing that yourself --- NOT straightforward.)

Here's how to use it:

// (Only necessary if your CSV file starts as a string)
Blob csvBlob = Blob.valueOf(csvString);

// Convert your csv file into a list of CSV fields
List<List<String>> fieldsByLine = CSVParser.readIETFRFC4180CSVFile(csvBlob);

Yeah, it's that easy!

His solution uses some good RegEx to bypass the "too many statements" limit.

Creating SObject records from CSV Rows in Apex

As far as creating records from these CSV rows, i've done this before using Batch Apex. Convert your CSV files into rows during the Batch Apex job and create rows one-by-one in the execute method. Also, shameless plug, there's actually an IETF RFC 4180-compliant CSV Import Wizard built-in to Skuid, so if you are open to using an external library, why roll your own parser?

Parsing CSV Rows in Apex

Winter 13 (API v26) has some awesome methods for escaping/unescaping CSV fields:

  • String.escapeCsv(String textToMakeIntoACSVField)
  • String.unescapeCsv(String csvField)
Related Topic