Object "type not expected" when using serialization in web services
-
Hi all, Hope I am posting this in the right forum. I seem to be having a problem when using serialization and web services. Scenario: I have built a web service which includes a multitude of web methods. All the web methods have various parameters, but one parameter which they all have in common is an 'object' parameter. This is due to the fact that the web service works differently according to the type of object being passed into it. Now as for the objects, I have constructed a set of custom classes which satisfy my need. For example's sake, let us say we have: Teacher class Student Class LectureRoom Class Lecture Class We will call these the "School Package classes" I am then wrapping these classes within other custom classes which I use not only to transport my objects around but also to keep track of statuses etc. Example: Request Class Response Class The school package classes would be serialized and saved within the request or response class. So effectively, the objects which the web service really sees are the request and response classes which will in turn have the school package classes inside of them. The Problem: The problem is found when a school package object is passed within the response and request object. On deserialisation or transportation of these objects, the web service always throws an exception as follows: {"System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> System.InvalidOperationException: There was an error generating the XML document. ---> System.InvalidOperationException: The type was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically. From what I could gather and the research I have done, the problem lies with the web service not knowing of the object types at compile time. Make Shift Solution: For a quick and dirty solution I created methods within the web service which use the object types needed. For Example: public void Junk(Student myStudent){} This seemed to have worked, but is not what I want, as with about 40 custom classes, this is both horrible coding and very tedious. I have also tried researching the xmlinclude and soapinclude found in the System.Xml.Serialization library, but dont really know how to use these. I have tried adding them above the web service class declaration and the web methods which use the objects in this format: [XmlInclude(typeof(Student))] But yet to no
-
Hi all, Hope I am posting this in the right forum. I seem to be having a problem when using serialization and web services. Scenario: I have built a web service which includes a multitude of web methods. All the web methods have various parameters, but one parameter which they all have in common is an 'object' parameter. This is due to the fact that the web service works differently according to the type of object being passed into it. Now as for the objects, I have constructed a set of custom classes which satisfy my need. For example's sake, let us say we have: Teacher class Student Class LectureRoom Class Lecture Class We will call these the "School Package classes" I am then wrapping these classes within other custom classes which I use not only to transport my objects around but also to keep track of statuses etc. Example: Request Class Response Class The school package classes would be serialized and saved within the request or response class. So effectively, the objects which the web service really sees are the request and response classes which will in turn have the school package classes inside of them. The Problem: The problem is found when a school package object is passed within the response and request object. On deserialisation or transportation of these objects, the web service always throws an exception as follows: {"System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> System.InvalidOperationException: There was an error generating the XML document. ---> System.InvalidOperationException: The type was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically. From what I could gather and the research I have done, the problem lies with the web service not knowing of the object types at compile time. Make Shift Solution: For a quick and dirty solution I created methods within the web service which use the object types needed. For Example: public void Junk(Student myStudent){} This seemed to have worked, but is not what I want, as with about 40 custom classes, this is both horrible coding and very tedious. I have also tried researching the xmlinclude and soapinclude found in the System.Xml.Serialization library, but dont really know how to use these. I have tried adding them above the web service class declaration and the web methods which use the objects in this format: [XmlInclude(typeof(Student))] But yet to no
How do you annotate your custom objects to make them serializable? Something like this? :
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://namespace/", TypeName = "person")]
public class Person
{
private string _name;[System.Xml.Serialization.XmlElementAttribute(ElementName = "name")]
public string Name { get { return _name; } }
}And are you sure that you don't have any namespace-conflicts between your custom objects?
-
How do you annotate your custom objects to make them serializable? Something like this? :
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://namespace/", TypeName = "person")]
public class Person
{
private string _name;[System.Xml.Serialization.XmlElementAttribute(ElementName = "name")]
public string Name { get { return _name; } }
}And are you sure that you don't have any namespace-conflicts between your custom objects?
no at the moment the objects are as follows:
using System;
using System.Runtime.Serialization;
using Person;namespace SchoolPackage
{
[Serializable()]
public class Student : ISerializable
{
private Person person;
private int intReference;
private int intReqTypeID;
//
private string strLastAlteredByUser;
private DateTime dteAlteredDate;
private Boolean blnIsDirty;
private Boolean blnIsNew;
private enum RequestType : int
{
Create = 1, Delete = 2, Update = 3, Execute = 4,
Retrieve = 5, RetrieveMultiple = 6
}public Student(Person person, String user, int TypeID, int Reference) { this.person = person; this.strLastAlteredByUser = user; this.dteAlteredDate = DateTime.Now; this.blnIsDirty = false; this.blnIsNew = true; this.intReqTypeID = TypeID; this.intReference = Reference; } //Deserialization constructor. public Student(SerializationInfo info, StreamingContext ctxt) { //Get the values from info and assign them to the appropriate properties person = (Person)info.GetValue("person", typeof(Person)); action = (string)info.GetValue("action", typeof(string)); intReference = (int)info.GetValue("intReference", typeof(int)); intReqTypeID = (int)info.GetValue("intReqTypeID", typeof(int)); } //Serialization function. public void GetObjectData(SerializationInfo info, StreamingContext ctxt) { info.AddValue("person", person); info.AddValue("action", action); info.AddValue("intReference", intReference); info.AddValue("intReqTypeID", intReqTypeID); }
}
-
no at the moment the objects are as follows:
using System;
using System.Runtime.Serialization;
using Person;namespace SchoolPackage
{
[Serializable()]
public class Student : ISerializable
{
private Person person;
private int intReference;
private int intReqTypeID;
//
private string strLastAlteredByUser;
private DateTime dteAlteredDate;
private Boolean blnIsDirty;
private Boolean blnIsNew;
private enum RequestType : int
{
Create = 1, Delete = 2, Update = 3, Execute = 4,
Retrieve = 5, RetrieveMultiple = 6
}public Student(Person person, String user, int TypeID, int Reference) { this.person = person; this.strLastAlteredByUser = user; this.dteAlteredDate = DateTime.Now; this.blnIsDirty = false; this.blnIsNew = true; this.intReqTypeID = TypeID; this.intReference = Reference; } //Deserialization constructor. public Student(SerializationInfo info, StreamingContext ctxt) { //Get the values from info and assign them to the appropriate properties person = (Person)info.GetValue("person", typeof(Person)); action = (string)info.GetValue("action", typeof(string)); intReference = (int)info.GetValue("intReference", typeof(int)); intReqTypeID = (int)info.GetValue("intReqTypeID", typeof(int)); } //Serialization function. public void GetObjectData(SerializationInfo info, StreamingContext ctxt) { info.AddValue("person", person); info.AddValue("action", action); info.AddValue("intReference", intReference); info.AddValue("intReqTypeID", intReqTypeID); }
}
System.Runtime.Serialization is used for binary serialization. Since Webservices use Soap wich is xml, you should serialize objects to xml. Read this article on msdn about XML Serialization with XML Web Services. In your example, just import System.Xml.Serialization and add the annotation [System.Xml.Serialization.XmlTypeAttribute()]. And make public accessors to the members you want to be serialized.
using System;
using System.Runtime.Serialization;
using Person;
using System.Xml;
using System.Xml.Serialization;namespace SchoolPackage
{
//This line is basically all you need for the class to be serializable
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://www.x.com/namespace/", TypeName = "Student")]
public class Student
{
[System.Xml.Serialization.XmlElementAttribute(ElementName = "myCustomXmlElementName")]
public string StrLastAlteredByUser
{
get { return strLastAlteredByUser; }
set { strLastAlteredByUser = value; }
}
}
}ps. personally I would let Student inherit Person :)
-
System.Runtime.Serialization is used for binary serialization. Since Webservices use Soap wich is xml, you should serialize objects to xml. Read this article on msdn about XML Serialization with XML Web Services. In your example, just import System.Xml.Serialization and add the annotation [System.Xml.Serialization.XmlTypeAttribute()]. And make public accessors to the members you want to be serialized.
using System;
using System.Runtime.Serialization;
using Person;
using System.Xml;
using System.Xml.Serialization;namespace SchoolPackage
{
//This line is basically all you need for the class to be serializable
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://www.x.com/namespace/", TypeName = "Student")]
public class Student
{
[System.Xml.Serialization.XmlElementAttribute(ElementName = "myCustomXmlElementName")]
public string StrLastAlteredByUser
{
get { return strLastAlteredByUser; }
set { strLastAlteredByUser = value; }
}
}
}ps. personally I would let Student inherit Person :)
I have done as you suggested and am still getting the same error. The problem now I believe is the way I am serialising the object. I have made a custom class for this: using System; using System.Text; namespace Helper { public class Serializer { public Serializer() { } /// /// Method to deserialise an object from a string of hex characters /// /// /// the object which was deserialised public Object DeserialiseObject(String hex) { System.IO.MemoryStream stream2; System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter2 = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); byte[] array = HexStringToByteArray(hex); stream2 = new System.IO.MemoryStream(array); Object obj = bformatter2.Deserialize(stream2); stream2.Close(); return obj; } /// /// Method to serialise an object into a hex string /// /// /// public string serializeObject(Object temp) { //write it to stream System.IO.MemoryStream stream2 = new System.IO.MemoryStream(); System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter2 = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); bformatter2.Serialize(stream2, temp); byte[] array = new byte[stream2.Length]; array = stream2.ToArray(); String hex = ByteArrayToHexString(array); stream2.Close(); return hex; } /// /// Helper to change a byte array to a hex string /// /// /// string of hex characters private static string ByteArrayToHexString(byte[] Bytes) { StringBuilder Result; string HexAlphabet = "0123456789ABCDEF"; Result = new StringBuilder(); foreach (byte B in Bytes) { Result.Append(HexAlphabet[(int)(B >> 4)]); Result.Append(HexAlphabet[(int)(B & 0xF)]); } return Result.ToString();
-
I have done as you suggested and am still getting the same error. The problem now I believe is the way I am serialising the object. I have made a custom class for this: using System; using System.Text; namespace Helper { public class Serializer { public Serializer() { } /// /// Method to deserialise an object from a string of hex characters /// /// /// the object which was deserialised public Object DeserialiseObject(String hex) { System.IO.MemoryStream stream2; System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter2 = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); byte[] array = HexStringToByteArray(hex); stream2 = new System.IO.MemoryStream(array); Object obj = bformatter2.Deserialize(stream2); stream2.Close(); return obj; } /// /// Method to serialise an object into a hex string /// /// /// public string serializeObject(Object temp) { //write it to stream System.IO.MemoryStream stream2 = new System.IO.MemoryStream(); System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter2 = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); bformatter2.Serialize(stream2, temp); byte[] array = new byte[stream2.Length]; array = stream2.ToArray(); String hex = ByteArrayToHexString(array); stream2.Close(); return hex; } /// /// Helper to change a byte array to a hex string /// /// /// string of hex characters private static string ByteArrayToHexString(byte[] Bytes) { StringBuilder Result; string HexAlphabet = "0123456789ABCDEF"; Result = new StringBuilder(); foreach (byte B in Bytes) { Result.Append(HexAlphabet[(int)(B >> 4)]); Result.Append(HexAlphabet[(int)(B & 0xF)]); } return Result.ToString();
I have got to this point. The classes are all now in this format:
using System;
using System.Collections;
using System.Xml.Serialization;
using System.Xml;namespace Data.Package
{
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "Data.Package", TypeName = "Changes")]
public class Changes
{
private Boolean blnIsDirty;
private Boolean blnIsNew;public Changes() { this.blnIsDirty = false; this.blnIsNew = true; } \[System.Xml.Serialization.XmlElementAttribute(ElementName = "IsDirty")\] public Boolean IsDirty { get { return blnIsDirty; } set { blnIsDirty = value; } } \[System.Xml.Serialization.XmlElementAttribute(ElementName = "IsNew")\] public Boolean IsNew { get { return blnIsNew; } set { blnIsNew = value; } } }
}
My serialization class is as follows:
using System;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using System.Xml;namespace Helper
{
public class Serializer
{
public Serializer()
{} /// /// Method to convert a custom Object to XML string /// /// Object that is to be serialized to XML /// XML string public String serializeObject(Object pObject) { try { String XmlizedString = null; MemoryStream memoryStream = new MemoryStream(); XmlSerializer xs = new XmlSerializer(pObject.GetType()); XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8); xs.Serialize(xmlTextWriter, pObject); memoryStream = (MemoryStream)xmlTextWriter.BaseStream; XmlizedString = UTF8ByteArrayToString(memoryStream.ToArray()); return XmlizedString; } catch (Exception e) { System.Console.WriteLine(e); return null; } } /// /// Method to reconstruct an Object from XML string /// /// /// public Object deserializeObject(String pXmlizedString) {