Content API for Shopping

Content API for Shopping Developer's Guide: .NET

Google provides the .NET library for the Google Data API for connecting to the Content API for Shopping server and interpreting the results. This client library is by no means the only way of connecting to the Content API for Shopping server. This document demonstrates how to use the .NET client library for accessing and editing your data.

Contents

  1. Introduction
  2. Prerequisites
  3. Setup
  4. Performing authentication
  5. Creating and Editing a Product Object
    1. Sample Product as XML
    2. Building a Product in .NET
    3. Editing a Product in .NET
  6. Managing items
    1. Retrieving all your items
    2. Retrieving a single item
    3. Inserting items
    4. Updating items
    5. Deleting items
  7. Batching
    1. Creating a list of entries to be modified
    2. Batch insert
    3. Batch update
    4. Batch delete
    5. Mixed Batch requests
  8. Error Handling
    1. Errors on Individual Items
    2. Batch Errors
    3. Inspecting Errors
  9. Warnings and Dry-run Mode
  10. Accessing Product Performance Data
  11. The generic projection

Introduction

The .NET library for the Google Data API is a generic client library that can be used with many of Google's APIs. For several of these APIs, a custom service class has been defined within the library to represent the data relevant to the API.

As part of this documentation, we provide an example of how you can do this. This example is only meant for teaching how the client library is used. It may not have all the data fields you might want to access; you can find information about fields not used in this example in the library source.

Prerequisites

This document assumes that you know how to program in .NET, and that you are familiar with the basic concepts of the Google Content API for Shopping.

Setup

The .NET client library is available for download from the project page or from the SVN repository.

This documentation has been written for and tested with version 2.1.0 of the client library. The latest version is not guaranteed to be backward compatible with this example.

Once you have installed the client library, you'll need to include the following namespaces for the examples below:

using System;
using System.Collections.Generic;
using System.Xml;
using Google.GData.Client;
using Google.GData.ContentForShopping;
using Google.GData.ContentForShopping.Elements;
using Google.GData.Extensions;

Performing authentication

To make requests to the Content API for Shopping server, you need to be authenticated, which you can do using a number of methods provided by Google. For more information, read the Google Account Authentication documentation. We strongly recommend starting your new applications using OAuth 2.0 as well as migrating existing ones to it. As was announced, all authentication protocols other than OAuth 2.0 are being phased out in the near future.

For information specific to the Google Data Protocol, read the Authentication and Authorization documentation. The .NET library for the Google Data API provides a Service class that can perform authentication and submit API requests. To use the Content API for Shopping, we provide a specialized subclass: ContentForShoppingService. Since the account ID is needed to construct the URL of each request to the Content API for Shopping server and a user agent is needed for each request, the ContentForShoppingService class has a constructor which takes both as arguments:

using Google.GData.ContentForShopping;

string userAgent = "content-api-example";
string accountId = "1234567";
ContentForShoppingService service = new ContentForShoppingService( userAgent, accountId);

Authorizing requests with OAuth 2.0

The details of the authorization process, or "flow," for OAuth 2.0 vary somewhat depending on what kind of application you're writing. The following general process applies to all application types:

  1. When you create your application, you register it with Google. Google then provides information you'll need later, such as a client ID and a client secret.
  2. When your application needs access to user data, it asks Google for a particular scope of access.
  3. Google displays an OAuth dialog to the user, asking them to authorize your application to request some of their data.
  4. If the user approves, then Google gives your application a short-lived access token.
  5. Your application requests user data, attaching the access token to the request.
  6. If Google determines that your request and the token are valid, it returns the requested data.

For detailed information about flows for various types of applications, see Google's OAuth 2.0 documentation. To use OAuth 2.0 with the .NET client library, you can use the OAuth2Parameters and GOAuth2RequestFactory classes from the Google.GData.Client namespace.

Creating a Parameters Object

To retrieve a token, you need your client ID and secret from the APIs Console, and the scope for the Content API for Shopping:

using Google.GData.Client;

string clientId = "bogus.id";  // Provided in the APIs Console
string clientSecret = "SeCr3Tv4lu3";  // Provided in the APIs Console
string scope = "https://www.googleapis.com/auth/structuredcontent";
// Replace the value below with your own callback set in the APIs Console
string redirectUri = "http://www.example.com/oauth2callback";

