OMG W[o]TF CAML!? Choice Field Pet Peeve

Actually I had a different pet peeve today, but I'll blog that later. There'll be some code - and ice cream for those of you who get here while there's some left.
Okay, SharePoint can awesome, but sometimes it can also be pretty damned stupid. Here is a classic example of that dichotomy.
This is the documentation for a CHOICES / CHOICE element collection in CAML, from the MSDN version of the WSS 3.0 SDK.
CHOICES Element (List)
Used to define several choices within a field for a drop-down list.


CHOICE Element (List)
Used to define a choice within a Choice field.

Value = "Text">
Value = "Text">
Optional Text. Can be used to specify an identifier for the choice.
Now, reading that, you might be led to believe that you could create a CHOICES collection like this to have SharePoint use value codes on the back-end of its DropDownList's options tag in HTML.

  <CHOICE Value="MD">Maryland</CHOICE>
  <CHOICE Value="DE">Delaware</CHOICE>
  <CHOICE Value="VA">Virginia</CHOICE>
  <CHOICE Value="PA">Pennsylvania</CHOICE>

Well, you would be WRONG! Wrong, as in "You get NOTHING! Good day sir!" wrong. In fact, as nearly as I can tell, the Value attrbiute of this element is less than useless. It's not invalid if you use it - sometimes. (Actually, sometimes it causes validation errors and sometimes it doesn't) It [usually] doesn't break anything; it just doesn't do anything, and the documentation leads you to believe that it should, which I think has the potential to cause a huge waste of time.

So, a big WoTF (wag of the finger) to Microsoft, for fragging this one up royally. And, I guess with that comment, I shouldn't expect to be getting that MVP nomination I've always wanted any time soon, huh? Oh well!

I dug into this problem a little deeper. At first, I believed that you could provide a multi-column style value-text pair in the CHOICE element text, similar to what you'd find in a ModStat field type. However, this turned out to be a pipe dream.


As far as I can tell so far, what is actually going on is that the XSD schema for WSS is actually not consistent with the online documentation. See the following from wss.xsd:

  <xs:complexType name="CHOICEDEFINITIONS" mixed="true">
      <xs:element name="CHOICE" type="xs:string" minOccurs="0" maxOccurs="unbounded" />

Well, it's no wonder when I try to use the Value attribute in my custom content types and other features, I get nasty XML validation messages from Visual Studio telling me that the Value attribute is no valid in the CHOICE element, because it's not.

I don't recommend you try this on a production box, but what I did was to patch wss.xsd as follows.

<!-- BEGIN CHANGE BY TCARPE 11/1/2007 -->
  <xs:complexType name="CHOICEDEFINITIONS" mixed="true">
      <xs:element name="CHOICE" type="CHOICEDEFINITION" minOccurs="0" maxOccurs="unbounded" />
  <xs:complexType name="CHOICEDEFINITION">
      <xs:extension base="xs:string">
        <xs:attribute name="Value" type="xs:string" use="optional" />
<!-- END CHANGE BY TCARPE 11/1/2007 -->

That made Visual Studio and the VSeWSS behave themselves, but it's not clear to me yet if it will actually have any effect on the behavior of the Choice field and its DropDownList. In theory it should, since I see CAML in the RenderTemplate for CHOICE that retreives the Value attribute - if it exists.

From FLDTYPES.xml:

<HTML><![CDATA[<SCRIPT>fld = new ChoiceField(frm,]]></HTML> <ScriptQuote><Property Select="Name"/></ScriptQuote><HTML>,</HTML> <ScriptQuote><Property Select="DisplayName"/></ScriptQuote> <HTML>,</HTML> <ScriptQuote><Column/></ScriptQuote> <HTML>); fld.format = "</HTML>     <Property Select="Format"/>  <HTML>"; </HTML> <Switch> <Expr><Property Select="FillInChoice"/></Expr> <Case Value="TRUE">fld.fFillInChoice = true;</Case> </Switch> <ForEach Select="CHOICES/CHOICE"> <HTML>fld.AddChoice(</HTML> <ScriptQuote><Property Select="."/></ScriptQuote> <HTML>, </HTML> <ScriptQuote><Property Select="Value"/></ScriptQuote> <HTML>);</HTML> </ForEach> <Switch> <Expr><Property Select="Required"/></Expr> <Case Value="TRUE">fld.fRequired = true;</Case> </Switch> <HTML><![CDATA[fld.IMEMode="]]></HTML>      <Switch>       <Expr><Property Select="Type"/></Expr>       <Case Value="Lookup"><HTML><![CDATA[inactive]]></HTML></Case>       <Case Value="DateTime"><HTML><![CDATA[inactive]]></HTML></Case>       <Case Value="GridChoice"><HTML><![CDATA[inactive]]></HTML></Case>       <Case Value="Calculated"><HTML><![CDATA[inactive]]></HTML></Case>       <Case Value="Currency"><HTML><![CDATA[inactive]]></HTML></Case>       <Case Value="Number"><HTML><![CDATA[inactive]]></HTML></Case>       <Case Value="User"><HTML><![CDATA[inactive]]></HTML></Case>       <Case Value="Boolean"><HTML><![CDATA[inactive]]></HTML></Case>       <Default><Property Select="IMEMode" HTMLEncode="TRUE"/></Default>     </Switch>     <HTML><![CDATA[";]]></HTML> <HTML><![CDATA[fld.BuildUI();</SCRIPT>]]></HTML>

Clearly, somebody over there at MS was expecting this attribute to be declared - at least sometimes. I really have to wonder how this fell through the cracks, and if I'll be doing more harm than good with this particular tweak.

So, I just gave it a try to see what happens. Sadly, there is no joy in mudville today. Mighty SharePoint has struck out. Using this:

  <CHOICE Value="C">Client</CHOICE>
  <CHOICE Value="P">Partner</CHOICE>
  <CHOICE Value="S">Staff / Professional</CHOICE>
  <CHOICE Value="V">Vendor</CHOICE>
  <CHOICE Value="O">Other</CHOICE>

SharePoint gives me:

     <option selected="selected" value="Client">Client</option>
     <option value="Partner">Partner</option>
     <option value="Staff / Professional">Staff / Professional</option>
     <option value="Vendor">Vendor</option>
     <option value="Other">Other</option>

I give up! I don't think anything short of creating a custom field type is going to solve this problem.

The remifications of this "bug" are actually far reaching and profound. For example, let's suppose you have a custom field (or site column) with custom properties defined, and you want to assign a value from a CHOICES collection to a property of an enumerable type in your own class derived from SPFIeld. If the Value attribute were working as claimed, you could assign it the string equivalent of each value of the enumeration, then use a simple call to Enum.Parse() to do the conversion. Instead, you are left with the ugly choice of writing custom parsers. (Choose your posion: property attributes in the enum, or a hard coded switch-case statement.) And, you'll have to write one for each choice property you want to create in this way. This increases the overall amount of time spent doing code and maintainance, which is the opposite of why we use SharePoint in the first place.

For now, I guess the only thing I can really take away from this experience, other than frustration, is an affirmation of one of the great truisms of software engineering as coined by the irrerent Fred Brooks, "Documentation lies."