How to Work with Managed Metadata Columns by Using the SharePoint Client Object Model (Kaushalendra Kumar)


Managed metadata is a hierarchical collection of centrally managed terms that you can define and then use as attributes for items in Microsoft SharePoint Server 2010. Managed terms can act as sources of data for columns in libraries or lists. To manage the taxonomy column by using the server object model, there are APIs available under the Microsoft.SharePoint.Taxonomy namespace. However, handling managed metadata columns by using the client object model requires a different strategy.

Managed Metadata Columns

To work with managed metadata columns by using the SharePoint client object model, it helps to have an understanding of how managed metadata columns store values in lists. Consider the following details:

Managed Metadata Columns are Derived from SPFieldLookup

TaxonomyFieldValue has a WSS identifier that is the LookupId property of the term in the TaxonomyHiddenList list. The TaxonomyHiddenList list can be viewed by navigating to /Lists/TaxonomyHiddenList/AllItems.aspx. This list contains the details of all of the terms that are used in any managed metadata column.

For Each Managed Metadata Column, the List has Two Columns

For every metadata column, there are two columns in the list. One column is created with a field name of type Taxonomy Field and other column is created with the name in format fieldnameTaxHTField0 of type Note. Details of the taxonomy hidden column (fieldnameTaxHTField0) can be found in the XML of the taxonomy field. The XML of a taxonomy field is similar to the following. The TextField highlighted in yellow contains the GUID of the hidden column.

<Field Type=”TaxonomyFieldType” DisplayName=”Language” List=”{df9afe98-76ae-45ff-9367-e8c94fgb72c2}” WebId=”e6b4333c-617b-4875-bd5d-10e745d5caa3″ ShowField=”Term1033″ Required=”FALSE” EnforceUniqueValues=”FALSE” ID=”{10c90ea3-94e8-453f-b046-ccf3d7b82c3b}” SourceID=”{8b16fadf-a200-429d-9f77-b218863efdba}” StaticName=”Language” Name=”Language” ColName=”int1″ RowOrdinal=”0″ Version=”1″>
        <Default />
        <Customization>
            <ArrayOfProperty>
                – <Property>
                    <Name>SspId</Name>
                    <Value xmlns:q1=”
http://www.w3.org/2001/XMLSchema” p4:type=”q1:string” xmlns:p4=”http://www.w3.org/2001/XMLSchema-instance”>a3c8fc5c-a9ca-46c5-b0c4-fd3f1d1b82f3</Value>
                </Property>
                – <Property>
                    <Name>GroupId</Name>
                </Property>
                – <Property>
                    <Name>TermSetId</Name>
                    <Value xmlns:q2=”
http://www.w3.org/2001/XMLSchema” p4:type=”q2:string” xmlns:p4=”http://www.w3.org/2001/XMLSchema-instance”>23cc4396-6de3-47a7-97a6-111675414126</Value>
                </Property>
                – <Property>
                    <Name>AnchorId</Name>
                    <Value xmlns:q3=”
http://www.w3.org/2001/XMLSchema” p4:type=”q3:string” xmlns:p4=”http://www.w3.org/2001/XMLSchema-instance”>00000000-0000-0000-0000-000000000000</Value>
                </Property>
                – <Property>
                    <Name>UserCreated</Name>
                    <Value xmlns:q4=”
http://www.w3.org/2001/XMLSchema” p4:type=”q4:boolean” xmlns:p4=”http://www.w3.org/2001/XMLSchema-instance”>false</Value>
                </Property>
                – <Property>
                    <Name>Open</Name>
                    <Value xmlns:q5=”
http://www.w3.org/2001/XMLSchema” p4:type=”q5:boolean” xmlns:p4=”http://www.w3.org/2001/XMLSchema-instance”>false</Value>
                </Property>
                – <Property>
                   
<Name>TextField</Name>
                    <Value xmlns:q6=”
http://www.w3.org/2001/XMLSchema” p4:type=”q6:string” xmlns:p4=”http://www.w3.org/2001/XMLSchema-instance”>{6a8b96d5-78a1-4e98-875f-52bcd3ebd95a}</Value>
                </Property>
                – <Property>
                    <Name>IsPathRendered</Name>
                    <Value xmlns:q7=”
