You might run into a variety of error messages, but we’ll try to focus on some of the more common areas where problems might arise.
Let’s start off by looking at a couple common error messages.You might run into this one right off the bat:
System.IO.FileInfo cannot be serialized because it does not have a default public constructor.
If you manage to get past that error, you might run into the following one:
System.Exception: There was an error generating the XML document. – - > System.Exception: System.IO.FileInfo cannot be serialized because it does not have a default public constructor. at System.Xml.Serialization. TypeScope.ImportTypeDesc(Type type, Boolean canBePrimitive) at System.Xml.Serialization. TypeScope.GetTypeDesc(Type type) at System.Xml.Serialization. TypeScope.ImportTypeDesc(Type type, Boolean canBePrimitive) at System.Xml.Serialization. TypeScope.GetTypeDesc(Type type) atSystem.Xml.Serialization.XmlSerializationWriter.CreateUnknownTypeException(Type type) at System.Xml.Serialization.XmlSerializationWriter.WriteTypedPrimitive(String name, String ns, Object o, Boolean xsiType) atn2499d7d93ffa468fbd8861780677ee41.XmlSerializationWriter1.Write4_Object(String n, String ns, Object o, Boolean isNullable, Boolean needType) atn2499d7d93ffa468fbd8861780677ee41.XmlSerializationWriter1.Write9_Object(Object o) at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces) at System.Xml.Serialization.XmlSerializer.Serialize(TextWriter textWriter, Object o) at System.Web.Services.Protocols.XmlReturnWriter.Write(HttpResponse response, Stream outputStream, Object returnValue) at System.Web.Services.Protocols.HttpServerProtocol.WriteReturns(Object[] returnValues, Stream outputStream) at System.Web.Services.Protocols.WebServiceHandler.WriteReturns(Object[] returnValues) at System.Web.Services.Protocols.WebServiceHandler.Invoke() at System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()
If you get either of those messages, whether while your service is just starting or as you invoke the method that returns the data that caused the error, most likely you are trying to return a complex data set or array that the system cannot serialize. If you take a look at Listings 13.5 and 13.6, you can see that it would be easy to overlook what type of data the system can return.
[WebMethod] public DirectoryInfo[] Dir(string dir) { DirectoryInfo di = new DirectoryInfo(dir); DirectoryInfo[] diList = di.GetDirectories("*.*"); return diList; } |
<WebMethod()> Public Function Dir(ByVal dir As String) As DirectoryInfo() Dim di As New DirectoryInfo(dir) Dim diList as di.GetDirectories("*.*") Return diList End Function |
The code in Listings 13.5 and 13.6 looks fine, but it won’t work. So now what do you do? Well, one way to work around this is to build your own array and then pass it back to the client. If you do this, you must make sure that the array structure is published to public so that the users know what they are working with. This gets accomplished in Listings 13.7 and 13.8.
using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Web; using System.Web.Services; using System.Xml.Serialization; using System.IO; namespace DirectoryTools { /// <summary> /// Summary description for Service1. /// </summary> public class Service1 : System.Web.Services.WebService { public Service1() { //CODEGEN: This call is required by the ASP.NET Web Services Designer InitializeComponent(); } #region Component Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { } #endregion /// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { } // WEB SERVICE EXAMPLE // The HelloWorld() example service returns the string Hello World // To build, uncomment the following lines and then save and build the project // To test this web service, press F5 [WebMethod] public string HelloWorld() { return "Hello World"; } [WebMethod()] [XmlInclude(typeof(DirObject))] public DirObject Test( string sPath) { DirectoryInfo di = new DirectoryInfo(sPath); DirectoryInfo[] diList = di.GetDirectories("*.*"); DirObject temp = new DirObject(); int x = 0; foreach ( DirectoryInfo d in diList) { temp[x] = d; x++; } return temp; } } [XmlInclude(typeof(DirItem))] public class DirObject { [XmlElement("Item")] public ArrayList Data { get { return data;} set { data = value;} } protected ArrayList data = new ArrayList(); public object this [int idx] { get { if (idx > -1 && idx < data.Count) { return (data[idx]); } else return null; } } set { if (idx > -1 && idx < data.Count) { data[idx] = value; } else if (idx == data.Count) { DirItem x = new DirItem(); DirectoryInfo temp = (DirectoryInfo)value; x.FileName = temp.FullName; data.Add(x); } else { //Possibly throw an exception here. } } } } // MyClass public class DirItem { protected string filename; public DirItem() { } [XmlAttribute("FileName")] public string FileName { get { return filename;} set { filename = value;} } } } //Name space |
Imports System.Web.Services Imports System.IO Imports System.Xml Imports System.Xml.Serialization <WebService(Namespace:="http://microsoft.com/webservices/")> Public Class Service1 Inherits System.Web.Services.WebService #Region " Web Services Designer Generated Code " Public Sub New() MyBase.New() 'This call is required by the Web Services Designer. InitializeComponent() 'Add your own initialization code after the InitializeComponent() call End Sub 'Required by the Web Services Designer Private components As System.ComponentModel.Container 'Do not modify it using the code editor. <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() components = New System.ComponentModel.Container() End Sub Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) 'Do not modify it using the code editor. End Sub #End Region ' <WebMethod()> Public Function Test(ByVal path As String) As DirObject Dim di As DirectoryInfo = New DirectoryInfo(path) Dim diList() As DirectoryInfo Dim d As DirectoryInfo Dim temp As DirObject = New DirObject() Dim x As Integer = 0 diList = di.GetDirectories("*.*") For Each d In diList temp(x) = d x = x + 1 Next Return temp End Function <XmlInclude(GetType(DirItem)), XmlRoot("Root")> Public Class DirObject Private pdata As ArrayList = New ArrayList() <XmlElement("Item")> Public Property Data() As ArrayList Get Return pdata End Get Set(ByVal Value As ArrayList) pdata = Value End Set End Property Default Public Property base(ByVal idx As Integer) As Object Get If (idx > -1 And idx < Data.Count) Then Return pdata(idx) Else Return Nothing End If End Get Set(ByVal Value As Object) If (idx > -1 And idx < pdata.Count) Then pdata(idx) = Value ElseIf (idx = pdata.Count) Then Dim x As DirItem = New DirItem() Dim temp As DirectoryInfo temp = CType(Value, DirectoryInfo) x.FileName = temp.FullName x.LastAccessDate = temp.LastAccessTime pdata.Add(x) Else 'Possibly throw an exception here. End If End Set End Property End Class Public Class DirItem Private pfilename As String Private pLastAccessDate As Date Sub DirItem() End Sub <XmlElement("FileName")> Public Property FileName() As String Get Return pfilename End Get Set(ByVal Value As String) pfilename = Value End Set End Property <XmlElement("LastAccessDate")> Public Property LastAccessDate() As Date Get Return pLastAccessDate End Get Set(ByVal Value As Date) pLastAccessDate = Value End Set End Property End Class End Class |
In Listings 13.7 and 13.8, you will notice a few new elements that you might not have seen before: <XmlAttribute> and <XmlInclude>. These additional features define the output when this class is serialized. This is a great way to rename elements in the XML structure to something more meaningful.