IXmlSerializable With WCF---Data Transfer in Service Contracts
Types that implement the IXmlSerializable interface are fully supported by the DataContractSerializer. TheXmlSchemaProviderAttribute attribute should always be applied to these types to control their schema.
![]() |
---|
If you are serializing polymorphic types you must apply the XmlSchemaProviderAttribute to the type to ensure the correct type is serialized. |
There are three varieties of types that implement IXmlSerializable: types that represent arbitrary content, types that represent a single element, and legacy DataSet types.
- Content types use a schema provider method specified by the XmlSchemaProviderAttribute attribute. The method does not return null and the IsAny property on the attribute is left at its default value of false. This is the most common usage of IXmlSerializable types.
- Element types are used when an IXmlSerializable type must control its own root element name. To mark a type as an element type, either set the IsAny property on the XmlSchemaProviderAttribute attribute to true or return null from the schema provider method. Having a schema provider method is optional for element types – you may specify nullinstead of the method name in the XmlSchemaProviderAttribute. However, if IsAny is true and a schema provider method is specified, the method must return null.
- Legacy DataSet types are IXmlSerializable types that are not marked with the XmlSchemaProviderAttributeattribute. Instead, they rely on the GetSchema method for schema generation. This pattern is used for the DataSettype and its typed dataset derives a class in earlier versions of the .NET Framework, but is now obsolete and is supported only for legacy reasons. Do not rely on this pattern and always apply the XmlSchemaProviderAttribute to your IXmlSerializable types.
IXmlSerializable Content Types
When serializing a data member of a type that implements IXmlSerializable and is a content type as defined previously, the serializer writes the wrapper element for the data member and passes control to the WriteXml method. The WriteXmlimplementation can write any XML, which includes adding attributes to the wrapper element. After WriteXml is done, the serializer closes the element.
When deserializing a data member of a type that implements IXmlSerializable and is a content type as defined previously, the deserializer positions the XML reader on the wrapper element for the data member and passes control to the ReadXmlmethod. The method must read the entire element, including the start and end tags. Make sure your ReadXml code handles the case where the element is empty. Additionally, your ReadXml implementation should not rely on the wrapper element being named a particular way. The name is chosen by the serializer can vary.
It is permitted to assign IXmlSerializable content types polymorphically, for example, to data members of type Object. It is also permitted for the type instances to be null. Finally, it is possible to use IXmlSerializable types with object graph preservation enabled and with the NetDataContractSerializer. All these features require the WCF serializer to attach certain attributes into the wrapper element ("nil" and "type" in the XML Schema Instance namespace and "Id", "Ref", "Type" and "Assembly" in a WCF-specific namespace).
Attributes to Ignore when Implementing ReadXml
Before passing control to your ReadXml code, the deserializer examines the XML element, detects these special XML attributes, and acts on them. For example, if "nil" is true, a null value is deserialized and ReadXml is not called. If polymorphism is detected, the contents of the element are deserialized as if it was a different type. The polymorphically-assigned type’s implementation of ReadXml is called. In any case, a ReadXml implementation should ignore these special attributes because they are handled by the deserializer.
Schema Considerations for IXmlSerializable Content Types
When exporting schema and an IXmlSerializable content type, the schema provider method is called. An XmlSchemaSet is passed to the schema provider method. The method can add any valid schema to the schema set. The schema set contains the schema that is already known at the time when schema export occurs. When the schema provider method must add an item to the schema set, it must determine whether an XmlSchema with the appropriate namespace already exists in the set. If it does, the schema provider method must add the new item to the existing XmlSchema. Otherwise, it must create a newXmlSchema instance. This is important if arrays of IXmlSerializable types are being used. For example, if you have anIXmlSerializable type that gets exported as type "A" in namespace "B", it is possible that by the time the schema provider method is called the schema set already contains the schema for "B" to hold the "ArrayOfA" type.
In addition to adding types to the XmlSchemaSet, the schema provider method for content types must return a non-null value. It can return an XmlQualifiedName that specifies the name of the schema type to use for the given IXmlSerializabletype. This qualified name also serves as the data contract name and namespace for the type. It is permitted to return a type that does not exist in the schema set immediately when the schema provider method returns. However, it is expected that by the time all related types are exported (the Export method is called for all relevant types on the XsdDataContractExporterand the Schemas property is accessed), the type exists in the schema set. Accessing the Schemas property before all relevant Export calls have been made can result in an XmlSchemaException. For more information about the export process, see Exporting Schemas from Classes.
The schema provider method can also return the XmlSchemaType to use. The type may or may not be anonymous. If it is anonymous, the schema for the IXmlSerializable type is exported as an anonymous type every time the IXmlSerializabletype is used as a data member. The IXmlSerializable type still has a data contract name and namespace. (This is determined as described in Data Contract Names except that the DataContractAttribute attribute cannot be used to customize the name.) If it is not anonymous, it must be one of the types in the XmlSchemaSet. This case is equivalent to returning the XmlQualifiedName of the type.
Additionally, a global element declaration is exported for the type. If the type does not have the XmlRootAttribute attribute applied to it, the element has the same name and namespace as the data contract, and its "nillable" property is true. The only exception to this is the schema namespace ("http://www.w3.org/2001/XMLSchema") – if the type’s data contract is in this namespace, the corresponding global element is in the blank namespace because it is forbidden to add new elements to the schema namespace. If the type has the XmlRootAttribute attribute applied to it, the global element declaration is exported using the following: ElementName, Namespace and IsNullable properties. The defaults with XmlRootAttributeapplied are the data contract name, a blank namespace and "nillable" being true.
The same global element declaration rules apply to legacy dataset types. Note that the XmlRootAttribute cannot override global element declarations added through custom code, either added to the XmlSchemaSet using the schema provider method or through GetSchema for legacy dataset types.
IXmlSerializable Element Types
IXmlSerializable element types have either the IsAny property set to true or have their schema provider method return null.
Serializing and deserializing an element type is very similar to serializing and deserializing a content type. However, there are some important differences:
- The WriteXml implementation is expected to write exactly one element (which could of course contain multiple child elements). It should not be writing attributes outside of this single element, multiple sibling elements or mixed content. The element may be empty.
- The ReadXml implementation should not read the wrapper element. It is expected to read the one element thatWriteXml produces.
- When serializing an element type regularly (for example, as a data member in a data contract), the serializer outputs a wrapper element before calling WriteXml, as with content types. However, when serializing an element type at the top level, the serializer does not normally output a wrapper element at all around the element that WriteXml writes, unless a root name and namespace are explicitly specified when constructing the serializer in theDataContractSerializer or NetDataContractSerializer constructors. For more information, see Serialization and Deserialization.
- When serializing an element type at the top level without specifying the root name and namespace at construction time, WriteStartObject and WriteEndObject essentially do nothing and WriteObjectContent calls WriteXml. In this mode, the object being serialized cannot be null and cannot be polymorphically assigned. Also, object graph preservation cannot enabled and the NetDataContractSerializer cannot be used.
- When deserializing an element type at the top level without specifying the root name and namespace at construction time, IsStartObject returns true if it can find the start of any element. ReadObject with the verifyObjectNameparameter set to true behaves in the same way as IsStartObject before actually reading the object. ReadObjectthen passes control to ReadXml method.
The schema exported for element types is the same as for the XmlElement type as described in an earlier section, except that the schema provider method can add any additional schema to the XmlSchemaSet as with content types. Using theXmlRootAttribute attribute with element types is not allowed, and global element declarations are never emitted for these types.
Differences from the XmlSerializer
The IXmlSerializable interface and the XmlSchemaProviderAttribute and XmlRootAttribute attributes are also understood by the XmlSerializer . However, there are some differences in how these are treated in the data contract model. The important differences are summarized in the following list:
- The schema provider method must be public to be used in the XmlSerializer, but does not have to be public to be used in the data contract model.
- The schema provider method is called when IsAny is true in the data contract model but not with the XmlSerializer.
- When the XmlRootAttribute attribute is not present for content or legacy dataset types, the XmlSerializer exports a global element declaration in the blank namespace. In the data contract model, the namespace used is normally the data contract namespace as described earlier.
Be aware of these differences when creating types that are used with both serialization technologies.
Importing IXmlSerializable Schema
When importing a schema generated from IXmlSerializable types, there are a few possibilities:
- The generated schema may be a valid data contract schema as described in Data Contract Schema Reference. In this case, schema can be imported as usual and regular data contract types are generated.
- The generated schema may not be a valid data contract schema. For example, your schema provider method may generate schema that involves XML attributes that are not supported in the data contract model. In this case, you can import the schema as IXmlSerializable types. This import mode is not on by default but can easily be enabled – for example, with the /importXmlTypes command-line switch to the ServiceModel Metadata Utility Tool (Svcutil.exe). This is described in detail in the Importing Schema to Generate Classes. Note that you must work directly with the XML for your type instances. You may also consider using a different serialization technology that supports a wider range of schema – see the topic on using the XmlSerializer.
- You may want to reuse your existing IXmlSerializable types in the proxy instead of generating new ones. In this case, the referenced types feature described in the Importing Schema to Generate Types topic can be used to indicate the type to reuse. This corresponds to using the /reference switch on svcutil.exe, which specifies the assembly that contains the types to reuse.
blabla...actually I understand little of all above words which come from MSDN. orz....any one who can give any comments to me .i will be apperiated for it .
but I just have done with some little code about this kind of data serialization which is dealed with DataContractSerializer background.
supposed we have a service contract below.
namespace CustomerLib { [ServiceContract] interface ICustomerServices { [OperationContract] string GetCustomer(CustomerImpIXmlSerializable c); } } [XmlSchemaProvider("GetSchema")] public class CustomerImpIXmlSerializable:IXmlSerializable { public string Id { get; set; } public string Name { get; set; } static string ns = "http://www.cnblogs.com/Charlesliu"; static string xs = "http://www.w3.org/2001/XMLSchema"; private CustomerImpIXmlSerializable _item; public CustomerImpIXmlSerializable LinkItem { get { return _item; } set { _item = value; } } public void WriteXml(XmlWriter writer) { writer.WriteStartElement("Id",ns);//must with the parameter ns. otherwise can not find the Id element from the xml. writer.WriteValue(Id); writer.WriteEndElement(); writer.WriteStartElement("Name" ,ns);//must with the ns parameter. writer.WriteValue(Name); writer.WriteEndElement(); } public void ReadXml(XmlReader reader) { CustomerImpIXmlSerializable item = new CustomerImpIXmlSerializable(); while (reader.IsStartElement()) { reader.MoveToContent(); reader.Read(); if (reader.IsStartElement("Id",ns)) { reader.MoveToContent(); item.Id = reader.ReadString(); reader.MoveToContent(); reader.ReadEndElement(); } else throw new XmlException("ExpectedElementMissing: Id element was expected."); if (reader.IsStartElement("Name",ns)) { reader.MoveToContent(); item.Name = reader.ReadString(); reader.MoveToContent(); reader.ReadEndElement(); } else throw new XmlException("ExpectedElementMissing: Title element was expected."); reader.MoveToContent(); reader.ReadEndElement(); } this._item = item; } public XmlSchema GetSchema() { return (null); }
//it is used for generate metadata for client side . in that .the client side can use this type in code public static XmlQualifiedName GetSchema(XmlSchemaSet schemaSet) { string schemaString = String.Format( "<xs:schema xmlns:tns='{0}' xmlns:xs='{1}' targetNamespace='{0}' elementFormDefault='qualified' attributeFormDefault='unqualified'>" + "<xs:complexType name='CustomerImpIXml'>" + "<xs:sequence>" + "<xs:element name='Id' type='xs:string' nillable='false'/>" + "<xs:element name='Name' type='xs:string' nillable='false'/>" + "</xs:sequence>" + "</xs:complexType>" + "</xs:schema>", ns, xs); XmlSchema schema = XmlSchema.Read(new StringReader(schemaString), null); schemaSet.XmlResolver = new XmlUrlResolver(); schemaSet.Add(schema); return new XmlQualifiedName("CustomerImpIXml", ns); } }
after that . by adding a service reference to wcf service. you can write code like this in main.
namespace testClient { class Program { static void Main(string[] args) { CustomerServicesClient client = new CustomerServicesClient(); CustomerImpIXml c = new CustomerImpIXml { Id = "1", Name = "xx" }; try { string sName = client.GetCustomer(c); Console.WriteLine(sName); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.ReadLine(); } } }
posted on 2012-07-05 23:14 malaikuangren 阅读(1255) 评论(0) 编辑 收藏 举报