Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / productivity / SharePoint

SharePoint 2010 Taxonomy Import

4.75/5 (4 votes)
9 May 2011CPOL6 min read 47.9K   506  
Importing taxonomy hierarchy into SharePoint 2010

Introduction

SharePoint 2010 added a very useful feature, Managed Metadata, with an easy to use interface for adding, securing and maintaining the Taxonomies that are created. However, importing taxonomies does not have a very robust tool and is often tedious to use.

In this article, I will explorer how Managed Metadata is created in SharePoint 2010 and present a method for easily importing taxonomy hierarchies.

Managed Metadata

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. - MSDN (http://msdn.microsoft.com/en-us/library/ee559337.aspx). The terms that are created can be used to tag content in SharePoint making it easier to search for items or categorize and filter them. In SharePoint 2010, the hierarchy starts with the Managed Metadata Term Store and is organized by Groups which contain TermSets which in turn contain Terms that can also contain other Terms. The image below is a sample to demonstrate this organization.

Image1.PNG

Creating Taxonomy Terms and TermSet

To create Managed Metadata through the UI in SharePoint 2010, you use the Term Store Management Tool, which can be accessed either from the Managed Metadata Service in the Central Administration site:

Image2.PNG

or from the Term store management link on the Site Settings page of a Site:

Image3.PNG

Once in the management tool, to add a new Managed Metadata group, hover over the end of the Managed Metadata Service item to expose an arrow and click to expand the menu then click the New Group menu item.

Image4.PNG

After the group has been added, you will see on the right side of the screen several areas that can be used to designate managers and contributors for the Group. Since these setting are similar to other types of configuration in SharePoint, I won't discuss it here and continue to concentrate on the creation of the Term Sets and Terms.

After the Group has been created, you can use the menu on it to create a Term Set.

Image5.PNG

We'll skip the Import Term Set item for the moment and just create a New Term Set. Once again, after the Term Set has been created, you will see the different settings that can be applied to it on the right.

Using the menu once again on the Term Set, there are a few more options that are available at the Term Set level.

Image6.PNG

Using these menu items, you can copy the Term Set to another Group, which is useful if you have the same structure in two different Groups and saves the time of entering all the information again. The Term Set can also be moved to another Group rather than entering it again and deleting it from the existing Group.

After the Term Set has been created, you can add Terms to it, and Terms to the Terms. You can have a depth of up to seven items, however, practical usage will limit the depth for your needs.

Import Taxonomy Terms and TermSets

All of the above steps are OK for adding a few items, but will quickly become tedious when attempting to enter a new hierarchy, particularly a large or deep one. Fortunately Microsoft has created a method to bulk import Term Sets, which we saw early at the Group level using the Import Term Set menu item. Click on this menu item displays a modal dialog asking a CSV formatted file to import. A sample of the file structure can be found at the Managed Metadata Service level of the Term Store Management Tool.

Image7.PNG

The contents of the file, as with most CSV formats, is not very easy to read. You should be able to see from the header line however the information that can be supplied. Although there is no validation on the file itself, it should be obvious that a name for the Term Set and Terms must be supplied. During the import, the process will fail if the file is not correct.

"Term Set Name","Term Set Description","LCID","Available for Tagging",
"Term Description","Level 1 Term","Level 2 Term","Level 3 Term",
"Level 4 Term","Level 5 Term","Level 6 Term","Level 7 Term"
"Political Geography","A sample term set, describing a simple political geography.",,
True,"One of the seven main land masses (Europe, Asia, Africa, North America, 
South America, Australia, and Antarctica)","Continent",,,,,,
,,,True,"Entity defined by people, not visible to the naked eye",
"Continent","Political Entity",,,,,
,,,True,"Politically defined state with a geographic area governed by a 
central government","Continent","Political Entity","Country",,,,
,,,True,"Administrative division of a country","Continent",
"Political Entity","Country","Province or State",,,
,,,True,"Large sub-region usually containing many cities and towns",
"Continent","Political Entity","Country","Province or State","County or Region",,
,,,True,"Small village","Continent","Political Entity","Country",
"Province or State","County or Region","Hamlet",
,,,True,"Collection of homes and business, often incorporated",
"Continent","Political Entity","Country","Province or State",
"County or Region","Village",
,,,True,"A small city","Continent","Political Entity","Country",
"Province or State","County or Region","Town",
,,,True,"An incorporated town with a large population, usually governed by a mayor 
or council","Continent","Political Entity","Country","Province or State",
"County or Region","City",
,,,True,"A division of a city, often represented in the city government",
"Continent","Political Entity","Country","Province or State","County or Region",
"City","District"
,,,True,"A sub-section of a city","Continent","Political Entity","Country",
"Province or State","County or Region","City","Borough"
,,,True,"Unofficial district or area of a city or town","Continent",
"Political Entity","Country","Province or State","County or Region","City","Neighborhood"

