Taxonomy Columns & SharePoint 2013 List REST API

Monday, May 12, 2014

14

I ran into a strange issue today when using the SharePoint 2013 REST API for Lists with Managed Metadata columns.  For a multi-select taxonomy column, the List service returns Label text as expected.  But if you change that same column to single-select, suddenly an ID is returned in the Label field!

For instance, consider an Orders list with a Product multi-select taxonomy field.  I run the following REST query to dump all the list items:

  http://sharepointificate/_api/web/Lists/GetByTitle('Orders')/Items

All is good and the Product label text is returned:

Now if I just change Product to single-select and run that same query, the friendly label is gone!  This has to be a bug.  Yes, YOU, Microsoft.  I'm talking to you!


Luckily, there is a workaround. The Taxonomy label is correctly returned when we use a CAML query to retrieve the list items.  Erik C. Jordan's answer on this MSDN thread reveals a way to use CAML against the REST API.  This guy must be some kind of SP savant, because I can find absolutely no other documentation on this method—but it works

/GetByTitle('Orders')/GetItems(query=@v1)?@v1={'ViewXml':'<View><Query></Query></View>'}

A couple things to note:

  1. GetItems is a POST operation.  The query above will not work when run from your browser's address bar, which performs a GET.  You need a tool like Fiddler to send a POST message to the REST endpoint.  More likely, you'll be doing a jQuery ajax call like the example below.
  2. An X-RequestDigest message header is required for POST operations against the REST API.  Its value differs between environments and apps.  If this header is missing or incorrect, the server will return a 403 Forbidden error.  When executing within a SharePoint-hosted or on-premise context, it should be available on the page itself, and you can grab it with $("#__REQUESTDIGEST").val().  For other contexts, you'll have to retrieve the value like this MSDN article describes.

The complete Ajax call:

$.ajax({
    url: "http://sharepointificate/_api/web/Lists/GetByTitle('Orders')/GetItems(query=@v1)?@v1={'ViewXml':'<View><Query></Query></View>'}",

    type: "POST",

    headers: {
        "Accept": "application/json;odata=verbose",
        "X-RequestDigest": $("#__REQUESTDIGEST").val()
    },

    success: function (data, textStatus, jqXHR) {
        console.log(data.d.results[0].Product.Label);
    },

    error: function (jqXHR, textStatus, errorThrown) {
        console.dir(jqXHR);
    }
});


14 comments:

You sir and Erik are both Geniuses!! Thanks so much for this. It helped me a lot!

Brilliant Post. No idea how long I was searching for that. Thank you very much!!!

Cool.This works like charm.But when i make rest call to a list which has a taxonomy field from another site collection i get 401(Unauthorized) error.
Appreciate if you can give me a fix.
Thanks.

kalvaanirudh, You need at least read access to that site collection's list.

I have another solution. When we use Taxonomy fields in a site, SharePoint creates a hidden list titled 'TaxonomyHiddenList'. We can use this list to retrieve the taxonomy field value based on the term guid as follows :

var item = null;

var url = webUrl + "/_api/web/lists/getbytitle('TaxonomyHiddenList')/items?" +
"?$filter=IdForTerm eq '" + termId + "'";

$.ajax({
url: url,
type: "GET",
async: false,
headers: { "accept": "application/json;odata=verbose" },
success: function (data) {
if (data.d.results.length > 0) {
item = data.d.results[0];
return item.Term;
}
},
error: function (err) {
console.log(JSON.stringify(err));
}
});

The main advantage is that it doesn't rely on any CAML query and that it doesn't depend on specific items.

Hope that helps you.

Great explanation. I checked responses in Fiddler and understood why we need the workaround.

Can anyone help me out.. if am trying the same thing its giving me an error as 403 forbidden

Can anyone help me out. i have followed the same approach but am getting 403 forbidden

Recently I found another possible workaround: just use method 'GetItemById' instead of 'items', e.g.:

replace this request:
http://sharepointificate/_api/web/Lists/GetByTitle('Orders')/Items(1)

to this one:
http://sharepointificate/_api/web/Lists/GetByTitle('Orders')/GetItemById(1)

An the label will be displayed correctly!

Another workaround is to use "FieldValuesAsText" to workaround this bug, e.g.

http://sharepointificate/_api/web/Lists/GetByTitle('Orders')/Items(1)?$expand=FieldValuesAsText

Post a Comment