Controlling XML Serialization

Defining the Root PurchaseOrder Datatype

The first step is to define the PurchaseOrder class, which will represent the root element within the body of the SOAP message. Recall from Chapter 4 that you can set elements to null by setting the xsi:nil attribute to true. For the SOAP message to be valid, it must not contain a null reference to the purchase order.

The following code defines the PurchaseOrder class:

[XmlRoot(IsNullable=false)] public class PurchaseOrder {     // Additional type definitions... }

To define the PurchaseOrder XML datatype, I need to define a public class by the same name. I also need to ensure that the PurchaseOrder element cannot contain a null value. I can do this by decorating the class with the XmlRoot attribute.

One of the properties exposed by the XmlRoot attribute is IsNullable. This property indicates whether the schema generated by XML serialization will permit instance documents to contain a PurchaseOrder element with a null value.

You can also use the XmlRoot attribute to control the behavior of XML serialization. Table 7-2 describes the properties exposed by the XmlRoot attribute.

Table 7-2  XmlRootAttribute Properties

Property

Description

DataType

Specifies the XML datatype in which the class should be encoded.

ElementName

Specifies the name of the root XML element.

Form

Specifies whether the XML element must be namespace qualified. It is set to one of three values defined by the XmlSchemaForm enumeration: None, Qualified, or Unqualified.

IsNullable

Specifies whether the value of the XML element can be set to xsd:nil.

Namespace

Specifies the XML namespace in which the root element is qualified.

Most of the elements within the PurchaseOrder document contain attributes or child elements themselves. For example, the BillingAddress and ShippingAddress elements each contain Name, Street, City, State, and ZipCode child elements, as shown here:

<PurchaseOrder>   <BillingAddress>     <Name accountNumber="12345">ABC Company</Name>     <Street>123 Some Street</Street>     <City>Some Town</City>     <State>CO</State>     <ZipCode>80427</ZipCode>   </BillingAddress>   <ShippingAddress>     <Name accountNumber="12345">ABC Company</Name>     <Street>123 Some Street</Street>     <City>Some Town</City>     <State>CO</State>     <ZipCode>80427</ZipCode>   </ShippingAddress>   <!-- Additional type definitions... --> </PurchaseOrder>

You can model complex structures such as the preceding one using a class hierarchy. The following example defines an Address class and then defines two fields within the PurchaseOrder class, BillingAddress and ShippingAddress. Both fields are defined as type Address.

[XmlRoot(IsNullable=false)] public class PurchaseOrder {     [XmlElement(IsNullable=true, DataType="normalizedString")]     public string Comments;     [XmlElement(IsNullable=false)]     public Address BillingAddress;     [XmlElement(IsNullable=false)]     public Address ShippingAddress;     // Additional type definitions... } public class Address {     public CompanyName Name;     public string Street;     public string City;     public string State;     public string ZipCode; }

The preceding code uses the XmlElement attribute to control how properties and fields are serialized as elements within the resulting XML document. For example, the Comments field can contain a null value, but neither the ShippingAddress field nor the BillingAddress field can be set to null. Like the XmlRoot attribute, the XmlElement attribute exposes an IsNullable property.

The Comments element can be set to null because XML serialization does not allow you to specify that a particular element or attribute be able to optionally occur within a document. This is because the XmlElement attribute does not provide a means of explicitly setting the minOccurs and maxOccurs constraints on an element. I discuss another potential workaround for this situation later in the chapter.

The Comments element contained within the PurchaseOrder document contains text related to the order. In addition to setting the XmlElement attribute's IsNullable property, the preceding code sets the DataType property for the Comments field.

XML serialization provides a default mapping between .NET types and the built-in datatypes defined by XML Schema. For example, the Comments property is defined as type String. By default, XML serialization will transform the String type to the XML built-in datatype string.

Suppose the back-end system that will record the receipt of the purchase order does not accept linefeeds or tabs. In order to communicate this to the client, I set the datatype of the Comments element to normalizedString. By definition, normalizedString cannot contain linefeeds or tabs.

XML serialization supports all XML built-in datatypes. Table 7-3 lists the supported mappings between XML datatypes and .NET types.

Table 7-3  Mapping Between XML Datatypes and .NET Types

XML Datatype

.NET Type

XML Datatype

.NET Type

anyUri

String

IDREFS

String

base64Binary

Byte (array)

Int

Int32

boolean

Boolean

language

String

byte

SByte

long

Int64

CDATA

String

Name

String

date

DateTime

NCName

String

dateTime

DateTime

negativeInteger

String

decimal

Decimal

NMTOKEN

String

double

Double

NMTOKENS

String

duration

String

nonNegativeInteger

String

ENTITY

String

nonPositiveInteger

String

ENTITIES

String

normalizedString

String

float

Single

NOTATION

String

gDay

String

positiveInteger

String

gMonth

String

QName

XmlQualifiedName

gMonthDay

String

string

String

gYear

String

short

Int16

gYearMonth

String

time

DateTime

hexBinary

Byte (array)

token

String

ID

String

unsignedByte

Byte

IDREF

String

unsignedInt

UInt32

Even though XML serialization will transform an XML datatype into its corresponding .NET type, it will not perform any validation on the data. For example, the XML datatype integer maps to the String .NET type, but the client can pass non-numeric data to the Web service. Therefore, it is up to the Web service to enforce additional constraints over and above what is provided by the .NET type.

The XmlElement attribute exposes additional properties that you can use to control how a .NET type is serialized to XML. Table 7-4 describes these properties.

Table 7-4   XmlElementAttribute Properties

Property

Description

DataType

Specifies the XML Schema built-in datatype in which the property or field should be encoded.

ElementName

Specifies the name of the XML element.

Form

Specifies whether the XML element must be namespace qualified. It is set to one of three values defined by the XmlSchemaForm enumeration: None, Qualified, or Unqualified.

IsNullable

Specifies whether the value of the XML element can have its xsi:nil attribute set to true.

Namespace

Specifies the XML namespace in which the element is defined.

Type

Specifies the .NET type that should be used to generate the schema that describes the element.

You can decorate a property or a field with more than one XmlElement attribute. Doing so specifies that an instance document must contain an element that complies with the criteria specified by one of the XmlElement attributes. Here is an example:

public class Person {     [XmlElement("SocialSecurityNumber")]     [XmlElement("DriversLicenseNumber")]     public string Identifier;     public string Name; }

The preceding .NET type definition creates the following XML datatype definition:

<s:complexType name="Person">   <s:sequence>     <s:choice minOccurs="1" maxOccurs="1">       <s:element minOccurs="1" maxOccurs="1" name="DriversLicenseNumber"       type="s:string" />       <s:element minOccurs="1" maxOccurs="1" name="SocialSecurityNumber"       type="s:string" />     </s:choice>     <s:element minOccurs="1" maxOccurs="1" name="Name" nillable="true"     type="s:string" />   </s:sequence> </s:complexType>

An instance of the Person XML datatype must contain the person's name as well as the person's driver's license number or social security number.

You can use the XmlType attribute to control how .NET types are serialized to an XML datatype, and you can apply the attribute to a variety of .NET type definitions, including classes, structures, enumerations, and interface definitions. You can use the XmlType attribute to set the name of the resulting XML datatype and the namespace in which the datatype is defined. You can also use it to specify whether a datatype definition will be generated within the Web service's WSDL document.

Table 7-5 describes the properties exposed by the XmlType attribute to control how XML datatypes are generated.

Table 7-5  XmlTypeAttribute Properties

Property

Description

IncludeInSchema

Specifies whether the type will be included in the schema

Namespace

Specifies the XML namespace in which the XML schema datatype is qualified

TypeName

Specifies the name of the XML datatype that describes the targeted .NET type

You can also use the XmlIgnore attribute to exclude entities from type definitions. For example, suppose the internal implementation of the AcceptPO Web service needs to track a processing code. The following class definition adds a ProcessingCode public field to the class for maintaining the state of this information:

[XmlRoot(IsNullable=false)] public class PurchaseOrder {     [XmlElement(IsNullable=true, DataType="normalizedString")]     public string Comments;     [XmlElement(IsNullable=false)]     public Address BillingAddress;     [XmlElement(IsNullable=false)]     public Address ShippingAddress;     [XmlIgnore]     public int ProcessingCode;     // Additional type definitions... }

The ProcessingCode field is declared as public because it needs to be accessed by the internal implementation of the AcceptPO Web service. However, because the field should not be exposed to the client, it is decorated with the XmlIgnore attribute.

There is another use for the XmlIgnore attribute. Suppose it is important to know whether the Comments element is set to null or simply contains an empty string. Because the underlying .NET type is a value type, you will not be able to directly test for null.

To discover whether the element contains a null value, you can create a property that will be set by XML serialization. You can define a Boolean public field with the prefix Specified. This field will be set to true if the associated XML element contains a null value. The following code provides an example:

[XmlRoot(IsNullable=false)] public class PurchaseOrder {     [XmlIgnore]     public bool CommentsSpecified;     [XmlElement(IsNullable=true, DataType="normalizedString")]     public string Comments;     // Additional type definitions... }

This example extends the PurchaseOrder class definition by adding the CommentsSpecified public field. This field will be set by XML serialization and therefore should not be exposed within the PurchaseOrder type definition. Therefore, I decorated the CommentsSpecified field with the XmlIgnore attribute to ensure that the ASP.NET runtime will not include the field in the auto-generated WSDL document.



Building XML Web Services for the Microsoft  .NET Platform
Building XML Web Services for the Microsoft .NET Platform
ISBN: 0735614067
EAN: 2147483647
Year: 2002
Pages: 94
Authors: Scott Short

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