http://www.w3.org/2001/XMLSchema” p4:type=”q7:boolean” xmlns:p4=”http://www.w3.org/2001/XMLSchema-instance”>false</Value>
                </Property>
                – <Property>
                    <Name>IsKeyword</Name>
                    <Value xmlns:q8=”
http://www.w3.org/2001/XMLSchema” p4:type=”q8:boolean” xmlns:p4=”http://www.w3.org/2001/XMLSchema-instance”>false</Value>
                </Property>
                – <Property>
                    <Name>TargetTemplate</Name>
                </Property>
                – <Property>
                    <Name>CreateValuesInEditForm</Name>
                    <Value xmlns:q9=”
http://www.w3.org/2001/XMLSchema” p4:type=”q9:boolean” xmlns:p4=”http://www.w3.org/2001/XMLSchema-instance”>false</Value>
                </Property>
                – <Property>
                    <Name>FilterAssemblyStrongName</Name>
                    <Value xmlns:q10=”
http://www.w3.org/2001/XMLSchema” p4:type=”q10:string” xmlns:p4=”http://www.w3.org/2001/XMLSchema-instance”>Microsoft.SharePoint.Taxonomy, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Value>
                </Property>
                – <Property>
                    <Name>FilterClassName</Name>
                    <Value xmlns:q11=”
http://www.w3.org/2001/XMLSchema” p4:type=”q11:string” xmlns:p4=”http://www.w3.org/2001/XMLSchema-instance”>Microsoft.SharePoint.Taxonomy.TaxonomyField</Value>
                </Property>
                – <Property>
                    <Name>FilterMethodName</Name>
                    <Value xmlns:q12=”
http://www.w3.org/2001/XMLSchema” p4:type=”q12:string” xmlns:p4=”http://www.w3.org/2001/XMLSchema-instance”>GetFilteringHtml</Value>
                </Property>
                – <Property>
                    <Name>FilterJavascriptProperty</Name>
                    <Value xmlns:q13=”
http://www.w3.org/2001/XMLSchema” p4:type=”q13:string” xmlns:p4=”http://www.w3.org/2001/XMLSchema-instance”>FilteringJavascript</Value>
                </Property>
            </ArrayOfProperty>
        </Customization>
    </Field>

As stated previously, the the TextField is of type Note. The XML for the TextField is similar to the following:

<Field Type=”Note” DisplayName=”Language_0″ StaticName=”LanguageTaxHTField0″ Name=”LanguageTaxHTField0″ ID=”{6a8b96d5-78a1-4e98-875f-52bcd3ebd95a}” ShowInViewForms=”FALSE” Required=”FALSE” Hidden=”TRUE” CanToggleHidden=”TRUE” SourceID=”{8b13fadf-a200-429d-9f77-b218843efdba}” ColName=”ntext2″ RowOrdinal=”0″ />

Taxonomy Field Data
The data in taxonomy fields is stored in the following format:
Sample Column Name Column Type Sample Value Format of Saved Data
Language Taxonomy Field 2;# English WSS Id ;# Name of Term
LanguageTaxHTField0 Note English | c61d9028-824f-446e-9389-eb9515813a42 Name of Term | GUID of Term

The WSS identifier is a 32-bit integer that uniquely identifies list’s list item containing the taxonomy field. This property behaves similarly to the LookupId property and is used as the lookup identifier on the TaxonomyHiddenList list.

Updating the Manage Metadata Column by Using the Client Object Model

To update the taxonomy column, the two associated columns need to be updated with appropriate values.

To update those columns, perform the following steps:

1. Find the WSS identifier of the term that needs to be set as the value. You can use the following code to get the value of WSS identifier of the term:

Microsoft.SharePoint.Client.List taxonomyList = clientContext.Site.RootWeb.Lists.GetByTitle(“TaxonomyHiddenList”);

CamlQuery camlQueryForTerm = new CamlQuery();
                    camlQueryForTerm.ViewXml = @”<View>
                          <Query>
                            <Where>
                              <Eq>
                                <FieldRef Name=’IdForTerm’/>
                                <Value Type=’Text’>” + TermGuidId + @”</Value>
                              </Eq>
                            </Where>
                          </Query>
                        </View>”;

Microsoft.SharePoint.Client.ListItemCollection termItems = taxonomyList.GetItems(camlQueryForTerm);
                    clientContext.Load(termItems);
                    clientContext.ExecuteQuery();

