Ugly Stool Rotating Header Image

WCF REST Client for SmugMug – 2 of n

In the last post I gave a brief overview of the SmugMug API, and what I wanted WCF to do.  To summarize, I wanted WCF to provide two things: strongly typed objects, and exceptions.

My first attempt to avoid XML parsing was to create an XML schema.  I had hoped to use an XML schema from SmugMug, but unfortunately I could not find one.  Furthermore, the SmugMug API wiki does not provide enough detail to create an XML schema.  SmugMug does a good, but not a great job of documenting the XML responses for all of the API methods.  (Granted, this is a tedious and laborious task, so it is understandable.)

The easiest way I found to generate an XML schema was to get example XML data directly from SmugMug by hand-crafting URI’s and using cURL.

The skeleton of my first attempt at an XML schema is shown below.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:complexType name="Albums"/>
  <xs:complexType name="Images"/>
  <xs:complexType name="ImageExif"/>
  <xs:complexType name="Response">
    <xs:sequence>
      <xs:element name="method" type="xs:string"/>
      <xs:choice>
        <xs:element name="Albums" type="Albums"/>
        <xs:element name="Images" type="Images"/>
        <xs:element name="Image" type="ImageExif"/>
      </xs:choice>
    </xs:sequence>
  </xs:complexType>
  <xs:element name="rsp" type="Response"/>
</xs:schema>

SmugMug wraps all objects in a response (<rsp/>) element, but only one object is returned in a response, where an object is Albums, Images, or Image.  This is modeled in the schema with a choice element.  The xsd.exe tool does not generate desirable code based on the XML schema shown above.  The generated code is shown below.

[XmlElementAttribute("Albums", typeof(Albums)]
[XmlElementAttribute("Image", typeof(ImageExif)]
[XmlElementAttribute("Images", typeof(Images)]
public object Item {
    get {
        return this.itemField;
    }
    set {
        this.itemField = value;
    }
}

The type information for Albums, Images, and Image has been condensed to a C# object ((System.Object) because it is the only type common to Albums, Images, an Image.  (I potentially could have solved this issue using derivation in the schema if there was commonality across all of SmugMug types I cared about, but I had not bothered to investigate.)

Instead of using a choice element to model the fact that only one object is returned, I could have used a mixture of minOccurs and maxOccurs.  An example schema is shown below.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:complexType name="Albums"/>
  <xs:complexType name="Images"/>
  <xs:complexType name="ImageExif"/>
  <xs:complexType name="Response">
    <xs:sequence>
      <xs:element name="method" type="xs:string"/>
      <xs:element minOccurs="0" maxOccurs="1" name="Albums" type="Albums"/>
      <xs:element minOccurs="0" maxOccurs="1" name="Images" type="Images"/>
      <xs:element minOccurs="0" maxOccurs="1" name="Image" type="ImageExif"/>
    </xs:sequence>
  </xs:complexType>
  <xs:element name="rsp" type="Response"/>
</xs:schema>

The schema duplicates the behavior of the choice element, but forces the xsd.exe tool to generate type information for the SmugMug objects.  The generated code is shown below.

[XmlElementAttribute]
public Albums Albums {
    get {
        return this.albumsField;
    }
    set {
        this.albumsField = value;
    }
}
[XmlElementAttribute]
public Images Images {
    get {
        return this.imagesField;
    }
    set {
        this.imagesField = value;
    }
}
[XmlElementAttribute]
public ImageExif Image {
    get {
        return this.imageField;
    }
    set {
        this.imageField = value;
    }
}

This code is closer to what I want.  The type is explicit – no casting required.  The objection is the fact that there are potentially many dead types to choose from.  If I am fetching Albums, why should the code ever have to consider anything but the list of Albums.  The calling code has to keep track of the method invoked, and then switch based on the SmugMug method to determine what property to interrogate.  Potentially, the client code devolves into the following.

Response response = this.InvokeSmugMugMethod();
switch (response.method)
{
    case "smugmug.albums.get":
        // do something with albums
        break;
    case "smugmug.images.get";
        // do something with images
}

In my next post I will describe the solution I used to solve these issues.

Comments are closed.

Page optimized by WP Minify WordPress Plugin