Go Back

Controlling Schema In WCF

So I’m studying the MCTS for WCF and they talked about the differences between Data Contract serialization and Xml Serializer serialization as it pertains to wsdl. I did a little deeper diving to get some insight into what that really meant to me. What I learned is that DataContracts are explicit while XmlSerializer is implicit. Meaning that I must choose what to include in a data contract and I must choose what to ignore in the XmlSerializer format. Here take a look at this code using the XmlSerializerFormatAttribute.

 

[ServiceContract(Name = "MyRenamedServiceName",

        Namespace="http://www.jamespeckham.com/2009/05/MCTS")]

    [XmlSerializerFormat(Style= OperationFormatStyle.Document,

        Use=OperationFormatUse.Literal)]

    //[DataContractFormat(Style= OperationFormatStyle.Document)]

    public interface IMyService

    {

        [OperationContract]

        MyData MyOperation(

            [MessageParameter(Name = "MyRequestObject")]

                MyData unseenParamName);

    }

 

    [Serializable]

    [DataContract]

    public class MyData

    {

        //[DataMember]

        public MySubData1 Field1;

        //[DataMember]

        public MySubData2 Field2;

    }

    [Serializable]

    [DataContract]

    public class MySubData1

    {

        //[DataMember]

        public int X;

    }

    [Serializable]

    [DataContract]

    public class MySubData2

    {

        //[DataMember]

        public int Z;

    }

What do these attributes mean?

 

The easy ones first:

ServiceContractAttribute is basically declaring your service name and the xml namespace. Pretty easy, it is of note here that if you do not put in a name it uses the interface name. So in this way you can control what gets put in the wsdl:

 

  <?xml version="1.0" encoding="utf-8" ?>

- <wsdl:definitions targetNamespace="http://www.jamespeckham.com/2009/05/MCTS" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:tns="http://www.jamespeckham.com/2009/05/MCTS" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex">

- <wsdl:types>

- <xsd:schema targetNamespace="http://www.jamespeckham.com/2009/05/MCTS/Imports">

  <xsd:import schemaLocation="http://localhost:52251/Service1.svc?xsd=xsd0" namespace="http://www.jamespeckham.com/2009/05/MCTS" />

  </xsd:schema>

  </wsdl:types>

- <wsdl:message name="MyRenamedServiceName_MyOperation_InputMessage">

  <wsdl:part name="parameters" element="tns:MyOperation" />

  </wsdl:message>

- <wsdl:message name="MyRenamedServiceName_MyOperation_OutputMessage">

  <wsdl:part name="parameters" element="tns:MyOperationResponse" />

  </wsdl:message>

- <wsdl:portType name="MyRenamedServiceName">

- <wsdl:operation name="MyOperation">

  <wsdl:input wsaw:Action="http://www.jamespeckham.com/2009/05/MCTS/MyRenamedServiceName/MyOperation" message="tns:MyRenamedServiceName_MyOperation_InputMessage" />

  <wsdl:output wsaw:Action="http://www.jamespeckham.com/2009/05/MCTS/MyRenamedServiceName/MyOperationResponse" message="tns:MyRenamedServiceName_MyOperation_OutputMessage" />

  </wsdl:operation>

  </wsdl:portType>

  </wsdl:definitions>

 

Ok so that’s all well and good. What about XmlSerializerFormatAttribute?

 

There are basically two ways to serialize your Xml. You can either format it using the XmlSerializer (using this attribute) or you can use the DataContractSerializer.

[DataContractFormat(Style= OperationFormatStyle.Document)]

 

Their output is different and the way the book explains it is that XmlIgnoreAttribute is used to ignore things that shouldn’t be in the contract, while in a Data Contract Serialization you have to explicitly include things that should be in there.

 

So here’s what it looks like based on the code above:

  <?xml version="1.0" encoding="utf-8" ?>

- <xs:schema elementFormDefault="qualified" targetNamespace="http://www.jamespeckham.com/2009/05/MCTS" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.jamespeckham.com/2009/05/MCTS">

- <xs:element name="MyOperation">

- <xs:complexType>