Having this automated method is helpful, but there are several drawbacks to this method. First, the file format is difficult to read, even if using Excel it is difficult to see a hierarchy from the flat file. Second, only one Term Set can be imported at a time. If you have multiple Terms Sets to add to a Group, the import process must be run using multiple files, one for each Term Set.

Importing Taxonomy Hierarchy

To overcome the drawbacks with the out of the box method of importing Managed Metadata, I have used an XML file to specify the hierarchy in an intuitive fashion and created an application page that will read the file and create the Taxonomy TermsSets and Terms found there.

XML
 <Taxonomy lcid="1033">
  <TermStore name="Managed Metadata Service">
    <Group name="CodeProject">
      <TermSet name="General Programming">
        <Term name=".NET Framework" guid="{fc456058-65a1-45b8-9b50-fbb561cba9c0}" />
      </TermSet>
    </Group>
  </TermStore>
</Taxonomy> 

In comparison to the CSV file from before, this is much easier to read and see the hierarchy that will be created. The name attribute for the elements is of course the name that will be applied to the Group, TermSet and Term respectively. The guid attribute will be explained shortly.

The code for the application page is very simple, nothing complex is needed, just upload a file and send it to the helper method for processing.

C#
protected void OnImport(object sender, EventArgs e)
{
    if(FileUpload.HasFile)
    {
        try
        {
            TextReader reader = new StreamReader(FileUpload.PostedFile.InputStream);
            MANSoftDev.SharePoint.Utilities.Taxonomy.TaxonomyHelper.ImportTerms
					(SPContext.Current.Site, reader);

            msg.InnerText = "Import complete";
            msg.Visible = true;
        }
        catch(Exception ex)
        {
            msg.InnerText = ex.Message;
            msg.Visible = true;
        }
    }
    else
    {
        msg.InnerText = "Please select a file.";
        msg.Visible = true;
    }
}

The helper method is also not very complex. The first thing is to Load the uploaded file stream into an XElement so it can be processed. After we have loaded the XElement, it is just a matter of stepping through the hierarchy and creating the appropriate elements.

C#
public static void ImportTerms(SPSite site, TextReader metadata)
{
    Site = site;
    XElement taxonomy = XElement.Load(metadata);
    if(taxonomy.Attribute("lcid") != null)
    {
        LCID = Convert.ToInt32(taxonomy.Attribute("lcid").Value);
    }
    else
    {
        LCID = 1033;
    }

    // Get the TaxonomySession from the specified site
    TaxonomySession session = new TaxonomySession(site);

    foreach(XElement termStoreElement in taxonomy.Elements())
    {
        // Get the TermStore from the import file
        TermStore termStore = session.TermStores[termStoreElement.Attribute("name").Value];
        if(termStore != null)
        {
            foreach(XElement groupElement in termStoreElement.Elements())
            {
                // Check if the Group already exits and create it if not
                Group group = termStore.Groups.FirstOrDefault
				(e => e.Name == groupElement.Attribute("name").Value);
                if(group == null)
                {
                    group = termStore.CreateGroup(groupElement.Attribute("name").Value);
                }

                foreach(XElement termsetElement in groupElement.Elements())
                {
                    CreateTermsSets(termsetElement, group);
                }
            }
        }

        // Commit everything that has been added
        termStore.CommitAll();
    }
}

