0

I have a serialized object in a string that was serialized by the XmlSerializer. I would like to deserialize it using the DataContractSerializer.

This code produces an empty object:

Dim Enc As New UTF8Encoding
Using Stream As New MemoryStream(Enc.GetBytes(rec.XML))
    Dim xtr As New XmlTextReader(Stream)
    Dim Formatter As New DataContractSerializer(GetType(SMTPSettings))
    smtp = Formatter.ReadObject(xtr, False)
End Using

This is the string:

<?xml version="1.0"?>
<SMTPSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Host>mail.clubcompete.com</Host>
  <Port>26</Port>
  <UserName>outgoing@clubcompete.com</UserName>
  <Password>xxxxxxxxx</Password>
  <UseSSL>false</UseSSL>
  <TestEmailAddress>al@xxxxx-micro.com</TestEmailAddress>
</SMTPSettings>

How do I deserialize the string using the DataContractSerializer? I cannot use the XmlSerializer because it creates DLLs in my server temp folder and the web host will not allow it.

dbc
  • 104,963
  • 20
  • 228
  • 340

1 Answers1

0

You don't show your class SMTPSettings, so I will guess it looks something like this:

Public Class SMTPSettings
    Public Property Host As String
    Public Property Port As Integer
    Public Property UserName As String
    Public Property Password As String
    Public Property UseSSL As String
    Public Property TestEmailAddress As String
End Class

If you wish to deserialize the XML shown in your question to this class using DataContractSerializer, you need to keep the following differences with XmlSerializer in mind:

  1. DataContractSerializer does not support XML attributes (other than namespace attributes and specialized attributes such as xsi:type). See here for confirmation.

    Luckily your XML does not contain such attributes.

  2. DataContractSerializer does not support serializing collections without an outer container element.

    Luckily your XML does not contain repeating elements without an outer container, so this does not apply.

  3. Default namespaces are inferred differently. XmlSerializer assumes that a type is serialized in the empty XML namespace unless an attribute such as XmlRootAttribute.Namespace is applied. DataContractSerializer assumes that a type is serialized in a nonempty XML namespace by default, specifically http://schemas.datacontract.org/2004/07/Clr.Namespace. For confirmation see Data Contract Namespaces.

    Since your XML is not in any namespace, you will need to apply data contract attrtibutes to your type to specify an empty namespace.

  4. Elements must be ordered. XmlSerializer allows elements to be in an any order, but DataContractSerializer requires the elements to be in a specific order. See Data Member Order for details. If you don't specify an order by using data contract attributes, the data contract serializer assumes that the elements should be serialized alphabetically.

    Since your XML elements are not in alphabetic order, once again you will need to use data contract attributes to specify the required order.

Putting all that together, the following version of SMTPSettings can be used to deserialize that XML sample:

<DataContract(Name:="SMTPSettings", [Namespace]:="")>
Public Class SMTPSettings
    <DataMember(Name:="Host", Order:=1)>
    Public Property Host As String
    <DataMember(Name:="Port", Order:=2)>
    Public Property Port As Integer
    <DataMember(Name:="UserName", Order:=3)>
    Public Property UserName As String
    <DataMember(Name:="Password", Order:=4)>
    Public Property Password As String
    <DataMember(Name:="UseSSL", Order:=5)>
    Public Property UseSSL As String
    <DataMember(Name:="TestEmailAddress", Order:=6)>
    Public Property TestEmailAddress As String
End Class

Demo fiddle #1 (in c#) here.

Given all the restrictions of DataContractSerializer, you might consider just loading your class into a LINQ to XML XElement and populating your SMTPSettings manually like so:

Dim xml = XElement.Parse(xmlString)
Dim smtp = New SMTPSettings With { _
    .Host = xml.Element("Host"), 
    .Port = xml.Element("Port"),
    .UserName = xml.Element("UserName"),
    .Password = xml.Element("Password"),
    .UseSSL = xml.Element("UseSSL"),
    .TestEmailAddress = xml.Element("TestEmailAddress")
}

This completely avoids having to fuss with element order.

Demo fiddle #2 here.

dbc
  • 104,963
  • 20
  • 228
  • 340