The Last Configuration Section Handler You'll Ever Need, take 4

Updating from my last post I've incorporated some code from Chris Sells XmlSerializerPreCompiler to help diagnose serialization problems with the configuration class.


Imports System.Configuration
Imports System.Xml.XPath
Imports System.Xml.Serialization
Imports System.Xml
Imports System.Text
Imports System.Text.RegularExpressions
 
Namespace Config
  Public Class XmlSerializerSectionHandler
    Implements IConfigurationSectionHandler
 
    Public Function Create(ByVal parent As Object, _
    ByVal configContext As Object, _
    ByVal section As System.Xml.XmlNode) As Object _
    Implements IConfigurationSectionHandler.Create
 
      '-- get the name of the type from the type= attribute on the root node
      Dim xpn As XPathNavigator = section.CreateNavigator
      Dim TypeName As String = xpn.Evaluate("string(@type)").ToString
      If TypeName = "" Then
        Throw New ConfigurationException("The type attribute is not present on the root node of " & _
        "the <" & section.Name & "> configuration section ", section)
      End If
 
      '-- make sure this string evaluates to a valid type
      Dim t As Type = Type.GetType(TypeName)
      If t Is Nothing Then
        Throw New ConfigurationException("The type attribute '" & TypeName & _
        "' specified in the root node of the the <" & section.Name & "> configuration section " & _
        "is not a valid type.", section)
      End If
      Dim xs As XmlSerializer = New XmlSerializer(t)
 
      '-- attempt to deserialize an object of this type from the provided XML section
      Dim xnr As New XmlNodeReader(section)
      Try
        Return xs.Deserialize(xnr)
      Catch ex As Exception
        Dim s As String = ex.Message
        Dim innerException As Exception = ex.InnerException
        Do While Not innerException Is Nothing
          s &= " " & innerException.Message
          innerException = innerException.InnerException
        Loop
        Throw New ConfigurationException( _
        "Unable to deserialize an object of type '" & TypeName & "' from " & _
        "the <" & section.Name & "> configuration section: " & s, _
        ex, section)
      End Try
    End Function
 
 
    Public Shared Function SerializeObjectToConfigXML(ByVal configObject As Object) As String
      Dim sb As New Text.StringBuilder
      Dim sw As New IO.StringWriter(sb)
      Dim typeName As String = configObject.GetType.FullName
      Dim asmName As String = configObject.GetType.Assembly.GetName.Name
 
      Try
 
        Dim xs As XmlSerializer = New XmlSerializer(configObject.GetType)
        Dim xsn As New XmlSerializerNamespaces
        xsn.Add("", "")
 
        Dim xtw As New Xml.XmlTextWriter(sw)
        xtw.Formatting = Xml.Formatting.Indented
        xtw.WriteRaw("")
        xs.Serialize(xtw, configObject, xsn)
 
        Dim s As String = sb.ToString
        s = regex.Replace(s, "(<" + configObject.GetType.Name + ")(>)", "$1 type=""" + typeName + ", " + asmName + """$2")
        Return s
 
      Catch ex As Exception
        Debug.WriteLine("Error, unable to serialize " + typeName)
        Debug.Indent()
 
        Debug.WriteLine(ex.Message)
        Dim innerex As Exception = ex.InnerException
        While Not innerex Is Nothing
          Debug.WriteLine(innerex.Message)
          innerex = innerex.InnerException
        End While
        Debug.Unindent()
        ' HACK: Pull our assembly base file name from exception message
        Dim regex As regex = New regex("File or assembly name (?<baseFileName>.*).dll")
        Dim match As match = regex.Match(ex.Message)
        Dim baseFileName As String = match.Groups("baseFileName").Value
        If baseFileName = "" Then
          sb.Append("If you still need additional information to diagnose this problem,").Append(vbNewLine)
          sb.Append("add the below to your app.config, which may let you examine the intermediate files produced by the serializer").Append(vbNewLine)
          sb.Append("and rerun this method").Append(vbNewLine)
          sb.Append("<system.diagnostics>").Append(vbNewLine)
          sb.Append(vbTab).Append("<switches>").Append(vbNewLine)
          sb.Append(vbTab).Append(vbTab).Append("<add name=""XmlSerialization.Compilation"" value=""4""/>").Append(vbNewLine)
          sb.Append(vbTab).Append("</switches>").Append(vbNewLine)
 
          sb.Append("</system.diagnostics>").Append(vbNewLine)
          Debug.WriteLine(sb.ToString)
          Exit Function
        End If
 
        Dim outputPath As String = IO.Path.Combine(IO.Path.GetTempPath(), baseFileName + ".out")
        Console.WriteLine((New IO.StreamReader(outputPath)).ReadToEnd())
        Console.WriteLine()
 
        Dim csPath As String = IO.Path.Combine(IO.Path.GetTempPath(), baseFileName + ".0.cs")
        Debug.WriteLine("XmlSerializer-produced source:\n" + csPath)
        Return "Error. See debug output"
      End Try
    End Function
    Public Function CreateConfigSectionDecl(ByVal ConfigObject As Object) As String
      Dim sb As New StringBuilder
      sb.Append("<section name='")
      sb.Append(ConfigObject.GetType.Name)
      sb.Append("' ")
      sb.Append("type='")
      sb.Append(Me.GetType.FullName)
      sb.Append(", ")
      sb.Append(Me.GetType.Assembly.GetName.Name)
      sb.Append("'/>")
 
      Return sb.ToString
    End Function
  End Class
End Namespace

No Comments