- <xs:sequence>

  <xs:element minOccurs="0" maxOccurs="1" name="MyRequestObject" type="tns:MyData" />

  </xs:sequence>

  </xs:complexType>

  </xs:element>

- <xs:complexType name="MyData">

- <xs:sequence>

  <xs:element minOccurs="0" maxOccurs="1" name="Field1" type="tns:MySubData1" />

  <xs:element minOccurs="0" maxOccurs="1" name="Field2" type="tns:MySubData2" />

  </xs:sequence>

  </xs:complexType>

- <xs:complexType name="MySubData1">

- <xs:sequence>

  <xs:element minOccurs="1" maxOccurs="1" name="X" type="xs:int" />

  </xs:sequence>

  </xs:complexType>

- <xs:complexType name="MySubData2">

- <xs:sequence>

  <xs:element minOccurs="1" maxOccurs="1" name="Z" type="xs:int" />

  </xs:sequence>

  </xs:complexType>

- <xs:element name="MyOperationResponse">

- <xs:complexType>

- <xs:sequence>

  <xs:element minOccurs="0" maxOccurs="1" name="MyOperationResult" type="tns:MyData" />

  </xs:sequence>

  </xs:complexType>

  </xs:element>

  </xs:schema>

 

So let me just change these 2 attributes:

    [ServiceContract(Name = "MyRenamedServiceName",

        Namespace="http://www.jamespeckham.com/2009/05/MCTS")]

    /*[XmlSerializerFormat(Style= OperationFormatStyle.Document,

        Use=OperationFormatUse.Literal)]*/

    [DataContractFormat(Style= OperationFormatStyle.Document)]

    public interface IMyService

    {

        [OperationContract]

        MyData MyOperation(

            [MessageParameter(Name = "MyRequestObject")]

                MyData unseenParamName);

    }

 

 

I get this with data contract format

  <?xml version="1.0" encoding="utf-8" ?>

- <xs:schema elementFormDefault="qualified" targetNamespace="http://www.jamespeckham.com/2009/05/MCTS" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.jamespeckham.com/2009/05/MCTS">

  <xs:import schemaLocation="http://localhost:52251/Service1.svc?xsd=xsd2" namespace="http://schemas.datacontract.org/2004/07/OTASample.Vehicles.Service" />

- <xs:element name="MyOperation">

- <xs:complexType>

- <xs:sequence>

  <xs:element minOccurs="0" name="MyRequestObject" nillable="true" type="q1:MyData" xmlns:q1="http://schemas.datacontract.org/2004/07/OTASample.Vehicles.Service" />

  </xs:sequence>

  </xs:complexType>

  </xs:element>

- <xs:element name="MyOperationResponse">

- <xs:complexType>

- <xs:sequence>

  <xs:element minOccurs="0" name="MyOperationResult" nillable="true" type="q2:MyData" xmlns:q2="http://schemas.datacontract.org/2004/07/OTASample.Vehicles.Service" />

  </xs:sequence>

  </xs:complexType>

  </xs:element>

  </xs:schema>

 

So what happened? Well because I am using the DataContractFormat attribute, it correctly sees the MyData class (due to being serializable and a datacontract) but because the DataMember attribute on the fields is commented out it does not put the xml elements for those fields in there.

 

Let’s uncomment my [DataMember] attributes and see if that helps

 

    [Serializable]

    [DataContract]

    public class MyData

    {

        [DataMember]

        public MySubData1 Field1;

        [DataMember]

        public MySubData2 Field2;

    }

    [Serializable]

    [DataContract]

    public class MySubData1

    {

        [DataMember]

        public int X;

    }

    [Serializable]

    [DataContract]

    public class MySubData2

    {

        [DataMember]

        public int Z;

    }

 

Why yes, it does work (it puts it in two different pages of schema):

  <?xml version="1.0" encoding="utf-8" ?>

- <xs:schema elementFormDefault="qualified" targetNamespace="http://www.jamespeckham.com/2009/05/MCTS" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.jamespeckham.com/2009/05/MCTS">

  <xs:import schemaLocation="http://localhost:52251/Service1.svc?xsd=xsd2" namespace="http://schemas.datacontract.org/2004/07/OTASample.Vehicles.Service" />

