SharePoint 2010: How to set Taxonomy Field Values programmatically
There are now many blogs and forum posts explaining how to add Taxonomy Fields to a list via the UI, via CAML or programmatically. The end result is a Taxonomy Field linked with a hidden text field, with the hidden text field usually taking the name of the Taxonomy Field appended with ‘TaxHTField0’.
The problem is there are also many posts explaining how to set values in Taxonomy Fields programmatically or how to query for list items using a Taxonomy Field, but they usually leave something out with consequences that are not immediately apparent or use unsupported methods.
The Taxonomy Field ultimately requires 3 values to be set correctly, the WSS ID, the term name, and the term GUID.
The term name and GUID are straight-forward assuming you have pre-populated the term store providing the GUIDs rather than letting them get auto-generated, or alternatively you can lookup the term in the term store and find out the GUID.
The WSS ID however has many confused. When a term is used for the first time in a site collection, the term is also added automatically behind-the-scenes to a hidden site collection list called ‘TaxonomyHiddenList’. This among other things acts as a cache of previously used terms in the site collection. The WSS ID of a term is only unique to a site collection, and is the value of the numeric list item ID in the TaxonomyHiddenList.
Therefore as the WSS ID is auto-generated at runtime only when used for the first time, it is impossible to know the WSS ID for all of your terms in advance. Some posts suggest providing -1 as the WSS ID when adding a list item and it will be OK. Some scenarios may allow this to be added. Other posts suggest actually performing the lookup on the hidden list yourself (or using TaxonomyField.GetWssIdsOfTerm which does this) but don’t explain what to do if a term is not already present. Note, you must never add to or modify the items in the TaxonomyHiddenList without suffering unexpected consequences. I have seen a couple of posts that provide code which uses Reflection to call the non-public TaxonomyField.AddTaxonomyGuidToWss method. This appears to work fine but is far from ideal having to use Reflection to do something which should be possible with normal means. Also when using this to help set a Taxonomy Field value, this only gets us part way anyway.
I found that when setting a Taxonomy Field value, both the Taxonomy Field and the associated hidden text field both need to be set with the appropriate values including using a valid WSS ID. Without doing all of this some aspects of the Taxonomy Field will not work properly. One failure does not manifest itself during use, but happens when the search crawler tries to crawl the list item as it may fail with the following error:
The SharePoint item being crawled returned an error when requesting data from the web service. ( Error from SharePoint site: *** Index was outside the bounds of the array. )
This error appears to be as a result on not being able to find a matching term in the TaxonomyHiddenList using the WSS ID stored in the Taxonomy Field because the field value is set incorrectly.
To correctly set a value in a Taxonomy Field, actually also requires setting another value in the associated hidden text field too.
When viewing the value of a Taxonomy Field it appears with the following format “{0};#{1}” where {0} is the WSS ID, and {1} is the term name. It does not contain the GUID. That is because the Taxonomy Field is behaving similar to a lookup field, with the WSS ID as the lookup ID (into the TaxonomyHiddenList) and the term name as the display value. The GUID however is still required and must be used in the value in the associated hidden text field, where the required format of the hidden text field is “{0}|{1}” where {0} is the term name, and {1} is the term GUID. This format is known as a Label Guid Pair and uses a Pipe (|) delimiter. Both fields combined contain all the information that a Taxonomy Field Control needs to function correctly as well as to allow the search crawler to index it correctly.
To set the Taxonomy Field correctly without having to use unsupported methods to obtain the WSS ID, simply create a new instance of TaxonomyFieldValue linked to the Taxonomy Field, and call the PopulateFromLabelGuidPair method on it, passing in the Label Guid Pair already mentioned. This method actually looks up the WSS ID and adds it if it does not already exist in the TaxonomyHiddenList automatically. Then assign the TaxonomyFieldValue object as the value for the TaxonomyField. Additionally the associated hidden text field must also be set directly with the Label Guid Pair text value.
Here is an example PowerShell function that will set the Taxonomy Field and associated hidden text field values. It uses the TaxonomyField.TextField property to determine which field is the associated hidden text field so there is no need to assume any naming convention when accessing this field.
function SetTaxonomyFieldValue([Microsoft.SharePoint.SPListItem]$listItem, [string]$fieldName, [string]$labelGuidPairFieldValue) { # Get the field as a Taxonomy Field and create a Taxonomy Field Value using the label guid pair [Microsoft.SharePoint.Taxonomy.TaxonomyField]$taxonomyField = $listItem.Fields.GetField($fieldName) [Microsoft.SharePoint.Taxonomy.TaxonomyFieldValue]$taxonomyFieldValue = New-Object Microsoft.SharePoint.Taxonomy.TaxonomyFieldValue($taxonomyField) $taxonomyFieldValue.PopulateFromLabelGuidPair($labelGuidPairFieldValue) # Set the taxonomy field to contain the Taxonomy Field Value $listItem[$taxonomyField.Id] = $taxonomyFieldValue; # Set the associated hidden text field with the label guid pair $listItem[$taxonomyField.TextField] = $labelGuidPairFieldValue }
Of course once all field values have been set in the list item, the SPListItem.Update method will need to be called for the values to take effect.
Things get a little more interesting when setting values in a multi-value Taxonomy Field. The Taxonomy Field itself must be populated with a collection of taxonomy Field Value objects, and the hidden text field must be populated with the Label Guid Pair for each value, combined with a semi-colon (;) separator, as used in the following example PowerShell function.
function SetTaxonomyFieldMultiValue([Microsoft.SharePoint.SPListItem]$listItem, [string]$fieldName, [string]$labelGuidPairFieldValues) { # Get the field as a Taxonomy Field and create a Taxonomy Value using the label guid pair [Microsoft.SharePoint.Taxonomy.TaxonomyField]$taxonomyField = $listItem.Fields.GetField($fieldName) # Split the multiple values into an array of label guid pairs [array]$labelGuidPairFieldValuesArray = $labelGuidPairFieldValues.Split(";") # For each label guid pair, add a Taxonomy Field Value into a Taxonomy Field Value Collection [Microsoft.SharePoint.Taxonomy.TaxonomyFieldValueCollection]$taxonomyFieldValues = New-Object Microsoft.SharePoint.Taxonomy.TaxonomyFieldValueCollection($taxonomyField) foreach ($labelGuidPairFieldValue in $labelGuidPairFieldValuesArray) { # Create a Taxonomy Field Value using the label guid pair [Microsoft.SharePoint.Taxonomy.TaxonomyFieldValue]$taxonomyFieldValue = New-Object Microsoft.SharePoint.Taxonomy.TaxonomyFieldValue($taxonomyField) $taxonomyFieldValue.PopulateFromLabelGuidPair($labelGuidPairFieldValue) # Add the Taxonomy Field Value to the collection $taxonomyFieldValues.Add($taxonomyFieldValue) } # Set the taxonomy field to contain all of the Taxonomy Field Values $listItem[$taxonomyField.Id] = $taxonomyFieldValues; # Set the associated hidden text field with all of the label guid pairs $listItem[$taxonomyField.TextField] = $labelGuidPairFieldValues }
Again once all field values have been set in the list item, the SPListItem.Update method will need to be called for the values to take effect.
Both of these functions simply require you to provide them with the list item instance, the Taxonomy Field name, and the Label Guid Pair(s) corresponding to the term(s) that should be set in the list item’s field.
These PowerShell functions could easily be converted to C# or VB.Net as they only use the .NET Framework and SharePoint API.
UPDATE March 5th, 2012: I have since found that the TaxonomyField class has several overloads of a method called ‘SetFieldValue’ which simplifies setting the Taxonomy field values, but performs the same actions behind-the-scenes of setting the main Taxonomy Field using the TaxonomyFieldValue (or the validated string representation of it which contains the WSS ID, term label, and term GUID) and it also sets the associated hidden text field with the label guid pair, which is also obtained using TaxonomyFieldValue.ToString() method. This confirms that the above approach works, but using the TaxonomyField.SetFieldValue does all of that with one line of code and has several overloads allowing you to instead pass in a Term, or a collection of terms for a multi-value field.
I have copied the reflected code (C#) for TaxonomyField.SetFieldValue below, and you can see it also calls another method called TouchAllTaxonomyFieldValues (also copied below) which just seems to iterate all the taxonomy fields in the list and attempts to copy them over the top of themselves, checking for an exception to see if all the fields have been fetched, throwing an ArgumentOutOfRangeException if any of the fields were not fetched. I am not sure why it needs to do this, but I guess it may be something to do with the system automatically setting the hidden TaxCatchAll field at some stage which contains a copy of all taxonomy values used by the list item (behaving like a cache), so this just makes sure all taxonomy fields are available so this will work.
public void SetFieldValue(SPListItem item, TaxonomyFieldValue taxValue) { if (item == null) { throw new ArgumentNullException("item"); } if (taxValue == null) { throw new ArgumentNullException("taxValue"); } TouchAllTaxonomyColumns(item); item[base.Id] = this.GetValidatedString(taxValue); item[this.TextField] = taxValue.ToString(); } private static void TouchAllTaxonomyColumns(SPListItem item) { foreach (SPField field in item.Fields) { TaxonomyField field2 = field as TaxonomyField; if (field2 != null) { try { item[field2.Id] = item[field2.Id]; item[field2.TextField] = item[field2.TextField]; continue; } catch (ArgumentException exception) { throw new ArgumentOutOfRangeException(Resources.GetString("AllFieldsNotFetched"), exception); } } } }
So there we have it, what appears to be the official way to set taxonomy field values in a list, and after going a long way round we now know how it works too.
Leave a comment Cancel reply
Top Posts
- SharePoint: How to check which Site Template was used to create a site just using a web browser
- SharePoint 2010: PowerShell to Clear the Timer Job Cache
- SharePoint 2010: PowerShell Scripts to Check Deployment Status of WSP Solutions
- SharePoint 2010: PowerShell to Wait for WSP Solution Deployment Timer Job to Complete
- Consistent Keyword Tagging In SharePoint and in the Windows File System
Archive
- June 2019 (1)
- May 2019 (1)
- January 2019 (1)
- October 2018 (1)
- July 2018 (1)
- October 2017 (1)
- May 2017 (1)
- June 2015 (1)
- June 2013 (1)
- May 2013 (2)
- April 2013 (3)
- March 2013 (1)
- February 2013 (1)
- December 2012 (1)
- November 2012 (3)
- October 2012 (3)
- August 2012 (1)
- July 2012 (1)
- June 2012 (1)
- May 2012 (1)
- April 2012 (2)
- March 2012 (5)
- February 2012 (8)
- September 2011 (1)
- July 2011 (2)
- June 2011 (6)
- August 2010 (1)
Hi,
I am creating custom UserProifle edit page, for all taxonomy type property generating taxonomy control on edit profile page using TaxonomyWebTaggigControl. When page is rendering it works fine but it is not setting the value for SPS_Location and SPS-Past projects user properties, other taxonomy type properties like Skills,school,interesets etc are rendering properly. Can anyone please help me hereto find out what is the reason why it is not setting value in SPS-Location filed. There is no server side error/exception found.
Please reply ASAP
Devendra
Hi Devendra
I believe those fields you mentioned are not taxonomy fields even though they look a bit like them so the TaxonomyWebTaggingControl will not work with them.
Best regards
Nick
Thanks for your blog! I had an event receiver that updated a hidden managed metadata field programmatically, the wrong way. I was really bumping my head against the wall to figure out why this error appeared in our crawl log. After using the SetFieldValue method, things started working smoothly again. Unfortunitelly I had to SystemUpdate all existing documents to kick off my improved EventReceiver. Thank God for PowerShell!
Thanx for your post. This is just one more of this SharePoint things, isn’t it? I just want to ask you for a clarification, considering your March 5th update: Does that mean that using this overload of the SetFieldValue method will properly update the Taxonomy field, or we still need to use your awesome powershell scripts ?
The SetFieldValue static method is all that you need to use.
I am working on a document Upload service, which will upload the file to a Drop off library, then routed by the routing rules. But we identify that Routing rules will not be triggered on programmatically uploaded documents.
So, we are Using OfficialFileCore class in Microsoft.Office.Policy for this task.
OfficialFileCore.SubmitFile() method will upload and automatically trigger the routing rule.
Everything is working as expected. But, now using Managed Metadata in one of the fields brought a new issue.
We will be creating RecordsRepositoryProerty object for each of the mandatory fields as below and will add it while file submission.
Adding properties:
properties.Add(new RecordsRepositoryProperty { Name = “Name”, Type = “Text”, Value = file.FileName + “test” });
Submit method:
var result = OfficialFileCore.SubmitFile(
objWeb,
ReadByteArrayFromStream(file.FileContent),
properties.ToArray(),
file.ContentTypeName,
file.FileName,
farmadminAccount,
false,
out destination,
out customOutput);
Everything working great for all the fields except Metadata field.
properties.Add( new RecordsRepositoryProperty
{
Name = “SupportingDocumentType”,
Type = “TaxonomyFieldType”,
Value = requiredTerm.Id.ToString()
});
properties.Add( new RecordsRepositoryProperty
{
Name = “SupportingDocumentType_0”,
Type = “TaxonomyFieldType”,
Value = requiredTerm.Id.ToString()
});
First one is field and second one is Note(hidden field), requiredTerm.Id gives GUID of term.
The “Type” value is creating issue. I have used everything like “TaxanomyField”, “TaxanomyFieldValue”, “TaxanomyFieldType”. “Microsoft.Sharepoint.Taxonomy.TaxonomyField”, “Microsoft.Sharepoint.Taxonomy.TaxonomyFieldValue”, “Text” and “Guid”. We are unable to find the exact string assigned to Type, which will make framework to identify the GUID as a Taxonomy field and display the pertaining Term.
Hi Pratap
Unfortunately when setting values for taxonomy fields, it is not as simple as just adding the GUID of the term in the fields.
The Taxonomy Field should contain a value with the format “{0};#{1}” where {0} is the WSS ID (not a GUID but the ID of the term found in the TaxonomyHiddenList), and {1} is the term name.
The hidden text field value uses the format “{0}|{1}” where {0} is the term name, and {1} is the term GUID. This format is known as a Label Guid Pair and uses a Pipe (|) delimiter.
Both fields must be populated with the formats described above before they will work.
I have not tried this specifically with the Official File upload web service but expect it to use the same field formats as everywhere else.
Hope this helps
Nick
thanks for the article/code.
any idea, how to add a manged metada column from a rmeote system ?
if i use copy.asmx , it didnt work. any other approaches ? ther eis no field property enumeration for the copy.asmx
am stuck with this reqmnt, as i am passing few paramters including the termset name from other system
pls help
thnx benjamin
Thanks for the great article. I was struggling with the exception ” GUID does not exist in the term store” when trying to SetFieldValue for taxonomy field and tis solved by setting taxonomy field Id and value.