Controlling How Data Is Transferred in Web Services


When data is returned from a Web Service call, it is serialized into XML, meaning that you can only return native types, or types that know how to serialize themselves (such as the DataSet). If you have a strongly typed business layer, and you wish to return this as the result of a Web method, the first thing you need to do is make sure that the class has a parameterless constructorotherwise it cannot be serialized.

Next, you must make sure that all properties you wish to serialize are read-write, because read-only properties are ignored during serialization, as are methods. Consider Listing 16.8, which shows a simple class for use in a Web Service.

Listing 16.8. The Shipper Class

public class Shipper {   private int _id;   private string _companyName;   private string _phone;   public Shipper()   {   }   public Shipper(int id, string companyName, string phone)   {     _id = id;     _companyName = companyName;     _phone = phone;   }   public int ID   {     get { return _id; }     set { _id = value; }   }   public string CompanyName   {     get { return _companyName; }     set { _companyName = value; }   }   public string Phone   {     get { return _phone; }     set { _phone = value; }   } }

When an instance of this class is returned from a Web Service, the XML contains an element for each property, as seen in Listing 16.9.

Listing 16.9. The Serialized Shipper Class

<?xml version="1.0" encoding="utf-8"?> <Shipper xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns:xsd="http://www.w3.org/2001/XMLSchema"   xmlns="http://aspnetillustrated.org/services/">   <ID>5</ID>   <CompanyName>Tortoise Inc</CompanyName>   <Phone>555 123 1234</Phone> </Shipper>

Customizing Serialization

You can change the way the XML is generated by attributing the class and properties. For example, consider Listing 16.10. The class has an XmlRoot attribute, which defines the name of the root element. The ID property has the XmlAttribute attribute, specifying the AttributeName, which will make the property become an XML attribute on the parent element. The Phone property has the XmlElement attribute, specifying the ElementName and that it can contain a null value.

Listing 16.10. The Shipper Class Attributed for Serialization

[XmlRoot("RegionalShipper")] public class Shipper {   private int _id;   private string _companyName;   private string _phone;   public Shipper()   {   }   public Shipper(int id, string companyName, string phone)   {     _id = id;     _companyName = companyName;     _phone = phone;   }   [XmlAttribute(AttributeName = "ShipperID")]   public int ID   {     get { return _id; }     set { _id = value; }   }   public string CompanyName   {     get { return _companyName; }     set { _companyName = value; }   }   [XmlElement(ElementName = "PhoneNumber",     IsNullable = true)]   public string Phone   {     get { return _phone; }     set { _phone = value; }   } }

When the Shipper class is returned from a Web Service, the custom attributes are taken into account, resulting in the XML shown in Listing 16.11.

Serializing Collections

Collections are serialized to structured XML, with the parent element having a default name of ArrayOfClass, where Class is the name of the class being serialized. For example, Listing 16.12 shows the results of a Web Service that returns a generic list of Shipper objects (List<Shipper>).

Listing 16.11. The Serialized Shipper Class with Customized Serialization

<?xml version="1.0" encoding="utf-8"?> <RegionalShipper   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns:xsd="http://www.w3.org/2001/XMLSchema"   Shipper   xmlns="http://aspnetillustrated.org/services/">   <CompanyName>Tortoise Inc</CompanyName>   <PhoneNumber>555 123 1234</PhoneNumber> </RegionalShipper>

Listing 16.12. A Serialized Collection

<?xml version="1.0" encoding="utf-8"?> <ArrayOfShipper xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns:xsd="http://www.w3.org/2001/XMLSchema"   xmlns="http://aspnetillustrated.org/services/">   <Shipper Shipper>     <CompanyName>Speedy Express</CompanyName>     <PhoneNumber>(503) 555-9831</PhoneNumber>   </Shipper>   <Shipper Shipper>     <CompanyName>United Package</CompanyName>     <PhoneNumber>(503) 555-3199</PhoneNumber>   </Shipper>   <Shipper Shipper>     <CompanyName>Federal Shipping</CompanyName>     <PhoneNumber>(503) 555-9931</PhoneNumber>   </Shipper> </ArrayOfShipper>

You can control the top element by adding an additional attribute to the Web method. For example, Listing 16.13 shows that you can specify the name of the element returned.

Listing 16.13. Specifying the Returned Element Name

[WebMethod] [return: XmlElement("Shippers")] public List<Shipper> GetShippers()

The top-level element would no longer be ArrayOfShipper, but Shippers. Note that this only seems to affect SOAP requests, and not HTTP POST requests, which the Web Service Help Page uses. You can tell this by examining the WSDL or the sample output on the help page.

You can also use the XmlElement attribute to remove a layer of indirection from within the generated XML. For example, consider the class in Listing 16.14, which is a collection of Shipper objects.

