Retrieving a list of eBay categories in a .Net 4.5 Windows Store App

Previously I wrote a post on how to get the eBay category list using their .Net SDK. I wanted to try doing the same thing in a Windows Store App for Windows 8 using Visual Studio 2012, but quickly ran into compatibility problems since the DLLs are compiled for .Net 2.0. I could have either changed the targetted framework, or attempted to re-write the DLLs, but neither sounded particularly exciting, so I simply decided to fall back to using a simple URL request and parsing it via Linq to XML.

As mentioned in the previous post, the first step is to join the eBay Developer Program. Once you've joined and gotten your app ID, you can try a simple request with this URL:

http://open.api.ebay.com/Shopping?callname=GetCategoryInfo&appid=yourappid8&version=679&siteid=0&CategoryID=-1&IncludeSelector=ChildCategories

Passing CategoryID=-1 returns the top-level categories, like this:

<GetCategoryInfoResponse xmlns="urn:ebay:apis:eBLBaseComponents">
   <Timestamp>2012-12-19T16:03:33.893Z</Timestamp>
   <Ack>Success</Ack>
   <Build>E803_CORE_BUNDLED_15627359_R1</Build>
   <Version>803</Version>
   <CategoryArray>
      <Category>
         <CategoryID>-1</CategoryID>
         <CategoryLevel>0</CategoryLevel>
         <CategoryName>Root</CategoryName>
         <CategoryParentID>0</CategoryParentID>
         <LeafCategory>false</LeafCategory>
      </Category>
      <Category>
         <CategoryID>20081</CategoryID>
         <CategoryLevel>1</CategoryLevel>
         <CategoryName>Antiques</CategoryName>
         <CategoryParentID>-1</CategoryParentID>
         <CategoryNamePath>Antiques</CategoryNamePath>
         <CategoryIDPath>20081</CategoryIDPath>
         <LeafCategory>false</LeafCategory>
      </Category>
(etc.)

You can continue to walk down the category tree by passing the sub-category IDs.

The first step will be to fire up an HttpClient and send an asynchronous GET request to the URL:

private async Task<string> CallGetCategoryInfo(int CategoryID)
{
    httpClient = new HttpClient();
    string searchUrl = "http://open.api.ebay.com/Shopping?callname=GetCategoryInfo";

    string requestUrl = searchUrl + "&appid=" + api_key + "&version=679&siteid=0&CategoryID=" + CategoryID.ToString() + "&IncludeSelector=ChildCategories";

    HttpResponseMessage response = await httpClient.GetAsync(requestUrl);

Next we'll load the response stream into an XDocument and create a Ling query:

    System.Xml.Linq.XDocument doc = System.Xml.Linq.XDocument.Load(await response.Content.ReadAsStreamAsync());

    XNamespace ns = "urn:ebay:apis:eBLBaseComponents";

    var query = from categories in doc.Descendants(ns + "Category")
        select new
        {
            CategoryID = categories.Element(ns + "CategoryID").Value,
            CategoryParentID = categories.Element(ns + "CategoryParentID").Value,
            CategoryName = categories.Element(ns + "CategoryName").Value,
            LeafCategory = categories.Element(ns + "LeafCategory").Value,
        };

We'll add the results of the query to a List of objects. I only needed the category name and ID, so that's all my object has:

public class ComboBoxPairs
{
    public string categoryName { get; set; }
    public int categoryId { get; set; }

    public ComboBoxPairs(string CategoryName, int CategoryId)
    {
        categoryName = CategoryName;
        categoryId = CategoryId;
    }
}

And here I add the objects to the List:

    List<ComboBoxPairs> cbp = new List<ComboBoxPairs>();

    foreach (var element in query)
    {
        //The first category returned in the xml will always be either root, or the parent level category.
        // I don't want it in the list
        if (element.CategoryID != "-1" && element.CategoryID != _categoryID.ToString())
        {
            cbp.Add(new ComboBoxPairs(element.CategoryName, Convert.ToInt32(element.CategoryID)));
        }
    }

Finally I assign the List to a global variable and exit:

    _cbp = cbp;
    return "Finished";

The big difference between this method and using the SDK is that you can only return one level at a time. This is probably a good thing; the web service is responsive enough that there's no noticeable wait to the call, versus a distinct wait (and massive size) for retrieving the entire list. If you did want the entire list for some reason, you can simply drill down - use a category ID of -1 (the root category ID) on the first call to get all the level 1 categories, then use a level 1 category ID on the second call to get all the level 2 children of the specified category ID, and so forth. When you get to LeafCategory = true, you can stop.

If you omit the &IncludeSelector=ChildCategories flag then you'll just get info for the selected category, but you'll also get the version and last update time, like this:

<GetCategoryInfoResponse xmlns="urn:ebay:apis:eBLBaseComponents">
   <Timestamp>2012-12-19T16:37:46.516Z</Timestamp>
   <Ack>Success</Ack>
   <Build>E803_CORE_BUNDLED_15627359_R1</Build>
   <Version>803</Version>
   <CategoryArray>
      <Category>
         <CategoryID>-1</CategoryID>
         <CategoryLevel>0</CategoryLevel>
         <CategoryName>Root</CategoryName>
         <CategoryParentID>0</CategoryParentID>
         <LeafCategory>false</LeafCategory>
      </Category>
   </CategoryArray>
   <CategoryCount>1</CategoryCount>
   <UpdateTime>2012-09-11T02:42:04.000Z</UpdateTime>
   <CategoryVersion>102</CategoryVersion>
</GetCategoryInfoResponse>

If you're storing the information locally you can then compare to determine whether the category has changed; if it hasn't, skip the full query and move on to the next.

Here's the full code:

private async Task<string> CallGetCategoryInfo(int CategoryID)
{
    // http://open.api.ebay.com/Shopping?callname=GetCategoryInfo&appid=appID&version=679&siteid=0&CategoryID=-1&IncludeSelector=ChildCategories

    httpClient = new HttpClient();
    string searchUrl = "http://open.api.ebay.com/Shopping?callname=GetCategoryInfo";

    string requestUrl = searchUrl + "&appid=" + api_key + "&version=679&siteid=0&CategoryID=" + CategoryID.ToString() + "&IncludeSelector=ChildCategories";

    HttpResponseMessage response = await httpClient.GetAsync(requestUrl);
    System.Xml.Linq.XDocument doc = System.Xml.Linq.XDocument.Load(await response.Content.ReadAsStreamAsync());

    XNamespace ns = "urn:ebay:apis:eBLBaseComponents";

    var query = from categories in doc.Descendants(ns + "Category")
        select new
        {
            CategoryID = categories.Element(ns + "CategoryID").Value,
            CategoryParentID = categories.Element(ns + "CategoryParentID").Value,
            CategoryName = categories.Element(ns + "CategoryName").Value,
            LeafCategory = categories.Element(ns + "LeafCategory").Value,
        };

    List<ComboBoxPairs> cbp = new List<ComboBoxPairs>();

    foreach (var element in query)
    {
        //The first category returned in the xml will always be either root, or the parent level category
        //I don't want it in the list
        if (element.CategoryID != "-1" && element.CategoryID != _categoryID.ToString())
        {
            cbp.Add(new ComboBoxPairs(element.CategoryName, Convert.ToInt32(element.CategoryID)));
        }
    }

    _cbp = cbp;
    return "Finished";
}

public class ComboBoxPairs
{
    public string categoryName { get; set; }
    public int categoryId { get; set; }

    public ComboBoxPairs(string CategoryName, int CategoryId)
    {
        categoryName = CategoryName;
        categoryId = CategoryId;
    }
}

Technorati Tags: ,,

Print | posted @ Wednesday, December 19, 2012 11:18 AM

Comments on this entry:

Gravatar # re: Retrieving a list of eBay categories in a .Net 4.5 Windows Store App
by jphil at 12/27/2013 8:44 AM

hi,

I wanted to call this on a button click initially and then a listbox will get filled with parent categories. On selecting one of the parent categories from the listbox, a new listbox with subcategories gets filled and so on.
But, when I tried to call this async method it doesn't wait for the task to get complete and listbox gets filled with old values in global variable. Can u let me knw how u call this method from ur page.
Post A Comment
Title:
Name:
Email:
Comment:
Verification: