Update taxonomy field values in a list item through SharePoint web services


Recently, I was tasked to find out how to update a taxonomy field value in a list item through SharePoint web services (lists.asmx).  Though this sounds quite simple, it turned out to be a good learning.

Taxonomy fields are very similar to lookup fields.  But at the same time, they are not lookup fields.  Internally, the values of a taxonomy fields are stored in this format: WSSID;#VAL|GUID.  The WSSID parameter is a property that uniquely identifies the list item containing the taxonomy field in a list.  More on this later.  You must be aware of ;# notation if you have worked with lookup fields through SharePoint OM.  The other pieces are the actual taxonomy value (got from the managed metadata store or taxonomy store) and its corresponding GUID separated by a pipe character.  So essentially, if you want to update a taxonomy field in a list item, you use the object model code as shown below:

   1: using (SPSite site = new SPSite(siteUrl))
   2: {
   3:     using (SPWeb web = site.OpenWeb())
   4:     {
   5:         SPList list = web.Lists[listName];
   6:         SPListItem item = list.GetItemById(1);
   7:         item["Title"] = "title updated by server object model 1";
   8:         item["Country"] = "1;#India";
   9:         item["Continent"] = "2;#Northamerica|d511f3c7-377f-480f-aff6-beebecd3c675";
  10:         item.Update();
  11:     }
  12: }

A little description on the additional fields that might be of interest: “Country” is a traditional lookup field.  “Continent” is a taxonomy field.  This is all good.  Except that when you try updating this through web services, you might be in for a little surprise!

Looks like specifying the taxonomy field value like this directly to the taxonomy field isn’t possible through web services.  So, the below code wouldn’t work (when I say wouldn’t work, I mean you won’t see any exceptions but the taxonomy field will not be updated).

   1: Lists list = new Lists();
   2: list.Url = siteUrl + "/_vti_bin/lists.asmx";
   3: list.Credentials = CredentialCache.DefaultCredentials;
   4:  
   5: XmlNode ndListView = list.GetListAndView(listName, "");
   6: string strListID = ndListView.ChildNodes[0].Attributes["Name"].Value;
   7: string strViewID = ndListView.ChildNodes[1].Attributes["Name"].Value;
   8:  
   9: XmlDocument doc = new XmlDocument();
  10: XmlElement batchElement = doc.CreateElement("Batch");
  11: batchElement.SetAttribute("OnError", "Return");
  12: batchElement.SetAttribute("ListVersion", "1");
  13: batchElement.SetAttribute("ViewName", strViewID);
  14:  
  15: batchElement.InnerXml = "<Method ID='1' Cmd='Update'>" +
  16:     "<Field Name='ID'>3</Field>" +
  17:     "<Field Name='Title'>Modified through lists.asmx web service 1</Field>" +
  18:     "<Field Name='Country'>3;#Germany</Field>" +
  19:     "<Field Name='Continent'>2;#Northamerica|d511f3c7-377f-480f-aff6-beebecd3c675</Field></Method>";
  20:     
  21:     XmlNode ndResult;
  22:     try
  23:     {
  24:         ndResult = list.UpdateListItems(strListID, batchElement);
  25:         richTextBox1.Text = ndResult.OuterXml;
  26:     }
  27:     catch (SoapServerException soapException)
  28:     {
  29:         richTextBox1.Text = "Error: " + soapException.Message + Environment.NewLine +
  30:                     "Stack Trace: " + soapException.StackTrace;
  31:     }

There’s another hidden field of type “Note” that actually needs to be updated to update the taxonomy field through lists.asmx web service.  Running a small Windows PowerShell script to fetch all the fields from the list will show you that hidden taxonomy field (one of every single taxonomy fields you have in the list).

   1: PS C:\Windows\System32\inetsrv> $web = get-spweb http://localhost:100
   2: PS C:\Windows\System32\inetsrv> $list = $web.Lists["ManagedMetaData"]
   3: PS C:\Windows\System32\inetsrv> foreach($fld in $list.Fields){$fld.Title + " :: " + $fld.InternalName + " :: " + $fld.Type}

In the result, you should see the hidden taxonomy field of type “Note” corresponding to the field you are trying to update.  In my case, here’s a part of the output:

HTML File Link :: xd_ProgID :: Text

Is Signed :: xd_Signature :: Boolean

Country :: Country :: Lookup

Continent :: Continent :: Invalid

Continent_0 :: ContinentTaxHTField0 :: Note

Taxonomy Catch All Column :: TaxCatchAll :: Lookup

Yep, you can find this out using OM code as well.  But Windows PowerShell is simply powerful, neat and I wanted to brag that I know a little bit of it.

