Welcome to Windows Communication Foundation (WCF)
Top Tasks :

WCF Team Bloggers

Browse by Tags

All Tags » Serialization   (RSS)

  • Avoiding Infinite Schema Chains

    I was working on some services with recursive data structures when I noticed that there were a few cases where I would get crashes while trying to generate proxy classes. The problems seemed to be around types that were a sequence of instances of themselves. That looks like this: < xs:schema xmlns:tns ="http://test" targetNamespace ="http://test" xmlns:xs ="http://www.w3.org/2001/XMLSchema" > < xs:complexType name ="CircularList" > < xs:sequence > < xs:element minOccurs ="0" maxOccurs ="unbounded" name ="CircularList" nillable ="true" type ="tns:CircularList" /> </ xs:sequence > </ xs:complexType > < xs:element name ="CircularList" nillable ="true" type ="tns:CircularList" /> </ xs:schema > When using message contracts I was able to fix the problem by moving the minOccurs and maxOccurs from the element to the containing sequence. This didn't change the meaning of the type when I examined the proxy. With data contracts, trying the same fix caused proxy generation to go into an infinite loop rather than crash, but that's not much of an improvement. The data contract serializer worked best when I defined an intermediate type to represent the sequence rather than directly including the list type in itself. These all look like bugs with schema analysis. Next time: Waiting for Ready Channels Read More...
  • JSON Service Speed

    I've been playing with the DataContractJsonSerializer that comes with Orcas recently to produce some JSON-based services. DataContractJsonSerializer works just like any other XmlObjectSerializer, except of course that the serialization output looks nothing like XML when written out. {"content1":"this is content","content2":"this is more content","version":1} If you attempt to push the serialized output through an XML reader or writer and examine it though, that works too through some simple but seemingly magical transformations that happen behind the scenes. < root type ="object" > < content1 > this is content </ content1 > < content2 > this is more content </ content2 > < version type ="number" > 1 </ version > </ root > This transformation trick is one that we've used elsewhere as well to give the appearance of a consistent and highly-structured set of data formats while not actually incurring the costs of that structure. That led me to start trying to observe when the simpler structure of JSON actually provides a performance advantage over the standard DataContractSerializer. I've found that while JSON wins in terms of size, it doesn't always win in terms of serialization speed. Here were the observations that I made. DataContractJsonSerializer tended to be faster for small and simple workloads. When the number of types was small and the types didn't have very many members, DataContractJsonSerializer could beat DataContractSerializer by 25%. This was most often true when the bulk of the object data was string content. On the other hand, DataContractSerializer caught up and then started winning as the types got more complicated. I also noticed that there were some primitive types, such as floating-point numbers, where DataContractSerializer always had a significant advantage. DataContractSerializer could turn a 25% loss into a 25% win just by changing several of the fields of a small type to doubles. This shows that performance is a very hard thing to predict without taking measurements. I would have expected DataContractJsonSerializer to consistently win given the simpler and smaller output format but I was able to find several data contracts taken from popular services for which that wasn't true. Next time: Timeout Error Messages Read More...
  • Serializing XML to XML

    How should I represent raw XML content in a contract? It seems like it would be really easy to have within the large blob of XML that makes up a message, a small blob of XML. However, it's more challenging to deal with that situation than you might expect because that small blob of XML has to be handled unlike everything else. With most contracts you can chew along the message and place each of the resulting bits in its proper place. When trying to preserve the raw XML though, you have to know when not to chew. In your contract you should use XMLSerializer formatted fields to turn off most of the unnecessary thinking regarding the XML content. Then, XmlSerializer knows about special handling for XmlElement and XmlAttribute to complete the mapping between pieces in the message and fields in your type. These two types work under the covers with XmlSerializer even though they don't implement the standard contract for serialization. With Orcas, you can also use the new XElement type that is defined by XLinq. XLinq doesn't have any deep integration with XmlSerializer but XElement directly implements the IXmlSerializable contract to make things work. Next time: Mapping Client Certificates Read More...
  • Serialization Temporary Assemblies

    The XmlSerializer is one of the options WCF provides for mapping between XML and strongly-typed objects. An XmlSerializer is generally preferred over other serialization approaches, such as a DataContractSerializer, when the description of the type already exists as an XML schema. The conversion process for XmlSerializer relies on decorating a type with metadata attributes that describe how the type mapping should take place. These metadata attributes link together fields in the type with elements and attributes in the XML schema. Internally, XmlSerializer analyzes the metadata attributes that you provided on the type to automatically construct the appropriate serialization code. Code generation greatly speeds up the serialization process but requires building some temporary classes and assemblies to host the code. Since these assemblies need to live somewhere, they are placed into the standard system temporary directory. Most normal user accounts have access to this directory but sometimes you want to run your application using an account with very few privileges. An anonymous or restricted service account may not be able to write to the temporary directory, causing serialization to fail. There are two ways that you can adjust the interaction between the application account and the generation of temporary assemblies. The first approach is to grant the application account read and write privileges to the temporary directory. If you don't know where the system temporary directory is located, the error message that you got when serialization failed should include the file path where XmlSerializer was expecting to find the generated code. The second approach is to change where XmlSerializer writes the generated code to be a location where the application account has the appropriate privileges. You can change the location for generated code by adding a serialization section to your configuration file: < system.xml.serialization > < xmlSerializer tempFilesLocation ="an absolute path of your choice" /> </ system.xml.serialization > Next time: Acting on Open Read More...
  • Generating Types with Lists

    I have a data contract that contains a collection type but the generated proxy appears as an array. How can I make the proxy use a collection type as well? I've talked in the past about how the representation of a type in metadata is decoupled from the CLR representation of a type in the service. For example, if I have a data contract that uses a List: [DataContract] class Data { [DataMember] public List< string > data; } Then, the metadata representation of this data contract is actually described as an array because arrays are the only primitive type for collections in schema. < xs:schema xmlns:tns ="http://schemas.datacontract.org/2004/07/" elementformdefault ="qualified" targetnamespace ="http://schemas.datacontract.org/2004/07/" xmlns:xs ="http://www.w3.org/2001/XMLSchema" > < xs:import namespace ="http://schemas.microsoft.com/2003/10/Serialization/Arrays" > < xs:complexType name ="Data" > < xs:sequence > < xs:element xmlns:q1 ="http://schemas.microsoft.com/2003/10/Serialization/Arrays" minoccurs ="0" name ="data" nillable ="true" type ="q1:ArrayOfstring" > </ xs:element > </ xs:sequence > < xs:element name ="Data" nillable ="true" type ="tns:Data" > </ xs:element > </ xs:complexType ></ xs:import ></ xs:schema > However, just as the metadata representation isn't coupled to the service, the metadata representation also isn't coupled to the client. You can on the client generate proxies with any type for this collection that similarly can be serialized or deserialized to an array. The mechanism for doing this with svcutil.exe is the /ct switch. The /ct switch, which stands for collectionType, allows you to give a qualified type name that is used for collection data types when generating a proxy. As an example, to get back to the original collection class used by the server, the proxy would need to be constructed using /ct:System.Collections.Generic.List`1 as the option passed to svcutil.exe. However, you could leave the proxy using arrays or provide a different collection class such as /ct:System.Collections.ObjectModel.Collection`1 and with any of these configurations the proxy would be able to exchange messages with the server. Next time: Setting the Configuration Name Read More...
  • One Shot Serialization

    Why do some serialization errors when sending a response not result in a fault being returned to the client? In the typical service operation, sending a response is comprised of returning the appropriate information from the service method to construct a message. You might think of that response as a single operation but performing the response is divided up into many different acts. As an example of a division you could make, one way to split a response into separate acts is to say that there is an act of thinking about whether to respond and what the response contains, an act of constructing the response, and an act of transmitting the response. There is a moment in time during those acts in which the system moves from thinking about a response to actually carrying out the response. Because there are many different ways to divide the operation into a series of acts, that moment in time does not always make up a clear line separating one part of the system from another. However, if you get to the point where you've started carrying out the response, then you've attempted to respond. As an example in WCF terms, RequestContext.Reply is one key point at which the act of responding is realized. There are a variety of different messaging patterns; the ones that you should be familiar with are the one-way, request-reply, and duplex patterns. One-way patterns don't have a response so we can ignore those in this discussion. Request-reply patterns have the property that for any given request there can only ever be one response. If you think about the act of responding now, then there is a point at which your single attempt to respond has fail. This point is totally divorced from what takes place on the wire. It instead is an internal artifact of how the particular system divides the response into acts. A failure before that point would permit an error response to be sent instead while a failure after that point means that no response can ever be sent. Next time: Hosting Identity Read More...
  • Differences in Enum Serialization

    Why does adding an enum parameter to an operation cause the proxy to explode into message contracts? This was a question asking why the following perfectly ordinary operation contract caused svcutil.exe to spit out some really ugly code. [OperationContract] void Foo(EnumType e); Getting started debugging the problem was fairly easy because svcutil left a nice comment explaining what went wrong during code generation. // CODEGEN: Generating message contract since element name e from namespace http://tempuri.org/ is not marked nillable This was a completely accurate statement because when I checked the schema for the operation, the operation parameter was not marked nillable. However, it didn't help at all to explain why this was happening. < xs:element name ="Foo" > < xs:complexType > < xs:sequence > < xs:element xmlns:q1 ="http://schemas.datacontract.org/2004/07/" minOccurs ="0" name ="e" type ="q1:EnumType" /> </ xs:sequence > </ xs:complexType > </ xs:element > A brutal way to solve the problem would be to change the operation parameter from EnumType to EnumType? to force it to be nillable. Clearly though, something more fundamental was wrong because when I checked the schema for EnumType it was not what I expected. < xs:simpleType name ="EnumType" > < xs:restriction base ="xs:string" /> </ xs:simpleType > The error wasn't that EnumType was passed as a string (it's relatively common to represent an enumeration using the names of the values), but that the type didn't actually define any values at all. I would have expected to see under the restriction a list of enumeration values for this type. This was also apparent in the generated proxy. The proxy was using raw strings rather than a nice enumerated type because the restriction had no values defined. The problem was solved by looking at the definition of EnumType. [DataContract] public enum EnumType { ValueOne, ValueTwo } By applying a DataContract attribute, the default serialization contract for the enum was replaced by a contract that didn't include any of the values as members. The default contract would have included all of the values. Specifying a DataContract attribute is only needed when customizing the serialization contract, such as when you only want to expose a subset of the enumeration values. Removing the spurious DataContract attribute fixed the schema problems and allowed svcutil to generate proxies with a nice enumeration type. Read More...
  • Producing Typed Messages

    How do typed messages get created from an object that has a message contract? There seem to be a lot of examples that talk about how messages get produced when they're described by data contracts but relatively few descriptions of the equivalent process for message contracts. There's really nothing complicated or magical here so let's go through it. I'll start with a message that I want to describe with a message contract and then build the contract class. < s:Envelope xmlns:s ="http://www.w3.org/2003/05/soap-envelope" > < s:Header > < h:version xmlns:h ="http://mycorp.com" > 1 </ h:version > </ s:Header > < s:Body > < name xmlns ="http://mycorp.com" > Microsoft </ name > < address xmlns ="http://mycorp.com" > 1 Microsoft Way </ address > </ s:Body > </ s:Envelope > There's a message header whose type is an integer and two body members whose types are strings. This is a pretty easy message to describe although I have to change the settings for the wrapper and namespaces to get the structure to match. I apply an order to the body members because the default would reverse them. [MessageContract(IsWrapped= false )] class MyMessage { [MessageHeader(Namespace = "http://mycorp.com" )] public int version { get; set; } [MessageBodyMember(Namespace = "http://mycorp.com" , Order = 1)] public string name { get; set; } [MessageBodyMember(Namespace = "http://mycorp.com" , Order = 2)] public string address { get; set; } } Now I can use the TypedMessageConverter class to go between the message contract and a Message object. MyMessage record = new MyMessage(); record.version = 1; record.name = "Microsoft" ; record.address = "1 Microsoft Way" ; TypedMessageConverter converter = TypedMessageConverter.Create( typeof (MyMessage), null ); Message message = converter.ToMessage(record, MessageVersion.Soap12); Console.WriteLine(message.ToString()); message.Headers.Clear(); message.Headers.Add(MessageHeader.CreateHeader( "version" , "http://mycorp.com" , 2)); MyMessage record2 = (MyMessage)converter.FromMessage(message); Console.WriteLine(record2.version); Console.WriteLine(record2.name); Console.WriteLine(record2.address); I changed the version header for the return trip just to prove that there's no cheating going on. I gave an explicit message version when creating the message because otherwise I would have gotten SOAP 1.2 with WS-Addressing 1.0. My sample message didn't have any addressing information so I Read More...
  • Differences in Guid Serialization

    Why do the guids in my contract turn into strings when generating a client? You're probably mixing different types of serializers between the client and service. There's nothing wrong with this and the generated client will work correctly but you don't get the user-friendly types. To see why, let's look at the metadata. A guid in a contract with the DataContractSerializer generates a type in the http://schemas.microsoft.com/2003/10/Serialization/ namespace that looks like this: < xs:element name ="guid" nillable ="true" type ="tns:guid" /> < xs:simpleType name ="guid" > < xs:restriction base ="xs:string" > < xs:pattern value ="[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}" /> </ xs:restriction > </ xs:simpleType > On the other hand, a guid in a contract with the XmlSerializer generates a type in the http://microsoft.com/wsdl/types/ namespace that looks like this: < xs:simpleType name ="guid" > < xs:restriction base ="xs:string" > < xs:pattern value ="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}" /> </ xs:restriction > </ xs:simpleType > These generated types are needed because a guid is not a primitive type. DataContractSerializer came after XmlSerializer so it recognizes both definitions but XmlSerializer has to rely on the schema when it sees a DataContractSerializer guid. Since the schema is based on a string type, the generated client field is a string. The same thing happens with other serializers that don't know how to map a particular schema pattern to a user-friendly type. Next time: TCP Throttling Read More...
  • Collections without CollectionDataContract

    In the article about serialization conflicts , one of the points mentioned was that CollectionDataContract doesn't let you add non-default data members. How do I format a collection that contains data members? CollectionDataContract is automatically being added even if I don't want it. The trick is that CollectionDataContract is automatically applied to the default collection implementations rather than to all collections. That means that if your class is a List of T then you'll get the automatic CollectionDataContract behavior. If your class is an IList of T then you can choose to decorate the type with normal data contracts. This subtle distinction allows you to save the day. Convert your collection type to implement the corresponding interface and delegate all of the calls to a concrete implementation of the collection that you keep as a member variable. Then, you can figure out how to serialize that member variable, which gives you full control over how the list elements mix together with the other data members. Next time: Printing Flexible Message Headers Read More...
  • Resolving Conflicts in Serialization

    DataContractSerializer supports multiple serialization mechanisms. If more than one serialization mechanism is specified for the same type, which one gets used? Experimentation is the easiest way to figure out what happens. I'll look at different combinations of the XML serializer, the data contract serializer, and the "collection-based" data contract serializer, which is the default mechanism for the concrete collection classes. Let's start with a base interface that defines a member for the type. public interface IFoo { string data { get; set; } } Now, let's apply various combinations of serialization mechanisms to implementations of the base interface. I'll start with each of the serializers by themselves and then each of the legal combinations. Data contracts and collection data contracts can't be used at the same time so that pairing isn't possible as well as trying to use all three serialization mechanisms at once. Here's a quick test program to run through the combinations. public class Program { [DataContract] public class FooDataContract : IFoo { [DataMember] public string data { get; set; } } [Serializable] public class FooSerializable : IFoo { public string data { get; set; } } [CollectionDataContract] public class FooCollection : List< int >, IFoo { [DataMember] public string data { get; set; } } [Serializable] [DataContract] public class FooSerializableDataContract : IFoo { [DataMember] public string data { get; set; } } [Serializable] [CollectionDataContract] public class FooSerializableCollection : List< int >, IFoo { [DataMember] public string data { get; set; } } static void Main( string [] args) { using (XmlTextWriter writer = new XmlTextWriter(Console.Out)) { foreach (Type type in typeof (Program).GetNestedTypes()) { IFoo foo = (IFoo)type.InvokeMember( null , BindingFlags.CreateInstance, null , null , null ); foo.data = type.Name; DataContractSerializer serializer = new DataContractSerializer(type); writer.WriteString(type.Name); writer.WriteWhitespace( "\n" ); serializer.WriteObject(writer, foo); writer.WriteWhitespace( "\n\n" ); } } Console.ReadLine(); } } That gives us a set of outputs to inspect to see what serializer won. FooDataContract <Program.FooDataContract xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/">< data >FooDataContract</ data ></Program.FooDataContract> FooSerializable <Program.FooSerializable xmlns:i="http://www.w3.org/2001/XMLSchema-instance" Read More...
  • Serialization and Types

    How does a DataContract type get initialized on the server? When I change the constructor for the type, nothing happens. Older serializers relied on calling the parameter-less constructor to initialize the type when deserializing data. Data contract types have all of the data members initialized to the default values without a constructor being called. However, you can still introduce callback hooks into the serialization process by decorating methods on your data contract type with attributes. Here's a sample program that shows you exactly what goes on. using System; using System.ServiceModel; using System.ServiceModel.Channels; using System.Runtime.Serialization; [DataContract] public class Data { [DataMember] public int a; [OnDeserialized] void OnDeserialized(StreamingContext c) { Console.WriteLine( "OnDeserialized: {0}" , a); a = 1; } [OnDeserializing] void OnDeserializing(StreamingContext c) { Console.WriteLine( "OnDeserializing: {0}" , a); a = 2; } [OnSerialized] void OnSerialized(StreamingContext c) { Console.WriteLine( "OnSerialized: {0}" , a); a = 3; } [OnSerializing] void OnSerializing(StreamingContext c) { Console.WriteLine( "OnSerializing: {0}" , a); a = 4; } } [ServiceContract] public interface IService { [OperationContract] void Method(Data d); } public class Service : IService { public void Method(Data d) { Console.WriteLine( "Method: {0}" , d.a); } } class Program { static void Main( string [] args) { string address = "http://localhost:8000/" ; Binding binding = new BasicHttpBinding(); ServiceHost host = new ServiceHost( typeof (Service)); host.AddServiceEndpoint( typeof (IService), binding, address); host.Open(); ChannelFactory<IService> factory = new ChannelFactory<IService>(binding); factory.Open(); IService proxy = factory.CreateChannel( new EndpointAddress(address)); Data d = new Data(); d.a = 5; proxy.Method(d); factory.Close(); host.Close(); Console.ReadLine(); } } You can try running the program to see the value of the data member at various points in time. If you're still confused, here's exactly what's going on. The data member on the client is set to 5. OnSerializing is called and changes the data member from 5 to 4. The service is called. OnSerialized is called and changes the data member from 4 to 3. The service doesn't see this because the data has already been sent. This only changed the value of the data member on the client. The service initializes the type to its default value, which for the data member is 0. OnDeserializing Read More...
  • Differences Between WSDL and XSD

    Obviously WSDL and XSD are two entirely different description languages, but I was looking at the outputs of the old wsdl.exe and xsd.exe programs recently to see how they differed. They turned out to generate data type classes that were pretty much the same. Starting with a description that was equivalent to the following data contract, I ran both of the programs. [DataContract] public class Data { [DataMember] public int a; [DataMember] public string b; [DataMember] public List<XmlElement> c; } Going through xsd.exe gave me a data type that looked like this (I've deleted some of the surrounding code for clarity): [System.CodeDom.Compiler.GeneratedCodeAttribute( "xsd" , "2.0.50727.312" )] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute( "code" )] [System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://schemas.datacontract.org/2004/07/" )] [System.Xml.Serialization.XmlRootAttribute(Namespace = "http://schemas.datacontract.org/2004/07/" , IsNullable = true )] public partial class Data { private int aField; private bool aFieldSpecified; private string bField; private System.Xml.XmlElement[] cField; public int a { get { return this .aField; } set { this .aField = value ; } } [System.Xml.Serialization.XmlIgnoreAttribute()] public bool aSpecified { get { return this .aFieldSpecified; } set { this .aFieldSpecified = value ; } } [System.Xml.Serialization.XmlElementAttribute(IsNullable = true )] public string b { get { return this .bField; } set { this .bField = value ; } } [System.Xml.Serialization.XmlArrayAttribute(IsNullable = true )] [System.Xml.Serialization.XmlArrayItemAttribute(Namespace = "http://schemas.datacontract.org/2004/07/System.Xml" )] public System.Xml.XmlElement[] c { get { return this .cField; } set { this .cField = value ; } } } Going through wsdl.exe gave me a very similar data type except for two differences. The GeneratedCode attribute is different because it contains the name of the generating program. When coming from the WSDL version, you don't get the XmlRoot attribute. The output of the two programs for data types was interchangeable after taking into account these two differences. All of the surrounding code was completely different but I happened to just need the data types and not the proxy classes. This doesn't hold for types that use the SOAP encoding style though. In that case, wsdl.exe uses new behavior that's not present in xsd.exe. Read More...
  • Two Videos on Orcas

    With all the things that were happening during the summer, I evidently missed out on two Channel 9 videos that were done on the Orcas version of WCF. These cover some of the features that are new in the upcoming .Net Framework 3.5 release. In the first video Eugene Osovetsky talks about using JSON in the programming model and serialization, which is one of several new features for writing non-SOAP web services. In the second video Pravin Indurkar talks about integrating web services with workflows using the Workflow Services technology codenamed Silver. Read More...
  • ContractNamespaceAttribute

    Back when I did an overview of custom namespaces , I omitted any namespace declarations that wouldn't appear in the final metadata. One of those declarations is the default namespace for data contracts. I had two example data contracts, one for faults and one for normal messages, although they actually work exactly the same in practice. [DataContract(Namespace = "http://example.com/faultcontract/datacontract" )] public class MyFault { [DataMember] public string detail; } [DataContract(Namespace = "http://example.com/datacontract" )] public class MyData { [DataMember] public string data; } These data contracts include an explicitly declared XML namespace. However, you can modify the test program in the earlier article to see what happens when those namespace declarations are removed. What you end up with is an ugly looking namespace based on a fixed prefix http://schemas.datacontract.org/2004/07/ and a suffix that is the CLR namespace. If you want to get rid of that default namespace for every data contract you define, then you can add a ContractNamespaceAttribute to the assembly. This attribute defines the default XML namespace for data contracts that are located in a particular CLR namespace. You can retrieve a ContractNamespaceAttribute in the standard way for assembly attributes by calling GetCustomAttributes on the assembly with the ContractNamespaceAttribute type. Next time: Configuring SSL Certificates for Vista Read More...
More Posts Next page »

Copyright © 2006 Microsoft Corporation. All Rights Reserved. | Terms of Use | Privacy Statement | Contact Us