Only some types work as Apex map keys from Visualforce

The “Referencing Apex Maps and Lists” section of the Visualforce Developer’s Guide provides examples of how to reference data held in an Apex map in the controller from a Visualforce page. I haven’t found examples where the key values used have been other than strings or integers and the documentation is not explicit. But now that there is support for Non-primitive Types in Map Keys and Sets, I thought it would be worth trying a DateTime value as a map key in a page I have that heavily uses DateTime objects.

But DateTime as a map key type does not work from Visualforce:

Incorrect parameter type for subscript. Expected java.lang.Class, received DateTime

To get some sense of what does and what does not work I created the controller and page listed at the end of this post (both set to API 26.0). I found two further failing map key types.

Time as a map key type results in:

Unsupported type shared.xml.soap.Time encountered.

A custom Apex class as a map key type results in:

Map key Custom:[value=987] not found in map

i.e. value semantics are not supported (but the generally less useful reference semantics are).

I’ll create a case with Salesforce about the types that don’t work.

<apex:page controller="MapTestController">
  <h1>Map Test</h1>
  <br/><apex:outputText value="{!stringKeyMap[stringKey]}"/>
  <br/><apex:outputText value="{!integerKeyMap[integerKey]}"/>
  <br/><apex:outputText value="{!longKeyMap[longKey]}"/>
  <br/><apex:outputText value="{!decimalKeyMap[decimalKey]}"/>
  <br/><apex:outputText value="{!dateKeyMap[dateKey]}"/>
  <br/><apex:outputText value="{!customByReferenceKeyMap[customByReferenceKey]}"/>
  <!--
  <br/><apex:outputText value="{!timeKeyMap[timeKey]}"/>
  -->
  <!--
  <br/><apex:outputText value="{!dateTimeKeyMap[dateTimeKey]}"/>
  -->
  <!--
  <br/><apex:outputText value="{!customByValueKeyMap[customByValueKey]}"/>
  -->
</apex:page>
public class MapTestController {

    public String stringKey {
        get {
            return 'key';
        }
    }
    public Map<String, String> stringKeyMap {
        get {
            return new Map<String, String>{stringKey => 'string value'};
        }
    }
    
    public Integer integerKey {
        get {
            return 1234;
        }
    }
    public Map<Integer, String> integerKeyMap {
        get {
            return new Map<Integer, String>{integerKey => 'integer value'};
        }
    }
    
    public Long longKey {
        get {
            return 1234;
        }
    }
    public Map<Long, String> longKeyMap {
        get {
            return new Map<Long, String>{longKey => 'long value'};
        }
    }
    
    public Decimal decimalKey {
        get {
            return 3.25;
        }
    }
    public Map<Decimal, String> decimalKeyMap {
        get {
            return new Map<Decimal, String>{decimalKey => 'decimal value'};
        }
    }
    
    public Date dateKey {
        get {
            return Date.newInstance(2012, 1, 1);
        }
    }
    public Map<Date, String> dateKeyMap {
        get {
            return new Map<Date, String>{dateKey => 'date value'};
        }
    }
    
    public Time timeKey {
        get {
            return Time.newInstance(8, 20, 30, 0);
        }
    }
    public Map<Time, String> timeKeyMap {
        get {
            return new Map<Time, String>{timeKey => 'time value'};
        }
    }
    
    public DateTime dateTimeKey {
        get {
            return DateTime.newInstance(2012, 1, 1, 0, 0, 0);
        }
    }
    public Map<DateTime, String> dateTimeKeyMap {
        get {
            return new Map<DateTime, String>{dateTimeKey => 'date time value'};
        }
    }
    
    public class Custom {
        private Integer value;
        public Custom(Integer value) {
            this.value = value;
        }
        public Boolean equals(Object other) {
            return this.value == ((Custom) other).value;
        }
        public Integer hashCode() {
            return value;
        }
    }
    
    private static final Custom customInstance = new Custom(321);
    public Custom customByReferenceKey {
        get {
            return customInstance;
        }
    }
    public Map<Custom, String> customByReferenceKeyMap {
        get {
            return new Map<Custom, String>{customByReferenceKey => 'custom (by reference) value'};
        }
    }
    
    public Custom customByValueKey {
        get {
            return new Custom(987);
        }
    }
    public Map<Custom, String> customByValueKeyMap {
        get {
            return new Map<Custom, String>{customByValueKey => 'custom (by value) value'};
        }
    }
}
Advertisements