Listing 16.14. The ShipperCollection Class

public class ShipperCollection {   private List<Shipper> _items;   public ShipperCollection()   {     _items = new List<Shipper>();   }   public List<Shipper> Items   {     get { return _items; }     set { _items = value; }   } }

Listing 16.15 shows that the results of the ShipperCollection class is returned from a Web method, and you can see that there is a redundant Items element.

Listing 16.15. The ShipperCollection Class Serialized

<?xml version="1.0" encoding="utf-8"?> <ShipperCollection   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns:xsd="http://www.w3.org/2001/XMLSchema"   xmlns="http://aspnetillustrated.org/services/">   <Items>     <Shipper Shipper>       <CompanyName>Speedy Express</CompanyName>       <PhoneNumber>(503) 555-9831</PhoneNumber>     </Shipper>     <Shipper Shipper>       <CompanyName>United Package</CompanyName>       <PhoneNumber>(503) 555-3199</PhoneNumber>     </Shipper>     <Shipper Shipper>       <CompanyName>Federal Shipping</CompanyName>       <PhoneNumber>(503) 555-9931</PhoneNumber>     </Shipper>     <Shipper Shipper>       <CompanyName>Tortoise Couriers</CompanyName>       <PhoneNumber>12555 123 456</PhoneNumber>     </Shipper>     <Shipper Shipper>       <CompanyName>Tortopoise Inc</CompanyName>       <PhoneNumber>555 123 1234</PhoneNumber>     </Shipper>   </Items> </ShipperCollection>

The ShipperCollection is the root element, with the Items property having an element, and then each Shipper an element of its own. To remove the redundant Items element, you can use the XmlElement attribute on the Items property in the collection (Listing 16.16), resulting in the Shipper elements becoming a child of the ShipperCollection.

Listing 16.16. Attributing the Collection

[XmlElement(ElementName = "Shipper")] public List<Shipper> Items

Collections and arrays within classes can also be controlled with a variety of attributes such as XmlArray and XmlArrayItem to dictate how the array and items should be serialized.

Manual Serialization

By default, ASP.NET will perform the serialization and schema creation for you, but you can do this yourself, if necessary, by implementing IXmlSerializable. For example, consider Listing 16.17, which implements this interface. Unlike ASP.NET 1.x, you don't use the GetSchema method to define the schema; instead, you specify the method that supplies the schema with the XmlSchemaProvider attribute on the class. The ReadXml and WriteXml methods perform the actual serialization.

Listing 16.17. Manually Serializing Classes

[XmlSchemaProvider("CustomShipperSchema")] public class CustomShipper : IXmlSerializable {   // standard properties and methods   public static XmlQualifiedName     CustomShipperSchema(XmlSchemaSet set)   {     XmlSchema s = new XmlSchema();     s.Id = "Test";     s.TargetNamespace = "urn:types-nw-com";     XmlSchemaComplexType t = new XmlSchemaComplexType();     t.Name = "CustomShipper";     XmlSchemaElement shipper = new XmlSchemaElement();     shipper.Name = "shipper";     XmlSchemaElement id = new XmlSchemaElement();     id.Name = "id";     id.Parent = shipper;     XmlSchemaElement name = new XmlSchemaElement();     name.Name = "shipperName";     name.Parent = shipper;     XmlSchemaElement phone = new XmlSchemaElement();     phone.Name = "phone";     phone.Parent = shipper;     XmlQualifiedName n = new       XmlQualifiedName(t.Name, s.TargetNamespace);     shipper.SchemaTypeName = n;     s.Items.Add(t);     s.Items.Add(shipper);     set.Add(s);     return n;   }   // this is not used in v 2.0 of the framework   // instead the method defined by the attribute sets the schema   public XmlSchema GetSchema()   {       return null;   }   public void ReadXml(XmlReader reader)   {     XmlNodeType type = reader.MoveToContent();   if (type == XmlNodeType.Element &&     reader.LocalName == "shipper")   {     reader.ReadToDescendant("id");     _id = int.Parse(reader.Value);     reader.ReadToNextSibling("shipperName");     _companyName = reader.Value;     reader.ReadToNextSibling("phone");     _phone = reader.Value;   } } public void WriteXml(XmlWriter writer) {   writer.WriteStartElement("shipper", "urn:nw-com");   writer.WriteElementString("id", _id.ToString());   writer.WriteElementString("shipperName", _companyName);   writer.WriteElementString("phone", _phone);   writer.WriteEndElement(); }

This technique gives you complete control over the schema generated, and exactly what is serialized, and can be useful in interoperability scenarios.



ASP. NET 2.0 Illustrated
ASP.NET 2.0 Illustrated
ISBN: 0321418344
EAN: 2147483647
Year: 2006
Pages: 147

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net