The CreateTerms method is where it gets a little more complex and is where the guid attribute is used.

C#
private static void CreateTerms(XElement element, TermSet set)
{
    Term term = set.Terms.FirstOrDefault(e => e.Name == element.Attribute("name").Value);
    if(term == null)
    {
        term = set.CreateTerm(element.Attribute("name").Value, LCID,
            new Guid(element.Attribute("guid").Value));

        int[] ids = TaxonomyField.GetWssIdsOfTerm
			(Site, set.Group.TermStore.Id, set.Id, term.Id, false, 999);
        if(ids.Length == 0)
        {
            int id = CreateWssId(Site, term);
        }
    }
}

As with quite a lot of things in SharePoint, guids are used to uniquely identify artifacts across Sites, Lists, Application and other places. Managed Metadata is no different. As you can see, one of the overloads for the TermSet.CreateTerm method takes a guid that will be used to identity the Term. If not specified, a guid will be created for it. This can lead to problems when deploying a feature to multiple servers since each server will create a different guid for the Term. Using the overloaded method, we can ensure that the guid will be the same and can be consistently referenced in code and other configuration files, such as ContentType or Field declarations.

Another point of interest with this code is the TaxonomyField.GetWssIdsOfTerm method. SharePoint uses a hidden list to maintain the Taxonomy Terms that have been used and adds an id for each one to the list. The Term will only be added if it is used, for example, by being selected in a TaxonomyPicker control and added to a list. Adding a Term to the TermSet is not enough to trigger this though. However, we can force the issue. Although there is no publically accessible method to do so, there is a way to have it happen using Reflection as shown below:

C#
private static int CreateWssId(SPSite site, Term term)
{
    site.RootWeb.AllowUnsafeUpdates = true;

    int result = -1;

    MethodInfo mi = typeof(TaxonomyField).GetMethod("AddTaxonomyGuidToWss",
            BindingFlags.NonPublic | BindingFlags.Static, null,
            new Type[3] { typeof(SPSite), typeof(Term), typeof(bool) },
            null);
    if(mi != null)
    {
        result = (int)mi.Invoke(null, new object[3] { site, term, false });
    }

    site.RootWeb.AllowUnsafeUpdates = false;

    return result;
}

Accessing the Functionality

All of the above code is fine but you need a way to access and use it. For that, I have created a new group on the Application Management page of the Central Administration site. As a convenience, I have also created a link to the Term store manager.

XML
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomActionGroup
     Id="MANSoftDev.Group"
     Location="Microsoft.SharePoint.Administration.Applications"
     Title="Managed Metadata" 
     ImageUrl="_layouts/Images/PORT_MAP.GIF"/>
  <CustomAction
    Id="MANSoftDev.ManagedMetaData"
    Title="Term store management"
    GroupId="MANSoftDev.Group"
    Location="Microsoft.SharePoint.Administration.Applications">
      <UrlAction Url="_layouts/termstoremanager.aspx"/>
  </CustomAction>
  <CustomAction
    Id="MANSoftDev.ManagedMetaData"
    Title="Import Managed Metadata"
    GroupId="MANSoftDev.Group"
    Location="Microsoft.SharePoint.Administration.Applications">
      <UrlAction Url="_layouts/MANSoftDev/TaxonomyImport/ImportMetadata.aspx"/>
  </CustomAction>
</Elements>

Conclusion

Importing Managed Metadata isn't a very complex process as I have demonstrated but I hope to have provided an explanation of the available methods and a needed utility to make it more convenient.

History

  • Initial post: 5/9/11

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)