TermGuidId is the GUID of the term to be set as the value.

2. If there is an entry in the TaxonomyHiddenList list for the given term then the values can be updated by using the WSS identifier. If the TaxonomyHiddenList list does not contain an entry for the given term then the scenario is different.

If there is no entry for the term then the WSS identifier must be set as -1. The following table describes the sample format in which to save the data:

Column Type Format (term exists in TaxonomyHiddenList list) Format (term does not exist in TaxonomyHiddenList list)
Taxonomy field WSS Id ;# Name of term -1 ;# Name of term | GUID of term
Note field Name of term | GUID of term Name of term | GUID of term

The following is the code to update the value based on the CAML query results from step 1:

if(termItems.Count > 0)
{
   Microsoft.SharePoint.Client.ListItem termItem = termItems[0];
   splistItem[ColumnName] = termItem[“ID”] + “;#English”;                               
   splistItem[TextColumnName] = “English |c61d9028-824f-446e-9389-eb9515813a42”;
}
else
{
   splistItem[ColumnName] = “-1;#English|c61d9028-824f-446e-9389-eb9515813a42”;                               
   splistItem[TextColumnName] = “English|c61d9028-824f-446e-9389-eb9515813a42”;
}
splistItem.Update();
context.ExecuteQuery();

For metadata columns with multiple values, the values should be separated by ;#. For example, more than one value can be saved in the format given below, based on whether the terms already exist in the TaxonomyHiddenList list.

  • If there is entry in the TaxonomyHiddenList list, use the following:

splistItem[ColumnName] = “2;#English;#3;#French”;
splistItem[TextColumnName] = “English|c61d9028-824f-446e-9389-eb9515813a42;#
French|de1d9028-824f-556e-9389-ac9515813a56″;

  • If there is no entry in the TaxonomyHiddenList list, use the following:

splistItem[ColumnName] = “-1;#English|c61d9028-824f-446e-9389-eb9515813a42;#
-1;#French|de1d9028-824f-556e-9389-ac9515813a56″;
splistItem[TextColumnName]= English|c61d9028-824f-446e-9389-eb9515813a42;# French|de1d9028-824f-556e-9389-ac9515813a56″;

In this post, I discussed how managed metadata columns can be handled by using the client object model to update the different columns with appropriate values.

Additional Resources

For more information on the topics covered in this post, see the following resources:

Managing Enterprise Metadata in SharePoint Server 2010 (ECM)
Using the Client Object Model
Metadata and Taxonomy Programming Model in SharePoint Server 2010 (ECM)