OAuth2Parameters parameters = new OAuth2Parameters() {
    ClientId = clientId,
    ClientSecret = clientSecret,
    RedirectUri = redirectUri,
    Scope = scope
};

Authorizing a Token With Parameters

After you have created this parameters object, you or the user need to authorize the parameters to make API requests by visiting the OAuth 2.0 authorize URL:

string authorizeUrl = OAuthUtil.CreateOAuth2AuthorizationUrl(parameters);
Console.WriteLine("Please visit: {0}", authorizeUrl);
Note: As mentioned in the OAuth 2.0 documentation, if your application loses the refresh token, then it needs to re-prompt the user for consent before obtaining another refresh token. If you need to re-prompt the user for consent, include the ApprovalPrompt parameter and set the value to force:
OAuth2Parameters parameters = new OAuth2Parameters() {
    ClientId = clientId,
    ClientSecret = clientSecret,
    RedirectUri = redirectUri,
    Scope = scope,
    ApprovalPrompt = "force"
};
// or set parameters.ApprovalPrompt = "force" on an existing object

string authorizeUrl = OAuthUtil.CreateOAuth2AuthorizationUrl(parameters);

Exchanging an Authorization Code for an Access Token

After successfully authorizing, the user is redirected to a page that has a value in the query parameter code containing the needed authorization. You can retrieve the query parameters from the redirect URL and use them to retrieve an access token:

string redirectUrl = "http://www.example.com/oauth2callback?code=SOME-RETURNED-VALUE";
Uri uri = new Uri(redirectUrl);
OAuthUtil.GetAccessToken(uri.Query, parameters);

Using a Token

Finally, to use this token with a service object, you must create a custom request factory that will sign the requests with the token:

string serviceName = "structuredcontent";
string userAgent = "content-api-example";

GOAuth2RequestFactory requestFactory = new GOAuth2RequestFactory(serviceName, userAgent, parameters);
service.RequestFactory = requestFactory;

Note: When an access token expires, the API requests signed with that token are rejected. By using a GOAuth2RequestFactory to generate these requests, the service object can address such rejections. When the said requests fail, the factory uses the parameters to attempt obtaining a new access token (using the refresh token stored in the parameters object). If it successfully obtains a new access token, the service resends the original API request, signed with the new access token.

ClientLogin for installed/mobile applications

Detailed code samples that show how to use a Service instance to authenticate with ClientLogin are provided in the Authentication and Authorization documentation. In order to use the Content API for Shopping, you need to authenticate with the structuredcontent service.

string email = "name@example.com";
string password = "pa$$word";

service.setUserCredentials(email, password);

Once you have called setUserCredentials, the service object will use your credentials to retrieve an authentication token. No request to retrieve said token will be sent until you attempt to make an API request. To authenticate immediately, call service.QueryClientLoginToken().

Creating and Editing a Product Object

The API represents products as XML objects. Each data item starts with an <entry> tag, includes a set of attributes that describe the said item, and ends with the </entry> tag.

Using the .NET client library, we will construct an example product.

Building a Product in .NET

The .NET client library provides a ProductEntry class whose attributes correspond to the XML product attributes. These can be set to instances of custom classes that encapsulate the value of the attributes that you set and the underlying XML namespace. These classes abstract from the creation and the structure of the XML object, allowing you to focus on your data.

The above example doesn't use every single attribute defined by the API; for details on attributes not listed here, check out the elements namespace for the custom data classes defined for the Content API for Shopping.

Before using of the classes provided by the .NET client library, we need to include the namespace containing all the custom attribute classes:

using Google.GData.ContentForShopping.Elements;

We start with an empty <entry> element that is the "container" for all our data:

ProductEntry entry = new ProductEntry();

Elements from the Atom namespace

Since <title>, <content>, and <link> come from the xmlns="http://www.w3.org/2005/Atom" namespace, which is shared across all GData APIs, we also need to import the module with Atom data classes:

using Google.GData.Client;

To set the title and description, we can use custom Atom classes to directly set an attribute on the entry:

entry.Title.Text = "Camera";
entry.Content.Content = "A great compact body to make it easy to get a " +
                        "great shot everytime.";

To set the product link, we proceed a bit differently. The Links attribute of each ProductEntry object is a collection that starts out empty. To add the link, we add to the list of links:

AtomLink link = new AtomLink();
link.Rel = "alternate";
link.Type = "text/html";
link.HRef = "http://www.replace-with-your-homepage.com/item1-info-page.html";
entry.Links.Add(link);

Standard Elements

For the majority of the attributes in the sc and scp namespaces, adding the attribute to the ProductEntry object simply involves setting the attribute with a string or an object from the custom class for that attribute:

entry.ProductId = "123456";
entry.ImageLink = new ImageLink("http://www.example.com/image1.jpg");
entry.TargetCountry = "US";
entry.ContentLanguage = "en";
entry.Brand = "Acme";
entry.Condition = "new";
entry.Gtin = "00013803105384";
entry.Color = "black";
entry.Availability = "in stock";

For the <scp:product_type> element and other attributes that use taxonomy values, the ampersand (&), greater than sign (>), less than sign (<), and some other characters must be escaped as proper XML. Conveniently, this is done automatically by the .NET client library:

entry.ProductType = "Cameras & Optics > Cameras > Digital Cameras";

Elements with Parameters

Many of these classes take a single string in their constructor, but XML elements that also use parameters must be constructed using additional arguments:

entry.Price = new Price("usd", "25");
entry.ShippingWeight = new ShippingWeight("lb", "0.1");

Nested Elements

The <scp:shipping> and <scp:tax> elements are represented as collection attributes on the ProductEntry object, just like the Links attribute. Since they are also nested elements, the corresponding classes for shipping and tax also have their own attributes that can be set:

Shipping shipping = new Shipping();
shipping.Country = "US";
shipping.Region = "MA";
shipping.Service = "Speedy Shipping - Ground";
shipping.Price = new ShippingPrice("usd", "5.95");
entry.ShippingRules.Add(shipping);

Tax tax = new Tax();
tax.Country = "US";
tax.Region = "CA";
tax.Rate = "8.25";
tax.Ship = true;
entry.TaxRules.Add(tax);

Note: The second shipping value and the second tax value can be added in the same fashion.

Finally, the only remaining attribute from the example XML is the required destination. Like tax and shipping, this is contained in a nested element, so setting the attribute is similar. Since it is possible to set more than one required destination, RequiredDestinations is also a collection.

ProductControl productControl = new ProductControl();
productControl.RequiredDestinations.Add(new RequiredDestination("ProductSearch"));
entry.AppControl = productControl;

Editing a Product in .NET

When making requests, you may also be working with ProductEntry objects that you did not construct yourself, but that you retrieved from your online inventory via a request to the Content API. To edit these objects, you can set the corresponding attribute as you did when first building the object. For example, if the price has lowered from $25 USD to $22.50 USD:

entry.Price = new Price("usd", "22.50");

If you have enabled warnings, then you may want to inspect the Warnings attribute on the product object:

Warnings warnings = insertedProduct.AppControl.Warnings;
foreach (Warning warningElt in warnings.Entries)
{
    Console.WriteLine("Code: {0}", warningElt.Code);
    Console.WriteLine("Domain: {0}", warningElt.Domain);
    Console.WriteLine("Location: {0}", warningElt.Location);
    Console.WriteLine("Message: {0}", warningElt.Message);
}

For the product defined above, you may get this output:

Code: validation/missing_recommended
Domain: ProductSearch
Location: google_product_category
Message: We recommend including this attribute.

You can address this error by updating the ProductEntry:

string gpc = "Cameras & Optics > Cameras > Digital Cameras";
entry.GoogleProductCategory = gpc;

Managing items

All actions on items—getting, inserting, updating, and deleting—can be performed with a ContentForShoppingService object. For each action performed with the service object, an account ID and an authentication token must be provided. The account ID is needed to construct the request URL (read more in the reference), and the authentication token is needed to sign each request.

