Click here to Skip to main content
15,885,701 members
Articles / Web Development / ASP.NET

Create a guestbook by using XML serialization

Rate me:
Please Sign up or sign in to vote.
4.60/5 (12 votes)
14 Jan 2009CPOL4 min read 142.5K   454   34   10
This article shows an easy way to create a guestbook using ASP.NET and XML serialization.

Introduction

This article shows an easy way to create a guestbook built using ASP.NET and XML serialization. The guestbook is a simple last-entry-last log, and the number of entries is limited to 20. When the 21st entry is submitted, the first one in the list is removed. To format the data, I use the DataList control that comes with Visual Studio .NET. The article is written to demonstrate the following technologies:

  • ASP.NET Web Forms
  • The DataList control
  • XML serialization of objects
  • Generics

The guestbook should be created as a Web Site project in Visual Studio 2005 or 2008. This is how the guestbook will look when it's ready:

Guestbook.JPG

The Code

The GuestBookEntry Class

To start with, we will create a new class file named GuestBookEntry. This class will contain the code for the entries in the guest book. An array of GuestBookEntry objects will be serialized to XML later and stored on the server.

C#
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;

/// <summary>

/// The class containing data and funtionality for one 
/// guest book entry.
/// </summary>
public class GuestBookEntry
{
    public string Message;
    public string Name;
    public DateTime Date;
    public string IPAddress;

    /// <summary>
    /// Public parameterless constructor required by XML serialization.
    /// </summary>
    public GuestBookEntry()
    {
    }

    public GuestBookEntry(string message, string name, DateTime date, string ipAddress)
    {
        this.Message = message;
        this.Name = name;
        this.Date = date;
        this.IPAddress = ipAddress;
    }

    public override string ToString()
    {
        return "<b>" + Message + "</b><br>" + "Name: <b>"

            + Name + "</b><br>" + "Date: " + Date.ToString();
    }
}

Note the overriding of the ToString() method. In .NET, every class inherits from Object, and therefore inherits implementations of several methods. One of those methods is ToString(), which returns a string that represents the current object. The default implementation isn’t very helpful, though. If you don’t override ToString(), then calling it will return the class name.

In this implementation, we override ToString() in order to get an HTML representation of the GuestBookEntry object. We are also storing the IP-address of the submitter with each entry. This information is not shown in the guest book, but can be using for blocking addresses if the guestbook is vandalized.

The GuestBookHandler Class

The second class we are going to create is the GuestBookHandler. The main responsibility for this class is to store the guestbook to the server as XML. The class also takes care of limiting the number of entries to 20 and encoding the text to HTML (replacing new lines with <br />).

The class contains the following code:

C#
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Xml;
using System.IO;
using System.Web.Hosting;

/// <summary>
/// The GuestBook handler will take care
/// of serializing the GuestBookEntry objects
/// to XML and storing it to the server.
/// </summary>
public class GuestBookHandler
{
    private List<GuestBookEntry> entrys = new List<GuestBookEntry>();
    private const string fileName = "~\\App_Data\\GuestBook.xml";
    // Configurable number of entries in the log.
    private const int maxNumberOfEntries = 20;
    // Static object used for locking critical section.
    private static object writeLock = new object();

    public GuestBookHandler() 
    {
        Load(); // Read data from XML file. 
    }

    // Returns array of Guestbook entries to presentation layer.
    public GuestBookEntry[] Entrys {
        get { return entrys.ToArray(); } 
    } 
    
    // Method to add a guestbook entry.
    public void Add(string message, string name, DateTime date, string ipAddress) 
    {
        if (entrys.Count >= maxNumberOfEntries) {
            entrys.RemoveAt(0); // Remove first entry.
        }
        // Add new entry.
        GuestBookEntry ge = new GuestBookEntry(message.Replace(Environment.NewLine,
            "<br />"),
            name, date, ipAddress);
        entrys.Add(ge);
    }
 
    public void Save() 
    {
        // Use shared lock to assure only one user at a time
        // has access to the file for writing.
        lock(writeLock) {
            // Create a new XmlSerializer instance with the type of the test class 
            XmlSerializer serializerObj = new XmlSerializer(entrys.GetType()); 
            
            // Create a new file stream to write the serialized object to a file 
            StreamWriter writeFileStream = null;
            try {
                string mappedPath = HostingEnvironment.MapPath(fileName);
                writeFileStream = new StreamWriter(mappedPath);
                serializerObj.Serialize(writeFileStream, entrys);
                writeFileStream.Flush();
            }
            finally {
                // Cleanup 
                if (writeFileStream != null) {
                    writeFileStream.Close();
                }
            }
        }
    } 
    
    private void Load() 
    { 
        // Create an instance of the XmlSerializer specifying type and namespace. 
        XmlSerializer serializer = new XmlSerializer(typeof(List<GuestBookEntry>)); 
        
        // A FileStream is needed to read the XML document. 
        FileStream fs = null;
        try {
            try {
                fs = new FileStream(HostingEnvironment.MapPath(fileName), FileMode.Open);
            }
            catch (System.IO.FileNotFoundException) {
                // No entrys to load.
                return; 
            }
            XmlReader reader = XmlReader.Create(fs);

            // Use the Deserialize method to restore the object's state. 
            entrys = (List<GuestBookEntry>)serializer.Deserialize(reader);
        }
        finally {
            if (fs != null) {
                fs.Close();
            }
        }
    } 
}

Note the use of generic lists in the source code: List<GuestBookEntry>.

Generics is a new feature introduced with .NET 2.0, which provides type safety at compile time. Generics allows you to create data structures without committing to a specific data type in your code at design time. At compile time, the compiler ensures that the types used with the data structure are consistent with type safety. In other words, generics provides type safety, but without any loss of performance or code bloat. Generics is similar to templates in C++, even though the implementation is very different.

