Cook Computing

UPC Database XML-RPC

March 18, 2011 Written by Charles Cook

The Internet UPC Database is a public database of products and their Universal Product Codes. The site provides an XML-RPC interface and following some recently enquires about how to access this using XML-RPC.NET I thought I'd provide some sample code. I've only implemented three methods but implementing the others would follow the same pattern.

The methods of the UPC Database XML-RPC endpoint take a single XML-RPC struct which contains various parameters according to the method being called, but always an rpc_key value. To avoid creating a datatype for each method, we can use a single datatype and set only the required parameters:

[XmlRpcMissingMapping(MappingAction.Ignore)]
public class UpcParameters
{
  public string rpc_key;
  public string upc;
  public string search;
  public int? max_results;
  // ... parameters for other methods
}

The Ignore mapping action means that if any of the parameters are null, they will be ignored when UpcParameters is serialized into an XML-RPC struct. This ensures that only the parameters necessary for that call are included in the struct. (In the next release use of this attribute for outgoing structs is superceded by the XmlRpcNullMapping attribute which has a more self-explantory name and also supports MappingAction.Nil for mapping members with null value to the <nil /> element; but XmlRpcMissingMapping will still be used for incoming structs.)

The service methods all return an XML-RPC struct which always includes message and status members. So we can use a base class containing these two members and derive classes for each method:

public class UpcResults
{
  public string message;
  public string status;
}

[XmlRpcMissingMapping(MappingAction.Ignore)]
public class HelpResults : UpcResults
{
  public string help;
}

public class UpcSearchResult
{
  public string ean;
  public string description;
  public string size;
}

[XmlRpcMissingMapping(MappingAction.Ignore)]
public class UpcSearchResults : UpcResults
{
  public int? result_count;
  public string search;
  public DateTime? noCacheAfterUTC;
  public UpcSearchResult[] results;
  public int? max_results;
}

[XmlRpcMissingMapping(MappingAction.Ignore)]
public class UpcLookupResults : UpcResults
{
  public string upc;
  public string description;
  public string issuerCountry;
  public string issuerCountryCode;
  public DateTime? lastModifiedUTC;
  public int? pendingUpdates;
  public bool? isCoupon;
  public string ean;
  public string size;
  public bool? found;
}

Note that nullable value types are used so that if a particular member is not present in a results struct it will be have a default value of null when deserialized to the .NET type. We can now define the interface:

[XmlRpcUrl("http://www.upcdatabase.com/xmlrpc")]
public interface UPChelp : IXmlRpcProxy
{
  [XmlRpcMethod]
  UpcLookupResults lookup(UpcParameters parameters);
  [XmlRpcMethod]
  HelpResults help(UpcParameters parameters);
  [XmlRpcMethod]
  UpcSearchResults search(UpcParameters parameters);
  // ... other methods
}

A proxy can then be created and the methods called (set variable key to your own rpc key):

UPChelp upcObj = XmlRpcProxyGen.Create<UPChelp>();

HelpResults help = upcObj.help(new UpcParameters { rpc_key = key });

UpcSearchResults ret = upcObj.search(
  new UpcParameters { rpc_key = key, search = "world" });

UpcLookupResults myLookup = upcObj.lookup(
  new UpcParameters { rpc_key = key, upc = "639382000393" });