Now, all I had to do was to use “ContinentTaxHTField0” (this is the internal name of this field) instead of “Continent” in my web service code.  Here’s the correct code:

   1: Lists list = new Lists();
   2: list.Url = siteUrl + "/_vti_bin/lists.asmx";
   3: list.Credentials = CredentialCache.DefaultCredentials;
   4:  
   5: XmlNode ndListView = list.GetListAndView(listName, "");
   6: string strListID = ndListView.ChildNodes[0].Attributes["Name"].Value;
   7: string strViewID = ndListView.ChildNodes[1].Attributes["Name"].Value;
   8:  
   9: XmlDocument doc = new XmlDocument();
  10: XmlElement batchElement = doc.CreateElement("Batch");
  11: batchElement.SetAttribute("OnError", "Return");
  12: batchElement.SetAttribute("ListVersion", "1");
  13: batchElement.SetAttribute("ViewName", strViewID);
  14:  
  15: batchElement.InnerXml = "<Method ID='1' Cmd='Update'>" +
  16:     "<Field Name='ID'>3</Field>" +
  17:     "<Field Name='Title'>Modified through lists.asmx web service 1</Field>" +
  18:     "<Field Name='Country'>3;#Germany</Field>" +                
  19:     "<Field Name='ContinentTaxHTField0'>2;#Northamerica|d511f3c7-377f-480f-aff6-beebecd3c675</Field></Method>";
  20:             
  21:     XmlNode ndResult;
  22:  
  23:     try
  24:     {            
  25:         ndResult = list.UpdateListItems(strListID, batchElement);
  26:         richTextBox1.Text = ndResult.OuterXml;
  27:     }
  28:     catch (SoapServerException soapException)
  29:     {
  30:         richTextBox1.Text = "Error: " + soapException.Message + Environment.NewLine +
  31:             "Stack Trace: " + soapException.StackTrace;
  32:     }

This works like charm and the update is done successfully!  HTH.

Comments (11)

  1. timw says:

    Thanks!  I think this needs to be posted in the SDK!

  2. James says:

    Great post but one thing I don't get – how do you know the GUID of each value? I'm integrating through literal SOAP calls only.

  3. Carl says:

    We ended up building a web service using Microsoft.SharePoint.Taxonomy that the client could query.

    msdn.microsoft.com/…/microsoft.sharepoint.taxonomy.aspx

    msdn.microsoft.com/…/ff521581.aspx

  4. Srinu Tamada says:

    Hi Sridhar, it is a nice article.

    Asking service consumer to provide hidden field name and metadata value in "WSSID;#VAL|GUID" format, is not a good approach; I feel. Is there any alternative way to update metadata field from webservices?

    How far BCS supports to update metadata fields(Taxonomy field)??

    Please respond to me. or email me to srinu.tamada@gmail.com.

    Thanks in advance.

  5. Marc D Anderson says:

    I've exposed the TaxonomyClientService in my SPServices jQuery library (spservices.codeplex.com/wikipage) which allows you to access the term store client side. It's another piece to this puzzle.

    M.

  6. Mike Williams says:

    Hi Sridhar,

    Thanks for the excellent artice.  It got me to a solution which worked in our dev environment.

    However when we went to production with a Managed Metadata service sourced from a different server we encountered a situation where some MMS fields would update.  We could not see any reason why particular fields failed.  The hidden taxonomy list (http://server/site/Lists/TaxonomyHiddenList/AllItems.aspx) was being added to for all new fields.

    We raised the issue with microsoft.

    At microsoft's recomendations we changed the code to update BOTH the fields for an MMS field as shown below :-

    batchElement.InnerXml = "<Method ID='1' Cmd='Update'>"

      + "<Field Name='ID'>3</Field>"

      + "<Field Name='Title'>Modified through lists.asmx web service 1</Field>"

      + "<Field Name='Country'>3;#Germany</Field>"

      + "<Field Name='Continent'>2</Field>"

      + "<Field Name='ContinentTaxHTField0'>0;#Northamerica|d511f3c7-377f-480f-aff6-beebecd3c675</Field>"

      + "</Method>";

    I also found that the pointer "<Field Name='Continent'>2</Field>" could be set to 0 if the term was not already in the hidden list. ie "<Field Name='Continent'>0</Field>"

    /Mike

  7. an.vn says:

    Hi there,

    Thanks for the post!

    But how about the taxonomy field with multi-values.

    Thanks in advance.

  8. Mark says:

    Hi

    There is a 3rd party tool to do bulk metadata editing at http://www.qipoint.com, it also can do multiple taxonomy values for multiple items, hope that helps someone else

  9. AP says:

    Its great.. But How to update value for the type of TaxonomyFieldTypeMulti

  10. Tina says:

    Thanks a ton. After searching for 2 days finally found this and it worked. Can you tell me how can we update the item with multiple taxonomies.

  11. Alex Talarico says:

    worth noting the limit of terms per item, i believe it is right under 200.. anyone verify?

Skip to main content