The following code snippets assume that the service object has been constructed with an account ID and has called some authentication method, which has set an authentication token. When the account ID is set, each API request made by the service uses it as a fallback values. You may also include it explicitly as a parameter in each function that makes calls to the Content API for Shopping.

Retrieving all your items

To retrieve the list of data items belonging to your inventory, you make an HTTP GET request to the items feed:

https://content.googleapis.com/content/v1/ACCOUNT_ID/items/products/schema

You can use a service object to create and send this request to the Content API for Shopping:

ProductFeed itemsFeed = service.Query();

The response itemsFeed contains a ProductFeed object. This object represents the <feed> element that contains a list of <entry> elements, one for each item in the feed. The Entries attribute on the ProductFeed object contains the collection of ProductEntry objects returned in the feed:

foreach (ProductEntry entry in itemsFeed.Entries)
{
    Console.WriteLine(entry.Title.Text);
}

Paging through results

For paging through your items, use the max-results and start-token query parameters (see the reference for more details).

To specify the maximum number of data items to be returned, use the NumberToRetrieve attribute of the ProductQuery object. If you don't specify a value, the .NET client library will not append any value to the API request and default value of 25 will be used.

int maxResults = 10;
ProductQuery query = new ProductQuery();
query.NumberToRetrieve = maxResults;
ProductFeed itemsFeed = service.Query(query);

If your product inventory exceeds the maximum number of results requested, you need to submit another request to the Content API for Shopping to get the next page of results. In this case, the returned feed contains a link element resembling:

<link rel="next" type="application/atom+xml"
  href="https://content.googleapis.com/content/v1/ACCOUNT_ID/items/products/schema?start-token=8234f784af4&amp;max-results=10"/>

This link contains a token pointing to the next page of results. You can retrieve it from the feed and set the StartToken attribute on the query object:

query.StartToken = itemsFeed.StartToken; // Using the previous value of {query}
ProductFeed itemsFeed = service.Query(query);

Note: The parameter max-results cannot exceed 250.

Retrieving a single item

You can retrieve a specific item's data by executing an HTTP GET request to its unique URL. This is formed using the item's channel, language, target country, and ID you submitted with the item. To retrieve the item from the above example, the request URL is:

https://content.googleapis.com/content/v1/ACCOUNT_ID/items/products/schema/online:en:US:123456

To retrieve this item using the .NET client library:

string language = "en";
string country = "US";
string productId = "123456";
ProductEntry entry = service.Get(language, country, productId);

Inserting items

After creating the product item you want to insert, use a service object to send a request to the Content API for Shopping:

ProductEntry insertedProduct = service.Insert(entry);

If the insert succeeds, the variable insertedProduct contains a ProductEntry object parsed from the response. The server returns the item you just inserted, together with some additional attributes for your entry, such as the Atom id element and the published date and time.

Updating items

Updating an item is very similar to inserting it. The only difference with inserting a product is that it first checks if the product is already present in your inventory. By sending an HTTP PUT request to the URL of the item, you will overwrite the old item. The request therefore needs to contain all of the attributes, even those you don't want to change.

To update an item, you can use a service object to send a request to the Content API for Shopping:

ProductEntry updatedProduct = service.Update(entry);

How the value entry is constructed may vary. After inserting a product, you may want to edit the product based on warnings returned from the Content API for Shopping server. Alternatively, changes in your inventory may cause you to first retrieve an item so that you may update its contents.

As with inserting, if the update request succeeds, the variable updatedProduct contains a ProductEntry object parsed from the response.

Note: The .NET client library uses the EditUri attribute of the entry as the URL for the PUT request.

Deleting items

For deleting an item, we simply send a delete request to its URL:

service.Delete(entry);  // return type is void

Rather than a ProductEntry element, there is no returned value. If a delete is not successful, an exception will be thrown.

Note: The .NET client library uses the EditUri attribute of the entry as the URL for the DELETE request.

Batching

When sending many requests, it is better to combine them into a single batch request instead of sending them individually. Batching reduces the time to process your requests and additionally reduces the load on your servers. For a general introduction to batching, read the GData documentation.

To perform batching with the Content API for Shopping server, create a list of entries to be modified (inserted, updated, deleted) in one batch. Each entry of the list can be a normal ProductEntry object, except that additionally the BatchData attribute must be set with the desired operation and other batch information.