Comments (17)

  1. hardwood floor refinishing toronto says:

    Master piece, I realy feel lucky to see such a wonderful article that is <a href="http://www.mlfhardwoodflooringltd.ca ">hardwood floor refinishing toronto</a> what i want.

  2. Sandy says:

    Great article. One question though, does the same approach works for Enterprise Keywords also? I see that there are two fields for them: TaxKeyword and TaxKeywordTaxHTField. However, setting both of them does not sets the keyword value.

    Any clues if setting Enterprise Keywords via the Client Object Model differs in any way from the normal managed metadata keywords?

  3. Kenny says:

    Incredible article, it was an incredible time-saver for me!

    Thanks!

  4. Gloria says:

    Your post really saves me a lot of time. Thanks a lot!

    tiffany-joyas-precios.com

  5. Chris says:

    Great article! Is there a way to discover the name of the hidden note field? The example you use of fieldnameTaxHTField0 only works for programmatically-created columns. If you hand-create the metadata column, the hidden note field has a guid as the name (without the dashes). How can I find out what that field name is?

  6. Kaushalendra Kumar says:

    Chris, you can get the hidden field name from SchemaXML and property name is TextField

  7. Alex says:

    In my environment there are absolutely no columns ending with "TaxHTField0". I used Sharepoint Manager 2010 to have a look and I can see that for each Managed Metadata column I added to my list, I have another column with the same name ending with "_0". Whatever I tried though, whatever the format I tried. Nothing worked and I don't even get any exception. Values are simply not assigned.

  8. Alex says:

    After looking at that for like 4 hours I decide to post a comment and find my solution right after posting!

    Finally figured out that the columns I had, ending with "_0" are actually the column with the Note Column Type that you are mentionning but instead of having a static name ending with "TaxHTField0" mine have a completely random static name like "b2f2926f0257424bac1db0fa8cf8d4ef"

    Your post really helped me a lot, Thanks!

  9. Kaushalendra Kumar says:

    Alex, Great that you found the solution. I just wanted to point out one thing, while finding the hidden column, try to find based on guid in TextField (Check the highlighted xml in the post). Finding column using guid is correct way rather than relying on static name.

  10. Alex says:

    Its exactly what I ended up doing! Thanks a lot!

  11. JAYESH says:

    Hi ,

    This concept doesn't work for documnet library managed maeatada field.

    Will you please kindly suggest on this???????????

    Regards

  12. the concept worked for a custom list and was not working for a sharepoint document library .can some one help me with this ?

  13. Kaushalendra Kumar says:

    It should work with document library. Please let me know the issue your are getting with document library.

  14. Not sure about a custom list as I am focusing on setting MMS field values in a document library which works, for me at least.  However, what I have found is that if you try and set field values in both the taxonomy field and the hidden note field (as prescribed here) then the value is not set i.e. it does not work.  It seems that all you have to do is to set the value in the taxonomy field.  I presume some magic goes on with event handlers in the background.

    So when working with document libraries the process seems to be a bit simpler as now you don't have to deal with the inconsistent naming of the internal name of the hidden note field which is sometimes the taxonomy field internal name with "TaxHTField0" appended and sometimes a Guid like string.  As a point of interest it seems that when an internal name is a Guid like string then it is derived from the unique ID of the taxonomy field i.e. get the Guid of the taxonomy field as a string, remove all dashes and then replace the first character of the string (whatever it is) with 'h' (standing for hidden is my guess).  Well that's my guess as to what's going on.

    Excellent article BTW 🙂

  15. After looking at this again it appears that just setting the taxonomy field (and ignoring the hidden note field) works for document libraries in most cases it does not work consistently.  I persevered with setting the prescribed value in the hidden note field and got it working.  As with others the main challenge was getting a reference to the associated hidden note field as it seems that the field naming convention is inconsistent depending on whether the field was provisioned by code or through the UI.

    As Kaushalendra points out the best approach is to get the Id of the associated TextField property which is stored in the SchemaXML property of the field, but how?  To make life a bit easier I have abstracted this task out to a couple of extension methods which can be parked in a static utilities class somewhere.

    using System.Xml.Linq;

    using System.Xml.XPath;

           public static string SchemaXmlPropertyValue(this Field SourceField, string PropertyName)

           {

                XDocument xDoc = XDocument.Parse(SourceField.SchemaXml);

               XElement PropertyElement = (from XElement xElem in xDoc.XPathSelectElements("//Property")

                                           where xElem.Element("Name").Value == PropertyName

                                           select xElem).FirstOrDefault();

               if (PropertyElement == default(XElement) || PropertyElement.Element("Value") == null)

                   return string.Empty;

               else

                   return PropertyElement.Element("Value").Value;            

           }

    The above allows you to get any property value from the SchemaXML, whereas the following uses the above to return the TextField property as a Guid which is the Id of the hidden note field associated with the taxonomy field

           public static Guid HiddenTextFieldId(this Field SourceField)

           {

               //Get the TextField property value as a string as stored in the SchemaXML, trim off the leading

               //and trailing brace characters and create a Guid to return from the resulting string            

               try

               {

                   if (string.IsNullOrEmpty(SourceField.SchemaXmlPropertyValue("TextField")))

                       return Guid.Empty;

                   else                    

                       return new Guid(SourceField.SchemaXmlPropertyValue("TextField").TrimStart('{').TrimEnd('}'));

               }                

               catch

               {

                       return Guid.Empty;

               }                      

           }

    Then call the extension method like this:

    Guid TextFieldId = TaxField.HiddenTextFieldId();

    Field HiddenNoteField = ParentList.Fields.GetById(TextFieldId);

  16. Michael Vasquez says:

    I have an issue with Taxonomy Metadata Columns where the title contains a space in it. For example, I have yet to populate the following column. However, I have been successful in populating the same type of column that contains no space in it.

    InternalName: Job_x0020_Title, StacicName: Job_x0020_Title, Type: TaxonomyFieldType, ID: fbda96f1-3c1d-4f9d-b44b-488cfd405581, DisplayName: Job Title

    Any ideas? I am using the copy web service CopyIntoItems.