[SalesForce] Compatible Schema.DisplayType For SObject Put

I'm trying to write a method for working with sObject.put(field, value) statements in a loop where the Schema.DisplayType of the fields received varies.

The goal is to have the method return the name of a compatible DisplayType you can convert the value to in order to have it assign properly without breaking the statement. Although I could figure this out by trial and error, I was wondering if anyone knew (off the top of their head) or could direct me to (since I haven't been able to find it online) a list of compatible types. As an example, in my method below, I've grouped String, TextArea, Id, Picklist, Reference, Phone, Email and URL together because if you pass a string to fields of any of these fields, it will accept the value, but I'm wondering if I can get away with further grouping. E.g. could I put Boolean in that list too, or are some of the Boolean field values 0 and 1, and passing a string would be problematic? Or if I pass an Integer field a Decimal value, will it just truncate it, or will an error be thrown?

/** SCHEMA DISPLAY TYPE COMPATIBILITY
 * @param field <Scehma.SObjectField
 * @return <String>
 **/
public static String schemaDisplayTypeCompatibility(Schema.SObjectField field){
    DescribeFieldResult fielddesc = field.getDescribe();
    DisplayType fieldType = fielddesc.getType();
    Boolean canUpdate = fielddesc.isUpdateable();

    if(canUpdate == FALSE)
        return 'NOT UPDATEABLE';    
    if(fieldType == Schema.DisplayType.String || 
        fieldType == Schema.DisplayType.TextArea ||
        fieldType == Schema.DisplayType.Id ||
        fieldType == Schema.DisplayType.Picklist ||
        fieldType == Schema.DisplayType.Reference ||
        fieldType == Schema.DisplayType.Phone ||
        fieldType == Schema.DisplayType.Email ||
        fieldType == Schema.DisplayType.URL)
            return 'String';
    if(fieldType == Schema.DisplayType.Currency ||
       fieldType == Schema.DisplayType.Double)
        return 'Decimal';
    if(fieldType == Schema.DisplayType.Integer)
        return 'Integer';
    if(fieldType == Schema.DisplayType.Boolean)
        return 'Boolean';
    if(fieldType == Schema.DisplayType.DateTime)
        return 'DateTime';    
    if(fieldType == Schema.DisplayType.Date)
        return 'Date';            
    if(fieldType == Schema.DisplayType.Time)
        return 'Time';
    if(fieldType == Schema.DisplayType.Combobox)
        return 'Combobox';    
    if(fieldType == Schema.DisplayType.MultiPicklist)
        return 'MultiPicklist';
   if(fieldType == Schema.DisplayType.EncryptedString)
        return 'EncryptedString';
   if(fieldType == Schema.DisplayType.DataCategoryGroupReference)
        return 'DataCategoryGroupReference';        
   if(fieldType == Schema.DisplayType.base64 )
        return 'base64 ';               
   //if(fieldType == Schema.DisplayType.anytype)
   //     return 'anytype';                                  
    return 'UNKNOWN';    
}

Best Answer

As @KeithC mentions, Schema.SoapType is much simpler to work with.

Disclaimers

Maybe there's a simpler way than what I outline below. I didn't see the project through to completion so I can't guarantee it will be worth the effort to implement. I am also unsure of where you are pulling your inputs from. Also you may want to handle null values, but I ommitted such logic for simplicty's sake.

Anyway, the way I've done this in the past is to map SoapType to a Type of converter.

public with sharing class PutReference
{
    final SObjectField field;
    final IConverter converter;
    public PutReference(SObjectField field)
    {
        this.field = field;
        SoapType soapType = field.getDescribe().getSoapType();
        this.converter = converters.get(soapType).newInstance();
    }
    public SObject putTo(SObject record, String input)
    {
        record.put(field, converter.getValue(input));
    }

    static final Map<SoapType, Type> converters = new Map<SoapType, Type>();
    {
        SoapType.DOUBLE, DoubleConverter.class
        // SoapType.DATE, DateConverter.class
        // etc
    }
    public interface IConverter { Object getValue(String input); }
    public class DoubleConverter implements IConverter
    {
        Object getValue(String input)
        {
            return Double.valueOf(input);
        }
    }
    // public class DateConverter
    // etc.
}

A bonus of this approach is that you could get more sophisticated with your conversion logic. For example:

public class BooleanConverter implements IConverter
{
    public Object getValue(String input)
    {
        if (input == '0') return false;
        if (input == '1') return true;
        return Boolean.valueOf(input);
    }
}
Related Topic