Creating a list of entries to be modified

To tell the Content API for Shopping server what to do with the products in the list that you send as a batch, that is, if it should insert, update, or delete them, you need to add this information to every single product by setting its batch:operation field. Additionally, for update and delete requests, you need to set the atom:id field to the edit link, so that the server can identify the product. For example:

// Retrieve a product to update
string language = "en";
string country = "US";
string productId = "123456";
ProductEntry entry = service.Get(language, country, productId);

// Add batch attributes to the product
entry.BatchData = new GDataBatchEntryData(GDataBatchOperationType.update);
entry.Id = new AtomId(entry.EditUri.ToString());

You may set the batch:id field to any value, so that you can more easily identify the products in the server's response. In case of products, it usually makes sense to set the batch ID (batch:id) to the same value as the product ID (sc:id):

if (entry.BatchData != null)
{
    entry.BatchData.Id = entry.ProductId;
}
else
{
    entry.BatchData = new GDataBatchEntryData(entry.ProductId,
                                              GDataBatchOperationType.update);
}

Batch insert

To submit a batch of items, all of which need to be inserted, the service object has a special method that will handle the setting of the batch operation.

If you have created two items—entry1 and entry2—you can insert them as follows:

using System.Collections.Generic;

List products = new List {entry1, entry2};
ProductFeed insertedFeed = service.InsertProducts(products);

foreach (ProductEntry entry in insertedFeed.Entries)
{
    Console.WriteLine("{0} inserted with code {1}",
                      entry.Title.Text,
                      entry.BatchData.Status.Code);
}

To set the batch ID (batch:id) to the product ID (sc:id) or some other value, you'll need to do so on each ProductEntry before calling InsertProducts.

Batch update

As described above, a batch update can be accomplished by appending the batch operation and the product ID to the existing product data (with changes/updates included). As with inserting, to submit a batch of items, all of which need to be updated, the service object has a special method that will handle the setting of the batch operation.

For a ProductEntry object returned from the API, the "edit" link will contain the unique product URL; proceeding as above:

entry.Id = new AtomId(entry.EditUri.ToString());

If the edit link is not set, you need to construct the unique product URL by hand; this can be done using your account ID, along with the ProductId, TargetCountry, and ContentLanguage attributes of the entry.

After setting the atom:id field, you can update the items as follows:

List products = new List {entry1, entry2};
ProductFeed updatedFeed = service.UpdateProducts(products);

foreach (ProductEntry entry in updatedFeed.Entries)
{
    Console.WriteLine("{0} updated with code {1}",
                      entry.Title.Text,
                      entry.BatchData.Status.Code);
}

Batch delete

As described in the GData documentation, a batch delete can be accomplished by including only the batch operation and the product ID. The product URL in the atom:id element should be set to the value of the rel="edit" link of the product as in the updating section.

To submit a batch of items, all of which need to be deleted, the service object has a special method that will handle the setting of the batch operation:

List products = new List {entry1, entry2};
ProductFeed deletedFeed = service.DeleteProducts(products);

foreach (ProductEntry entry in deletedFeed.Entries)
{
    Console.WriteLine("{0} deleted with code {1}",
                      entry.Title.Text,
                      entry.BatchData.Status.Code);
}

Mixed Batch requests

A batch request can contain any combination of insert, update, and delete entries. You can use one of the client methods defined above (API_VERBProducts), or manually set BatchData on each ProductEntry object, followed by adding every ProductEntry to an empty ProductFeed object.

For example, to mix different operations with three entries (insertEntry, updateEntry, and deleteEntry):

ProductFeed feed = new ProductFeed(null, service);

insertEntry.BatchData = new GDataBatchEntryData(GDataBatchOperationType.insert);
feed.Entries.Add(insertEntry);

updateEntry.BatchData = new GDataBatchEntryData(GDataBatchOperationType.update);
updateEntry.Id = new AtomId(updateEntry.EditUri.ToString());
feed.Entries.Add(updateEntry);

