Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Xml Serialization & Deserialization

I am trying to serialize an object & save it into a Sql server 2008 xml field. I also have some deserialization code that re-hydrates the object. I am able to serialize & save the object into the db, but get a "Root element missing" exception.

[XmlRoot("Patient")]
public class PatientXml
{
    private AddressXml _address = null;
    private EmergencyContactXml _emergencyContact = null;
    private PersonalXml _personal = null;

    [XmlElement]
    public PersonalXml Personal
    {
        get { return _personal; }
        set { _personal = value; }
    }

    [XmlElement]
    public AddressXml Address
    {
        get { return _address; }
        set { _address = value; }
    }

    [XmlElement]
    public EmergencyContactXml EmergencyContact
    {
        get { return _emergencyContact; }
        set { _emergencyContact = value; }
    }

    public PatientXml(){}
    public PatientXml(Patient patient)
    {
        _address = new AddressXml(patient.Address);
        _emergencyContact = new EmergencyContactXml(patient.EmergencyInfo);
        _personal = new PersonalXml(patient);
    }
}

public class PersonalXml
{
    private string _firstName = string.Empty, _lastName = string.Empty, _dateOfBirth = string.Empty, _phone = string.Empty;

    [XmlAttribute]
    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value; }
    }

    [XmlAttribute]
    public string LastName
    {
        get { return _lastName; }
        set { _lastName = value; }
    }

    [XmlAttribute]
    public string DateOfBirth
    {
        get { return _dateOfBirth; }
        set { _dateOfBirth = value; }
    }

    [XmlAttribute]
    public string Phone
    {
        get { return _phone; }
        set { _phone = value; }
    }

    public PersonalXml(){}
    public PersonalXml(Patient patient)
    {
        _firstName = patient.FirstName;
        _lastName = patient.LastName;
        _dateOfBirth = patient.DateOfBirth.ToShortDateString();
        _phone = patient.Phone;
    }
}

public class AddressXml
{
    private string _address1 = string.Empty, _address2 = string.Empty, _city = string.Empty, _state = string.Empty, _zip = string.Empty;

    [XmlAttribute]
    public string Address1
    {
        get { return _address1; }
        set { _address1 = value; }
    }

    [XmlAttribute]
    public string Address2
    {
        get { return _address2; }
        set { _address2 = value; }
    }

    [XmlAttribute]
    public string City
    {
        get { return _city; }
        set { _city = value; }
    }

    [XmlAttribute]
    public string State
    {
        get { return _state; }
        set { _state = value; }
    }

    [XmlAttribute]
    public string Zip
    {
        get { return _zip; }
        set { _zip = value; }
    }

    public AddressXml(){}
    public AddressXml(Address address)
    {
        _address1 = address.Address1;
        _address2 = address.Address2;
        _city = address.City;
        _state = address.State;
        _zip = address.ZipCode;
    }
}

public class EmergencyContactXml
{
    private string _name = string.Empty, _phone = string.Empty, _relationship = string.Empty;

    [XmlAttribute]
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    [XmlAttribute]
    public string Phone
    {
        get { return _phone; }
        set { _phone = value; }
    }

    [XmlAttribute]
    public string Relationship
    {
        get { return _relationship; }
        set { _relationship = value; }
    }

    public EmergencyContactXml(){}
    public EmergencyContactXml(EmergencyContact contact)
    {
        _name = contact.ContactName;
        _phone = contact.Phone;
        _relationship = contact.Relationship;
    }
}

Serialized Xml output:

<Patient 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Personal FirstName="Test" LastName="User 1" DateOfBirth="3/13/1966" Phone="6304449866" />
    <Address Address1="123 Some St" City="Bartlett" State="CT" Zip="60111" />
    <EmergencyContact Name="Dr Chanduwarthana" Phone="6309769484" Relationship="Father" />
</Patient>

Serization & Deserialization code:

public static class XmlSerializer
{
    public static string Serialize<T>(T item)
    {
        MemoryStream memStream = new MemoryStream();
        using (XmlTextWriter textWriter = new XmlTextWriter(memStream, Encoding.Unicode))
        {
            System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
            serializer.Serialize(textWriter, item);

            memStream = textWriter.BaseStream as MemoryStream;
        }
        if (memStream != null)
            return Encoding.Unicode.GetString(memStream.ToArray());
        else
            return null;
    }

    public static T Deserialize<T>(string xmlString)
    {
        if (string.IsNullOrWhiteSpace(xmlString))
            return default(T);

        using (MemoryStream memStream = new MemoryStream())
        {
            using (XmlTextWriter textWriter = new XmlTextWriter(memStream, Encoding.Unicode))
            {
                memStream.Position = 0;
                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
                return (T)serializer.Deserialize(memStream);
            }
        }
    }
}
like image 205
Skadoosh Avatar asked Jan 04 '12 04:01

Skadoosh


2 Answers

In your deserialization code you're creating a MemoryStream and XmlTextWriter but you're not giving it the string to deserialize.

using (MemoryStream memStream = new MemoryStream())
{
    using (XmlTextWriter textWriter = new XmlTextWriter(memStream, Encoding.Unicode))
    {
        // Omitted
    }
}

You can pass the bytes to the memory stream and do away with the XmlTextWriter altogether.

using (MemoryStream memStream = new MemoryStream(Encoding.Unicode.GetBytes(xmlString)))
{
    System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
    return (T)serializer.Deserialize(memStream);
}
like image 174
Dave Fancher Avatar answered Sep 24 '22 10:09

Dave Fancher


Looks like you got a handle on serializing to XML, so take my advice, store the XML in a string field (varchar, nvarchar, text, ntext) and not a specialized field.

If you do that little switch you will be ready to go... no further modification required.

XML field is subject to validations, and more than a few headaches, and if your application is only producer and consumer of that field, you might as well take that shortcut. SQL2008 (even 2005) is strong enough to compensate for the resources you might save by it compiling the xml field.

HOWEVER , I would optimize your code a bit, looks like you wrote way more code than you had to. For example, you no longer need to create a private field to store the data from your property, for example :

public PersonalXml Personal
{
    get { return _personal; }
    set { _personal = value; }
}

will work just fine if you wrote it like :

    public PersonalXml Personal { get ; set ; }

there's more fat you could have cut...

like image 24
Segev -CJ- Shmueli Avatar answered Sep 22 '22 10:09

Segev -CJ- Shmueli