The GuestBookHandler class also stores data to the server by using XML serialization. Serialization is the process of persisting an object to disk. Another application can deserialize your object, and it will be in the same state it was before the serialization. This tutorial uses XML serialization. The namespace containing the classes and methods suitable for such a serialization is System.Xml.Serialization.

In order to serialize an object, we first create an XmlSerializer object. We also create a stream that will write to or read from a file. Then, you call the appropriate method of serialization, passing it the stream object you created. To deserialize an XML serialized object you created, you simply call the Deserialize method, passing it the stream that reads from the XML document. The final step is to cast the object into the correct type.

XML provides the following benefits over standard serialization techniques:

  1. Greater interoperability: XML is a text file based format, and all modern Operating Systems and developing environments include libraries for processing such files.
  2. Administrator friendly: by storing objects in XML format, it gives the administrators the opportunity to view and edit XML files. So, an administrator can easily modify your object or troubleshoot problems.
  3. Better forward compatibility: XML-serialized objects are self-described. When you need to replace your application with a newer one, the transition will be straightforward.

The GuestBook.aspx File

The GuestBook.aspx file contains the following HTML code:

HTML
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="GuestBook.aspx.cs"
    Inherits="GuestBook" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>

</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h1>

            Simple Guestbook</h1>
        <asp:DataList ID="DataList1" runat="server" CellPadding="4"
            ForeColor="#333333" Width="100%">

            <ItemTemplate>
                <%# Container.DataItem %>
            </ItemTemplate>
            <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />

            <AlternatingItemStyle BackColor="White" />
            <ItemStyle BackColor="#EFF3FB" />
            <SelectedItemStyle BackColor="#D1DDF1" Font-Bold="True"
                ForeColor="#333333" />

            <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
        </asp:DataList><br />
    </div>

    Message<br />
    <asp:TextBox ID="txtMessage" runat="server" Height="75px" Width="301px" 
        TextMode="MultiLine"></asp:TextBox><br />

    <br />
    Name<br />
    <asp:TextBox ID="txtName" runat="server" Width="138px"></asp:TextBox>

    <br />
    <br />
    <asp:Button ID="butSubmit" runat="server" Text="Submit" OnClick="butSubmit_Click" />

    </form>
</body>
</html>

Note the following code under the DataList tag.

HTML
<asp:DataList ID="DataList1" runat="server" CellPadding="4" Width="305px">
            <ItemTemplate> 
                <%# Container.DataItem %> 
            </ItemTemplate>         

</asp:DataList>

The <%# %> means this is a data binding expression, and Container.DataItem is an alias for the current item in the data source. In other words, when we are binding to a collection of objects, Container.DataItem is the current row of that collection.

The Code-behind: Guestbook.aspx.cs

C#
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;

public partial class GuestBook : System.Web.UI.Page
{
    private GuestBookHandler guestBookHandler;

    protected void Page_Load(object sender, EventArgs e)
    {
        guestBookHandler = new GuestBookHandler();
        BindData();
    }

    protected void butSubmit_Click(object sender, EventArgs e)
    {
        if (txtMessage.Text != "" && txtName.Text != "") {
            guestBookHandler = new GuestBookHandler();
            guestBookHandler.Add(txtMessage.Text,
                txtName.Text, DateTime.UtcNow, 
                HttpContext.Current.Request.UserHostAddress);
            guestBookHandler.Save();
            txtMessage.Text = "";
            txtName.Text = "";
            // Re-bind data since the data has changed.
            BindData();
        }
    }

    private void BindData()
    {
        DataList1.DataSource = guestBookHandler.Entrys;
        DataList1.DataBind();
    }
}

Conclusion

As you can see, it is not very difficult to create a guestbook in .NET. This guestbook is based on XML serialization and is a last-entry-last guestbook. I could contain the base code for developing your own with more advanced functionality. With minor changes, this guestbook can also be made to a last-entry-first guestbook. All comments and improvement suggestions are welcome.

History

  • November 22, 2008: Article first published.
  • January 14, 2009: Updated with support for newline in input and error handling improved.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



Comments and Discussions

 
Questionchange this guestbook to a last-entry-first log Pin
Member 135315758-Aug-21 4:25
Member 135315758-Aug-21 4:25 
QuestionI found the problem myself... Pin
Don Reyntjes29-Aug-12 5:26
Don Reyntjes29-Aug-12 5:26 
QuestionWorks perfect on the server browser, but not on the browser outide of the server. Pin
Don Reyntjes28-Aug-12 20:12
Don Reyntjes28-Aug-12 20:12 
GeneralMy vote of 2 Pin
ProJester115-Jan-09 11:00
ProJester115-Jan-09 11:00 
too simple
AnswerRe: My vote of 2 Pin
User 571134816-Jan-09 23:49
User 571134816-Jan-09 23:49 
GeneralMost recent entry to disply first Pin
Fiorina8124-Nov-08 9:13
Fiorina8124-Nov-08 9:13 
AnswerRe: Most recent entry to disply first Pin
User 571134824-Nov-08 10:48
User 571134824-Nov-08 10:48 
GeneralCannot enter multiline input. Pin
Arthit Changsan23-Nov-08 20:55
Arthit Changsan23-Nov-08 20:55 
AnswerRe: Cannot enter multiline input. Pin
User 571134823-Nov-08 22:14
User 571134823-Nov-08 22:14 
GeneralRe: Cannot enter multiline input. Pin
Arthit Changsan6-Apr-09 15:59
Arthit Changsan6-Apr-09 15:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.