deleteEntry.BatchData = new GDataBatchEntryData(GDataBatchOperationType.delete);
deleteEntry.Id = new AtomId(deleteEntry.EditUri.ToString());
feed.Entries.Add(deleteEntry);

// Assumes AccountId and Projection are set on service
ProductFeed mixedFeed = service.Batch(feed);

A request may not contain two or more actions that affect the same product. If it does, there is not necessarily an error message, but the outcome is undefined.

If you are inserting a new product that has the same sc:id as another one of your products that is already stored on the server, then the new product overwrites the previous one. This is another way of updating products.

Error Handling

The examples above do not contain any error-handling code to keep things simple. For more details on the errors that the Content API for Shopping server may return, see the error code list.

Errors on Individual Items

If a non-batch request is rejected by the Content API for Shopping server, the service method throws a GDataRequestException. If you encounter a different error, it is likely caused by an issue other than the .NET client library.

You can catch a GDataRequestException and recover the body of the rejected request in the ResponseString attribute:

try
{
    ProductEntry inserted = service.Insert(entry);
}
catch (GDataRequestException exc)  // from Google.GData.Client namespace
{
    string errorResponse = exc.ResponseString;
}

You can parse the errors returned by the Content API for Shopping server into an instance of the BatchErrors class:

using System.Xml;

XmlReader reader = new XmlTextReader(errorResponse, XmlNodeType.Document, null);
XmlDocument xmlDocument = new XmlDocument();
XmlNode node = xmlDocument.ReadNode(reader);

BatchErrors batchErrors = new BatchErrors();
AtomFeedParser parser = new AtomFeedParser();
batchErrors.ProcessChildNodes(node, parser);

// Iterable Collection of BatchError objects
ExtensionCollection errors = batchErrors.Errors;

Batch Errors

In a batch request, many operations are combined into one request. Some of these operations may succeed, while some may fail. When batching, the batch request should return an HTTP success code, even if there were errors for individual products. In the batch response, the result of each operation is represented as an <entry> element. For operations that fail, the errors are returned in the following element:

<content type='application/vnd.google.gdata.error+xml'>
For such errors, the reason field contains the error message you would have received, had you sent the product as an individual request.

To determine if an operation has failed or succeeded, you can check the response code from the <batch:status> element. If the code is something other than 200 or 201, there will be an errors block within the <content> element, which you can access with a custom method from the ProductEntry class:

using Google.GData.Extensions;

ProductFeed feed = service.InsertProducts(products);

foreach (ProductEntry errorEntry in feed.Entries)
{
    if (errorEntry.BatchData.Status.Code != 200 &&
        errorEntry.BatchData.Status.Code != 201)
    {
        // Custom
        ExtensionCollection errors = errorEntry.BatchErrors;
    }
}

If an errors element is not found, errors is null; if it is found, a collection of BatchErrors is returned.

In rare cases, a batch interruption can occur. In this case, you get an entry returned that doesn't contain any product or batch:id, but instead contains the field batch:interrupted. This means that all products that are not reported as processed, have been dropped.

Inspecting Errors

The errors returned by the Content API for Shopping server are in the gd:errors element with a list of gd:error elements corresponding to each error. These errors contain similar information as the warnings elements:

foreach (BatchError error in errors)
{
    Console.WriteLine("Code: {0}", error.Code);
    Console.WriteLine("Domain: {0}", error.Domain);
    Console.WriteLine("Location: {0}", error.Location.Value);
    Console.WriteLine("InternalReason: {0}", error.InternalReason);
}

Warnings and Dry-run Mode

The Content API for Shopping allows you to run all the requests that modify persistent data (insert, update, delete) in dry-run mode. A dry-run request is similar to a normal request (authentication checks are performed as well as validity checks of the operation and the item), but the final step of persisting the changes is skipped. Dry-run mode is intended to check the validity of your requests. For update and delete operations, the validity of the request is determined independently of the item's existence.

You can set a single request or all operations in a batch to be executed in dry-run, by appending the dry-run query parameter at the end of the batch URL request. The .NET client library does this for you by providing a DryRun attribute on the ContentForShoppingService class. This argument defaults to false, but when set to true, the parameter is appended to the query. For example, to test if an insert of entry will succeed:

service.DryRun = true;
ProductEntry insertedProduct = service.Insert(entry);

The Content API for Shopping can also return warnings, which allow some non-fatal product data issues to be detected.

As with the dry-run mode, warnings are opt-in, and should be enabled by setting the ShowWarnings attribute on the service object. If this is provided, additional warning elements are returned. As noted in the editing section, warnings can be used to make edits to improve your product data. To receive warnings:

service.ShowWarnings = true;
ProductEntry insertedProduct = service.Insert(entry);

When used, the warnings appear in the app:control element:

Warnings warnings = insertedProduct.AppControl.Warnings;

Accessing Product Performance Data

The Content API for Shopping allows users to view data on how often products have been clicked on Google (for example, Google Shopping and Google Book Search).

To access this data, add the query parameters performance.start and performance.end to the GET request URI when retrieving a list of products. Each of these parameters should be a date, one for the start, and one for the end of the required period of performance data.

This is enabled in the ProductQuery class via the PerformanceStart and PerformanceEnd attributes. To specify these dates, you can use a string date represented as "YYYY-MM-DD". For example, to retrieve the product data for your first 5 products from January 1, 2012 to January 5, 2012:

ProductQuery query = new ProductQuery();
query.NumberToRetrieve = 5;
query.PerformanceStart = "2012-01-01";
query.PerformanceEnd = "2012-01-05";
ProductFeed itemsFeed = service.Query(query);

By enabling this, we can access the performance data for each parsed ProductEntry in the result itemsFeed using the Performance attribute:

foreach (ProductEntry entry in itemsFeed.Entries)
{
    Console.WriteLine(entry.Title.Text);

    Performance performance = entry.Performance;
    foreach (Datapoint datapoint in performance.Datapoints)
    {
        Console.WriteLine("{0} paid clicks on {1}",
                          datapoint.PaidClicks,
                          datapoint.Date);
    }
}

This attribute contains a Performance object that holds a collection of Datapoint objects, each of which has Clicks and Date set.

The generic projection

The schema projection is used in all of the examples above, but the .NET client library can also handle the generic projection. Product data returned from the generic projection does not have any scp elements; they are all replaced by generic attributes.

Instead of the scp attributes, we get a list of generic attributes (sc:attribute) and generic attribute groups (sc:group).

For example, rather than

<scp:availability>in stock</scp:availability>
you receive
<sc:attribute name="availability">in stock</sc:attribute>

For a group example, rather than

<scp:tax>
  <scp:tax_country>US</scp:tax_country>
  <scp:tax_region>CA</scp:tax_region>
  <scp:tax_rate>8.25</scp:tax_rate>
  <scp:tax_ship>true</scp:tax_ship>
</scp:tax>
you receive
<sc:group name="tax">
  <sc:attribute name="country">US</sc:attribute>
  <sc:attribute name="region">CA</sc:attribute>
  <sc:attribute name="rate">8.25</sc:attribute>
  <sc:attribute name="tax ship">true</sc:attribute>
</sc:group>

The .NET client library represents them with the CustomAttribute and CustomGroup classes. To construct the tax group above and add it to a product entry:

CustomAttribute country = new CustomAttribute("country", "US");
CustomAttribute region = new CustomAttribute("region", "CA");
CustomAttribute rate = new CustomAttribute("rate", "8.25");
CustomAttribute taxShip = new CustomAttribute("tax ship", "true");

CustomGroup taxGroup = new CustomGroup("tax");
taxGroup.CustomAttributes.Add(country);
taxGroup.CustomAttributes.Add(region);
taxGroup.CustomAttributes.Add(rate);
taxGroup.CustomAttributes.Add(taxShip);

entry.Groups.Add(taxGroup);

We only used the two arguments in the constructor for CustomAttribute, which sets only the value and the name attribute of the sc:attribrute XML elements. It is also possible to use other constructors or to directly set the Type and Unit attributes on each CustomAttribute.

The advantage of this method is that it can be used for products as well as other item categories that might be available in the future. The downside is that locating and using returned attributes becomes more complicated, potentially slower, and error-prone, because of the unsafe conversion logic from standard attributes to generic attributes.

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.