- <xs:element name="MyOperation">

- <xs:complexType>

- <xs:sequence>

  <xs:element minOccurs="0" name="MyRequestObject" nillable="true" type="q1:MyData" xmlns:q1="http://schemas.datacontract.org/2004/07/OTASample.Vehicles.Service" />

  </xs:sequence>

  </xs:complexType>

  </xs:element>

- <xs:element name="MyOperationResponse">

- <xs:complexType>

- <xs:sequence>

  <xs:element minOccurs="0" name="MyOperationResult" nillable="true" type="q2:MyData" xmlns:q2="http://schemas.datacontract.org/2004/07/OTASample.Vehicles.Service" />

  </xs:sequence>

  </xs:complexType>

  </xs:element>

  </xs:schema>

 

AND

 

  <?xml version="1.0" encoding="utf-8" ?>

- <xs:schema elementFormDefault="qualified" targetNamespace="http://schemas.datacontract.org/2004/07/OTASample.Vehicles.Service" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.datacontract.org/2004/07/OTASample.Vehicles.Service">

- <xs:complexType name="MyData">

- <xs:sequence>

  <xs:element minOccurs="0" name="Field1" nillable="true" type="tns:MySubData1" />

  <xs:element minOccurs="0" name="Field2" nillable="true" type="tns:MySubData2" />

  </xs:sequence>

  </xs:complexType>

  <xs:element name="MyData" nillable="true" type="tns:MyData" />

- <xs:complexType name="MySubData1">

- <xs:sequence>

  <xs:element minOccurs="0" name="X" type="xs:int" />

  </xs:sequence>

  </xs:complexType>

  <xs:element name="MySubData1" nillable="true" type="tns:MySubData1" />

- <xs:complexType name="MySubData2">

- <xs:sequence>

  <xs:element minOccurs="0" name="Z" type="xs:int" />

  </xs:sequence>

  </xs:complexType>

  <xs:element name="MySubData2" nillable="true" type="tns:MySubData2" />

  </xs:schema>

 

As you can see adding a DataMember Attribute brings in the Field that you want in the schema when using the DataContractFormat and the XmlIgnore Attribute would thusly exclude anything you did not want in your schema when using the XmlSerializerFormatAttribute.

 

I hope this helps you do some more controlled schema writing using WCF.

Comments  5

  • James Peckham 6/6/2009 12:00:00 AM

    This surprised me...
    given this code:

        [DataContract]

        public class Address

        {

            [DataMember(IsRequired = false, Order = 0)]

            public string City;

           

            [DataMember(IsRequired = false, Order = 0)]

            public string AddressLine;

           

            [DataMember(IsRequired = false, Order = 1)]

            public string ZipOrPostalCode;

           

            [DataMember(IsRequired = false)]

            public string StateOrProvince;

           

            [DataMember(IsRequired = false)]

            public string Country;

     

            public string ApartmenNumber;

     

        }


    The order they're serialized is Alphabetical, then non-ordered, then ordered, then anything without DataMember is ignored.
    Therefore Country,StateOrProvince, AddressLine, City, ZipOrPostalCode.

  • Brent Davis 6/9/2009 12:00:00 AM

    That is interesting. I had assumed that not supplying the Order= was the same as Order=0.
  • Ben 6/14/2009 12:00:00 AM

    Dude, page formatting...much to be desired here.
  • James Peckham 6/16/2009 12:00:00 AM

    Ben, can you elaborate? Are you seeing a specific issue with the formatting?
  • james peckham 7/5/2009 12:00:00 AM

    this totally helped me out with facebook because i forgot about it and was getting this error:

    Unable to deserialize XML body with root name 'auth_getSession_response' and root namespace 'http://api.facebook.com/1.0/' (for operation 'getSession' and contract ('IFacebookService',  'http://api.facebook.com/1.0/')) using DataContractSerializer. Ensure that the type corresponding to the XML is added to the known types collection of the service.
Post a comment!
  1. Formatting options