After lots new coding and refactoring, dotNetTips.Utility 3.5 R2 is finally released! This assembly is much of the common code I have been writing for the past 9+ years all wrapped up in a nice package and easy to use. Here is just some of what is in the new version:

New Classes

  • EmailTraceListener - Send emails when trace events happen (I suggest using filters here)!
  • WebServiceTraceListener - Send trace events to a web service. I use this for logging events to a central back-end database!
  • Lots of new Extension methods and classes:
    • Color
    • DateTime
    • Decimal
    • Entity Framework
    • Image
    • Nullable
    • Xlement
    • And more!
  • GeoInfoHelper - Get geo location info based on IP address.
  • And lots more!
The documentation, binary and source code can be downloaded from CodePlex.

Coming soon... .NET 4.0 version!


 
If you need to serialize and deserialize your objects and persist them to disk, the the code below is an easy way to accomplish this. The GetObjectTypes function correctly detects types so that the serializer will do it's job correctly. Without this you could run into problems (as I did).

VB

    ''' <summary>

    ''' Deserializes from XML file.

    ''' </summary>

    ''' <typeparam name="T">Type</typeparam>

    ''' <param name="fileName">Name of the file.</param>

    ''' <returns></returns>

    Public Shared Function DeserializeFromXmlFile(Of T)(ByVal fileName As String) As T

        Return Deserialize(Of T)(My.Computer.FileSystem.ReadAllText(fileName))

    End Function

 

    ''' <summary>

    ''' Deserializes the specified XML.

    ''' </summary>

    ''' <typeparam name="T">Type</typeparam>

    ''' <param name="xml">The XML.</param>

    ''' <returns></returns>

    Public Shared Function Deserialize(Of T)(ByVal xml As String) As T

        Dim serializer = New XmlSerializer(GetType(T))

 

        Return DirectCast(serializer.Deserialize(New XmlTextReader(New StringReader(xml))), T)

    End Function

 

    ''' <summary>

    ''' Serializes obj to XML file.

    ''' </summary>

    ''' <param name="obj">The obj.</param>

    ''' <param name="fileName">Name of the file.</param>

    Public Shared Sub SerializeToXmlFile(ByVal obj As Object, ByVal fileName As String)

        My.Computer.FileSystem.WriteAllText(fileName, Serialize(obj), False)

    End Sub

 

    ''' <summary>

    ''' Serializes the specified obj to xml.

    ''' </summary>

    ''' <param name="obj">The obj.</param>

    ''' <returns></returns>

    Public Shared Function Serialize(ByVal obj As Object) As String

 

        Dim returnXml As String = String.Empty

 

        Dim serializer = New XmlSerializer(obj.[GetType](), GetObjectTypes(obj).ToArray())

 

        Using writer As New StringWriter(CultureInfo.CurrentCulture)

            serializer.Serialize(New XmlTextWriter(writer), obj)

 

            returnXml = writer.ToString()

        End Using

 

        Return returnXml

    End Function

 

    Private Shared Function GetObjectTypes(ByVal input As Object) As List(Of Type)

 

        Dim types = New List(Of Type)()

 

        Dim currentAssembly = input.[GetType]().Assembly

 

        Dim currentType As Type = input.[GetType]()

 

        'Query our types. We could also load any other assemblies and

        'query them for any types that inherit from the currentType

 

        For Each tempType As Type In currentAssembly.GetTypes()

            If Not tempType.IsAbstract AndAlso Not tempType.IsInterface AndAlso currentType.IsAssignableFrom(tempType) Then

                types.Add(tempType)

            End If

        Next

 

        Return types

    End Function

C#

        /// <summary>

        /// Deserializes from XML file.

        /// </summary>

        /// <typeparam name="T">Type</typeparam>

        /// <param name="fileName">Name of the file.</param>

        /// <returns></returns>

        public static T DeserializeFromXmlFile<T>(string fileName)

        {

            return Deserialize<T>(Microsoft.VisualBasic.FileIO.FileSystem.ReadAllText(fileName));

        }

 

        /// <summary>

        /// Deserializes the specified XML.

        /// </summary>

        /// <typeparam name="T">Type</typeparam>

        /// <param name="xml">The XML.</param>

        /// <returns></returns>

        public static T Deserialize<T>(string xml)

        {

            var serializer = new XmlSerializer(typeof(T));

 

            return (T)serializer.Deserialize(new XmlTextReader(new StringReader(xml)));

        }

 

        /// <summary>

        /// Serializes obj to XML file.

        /// </summary>

        /// <param name="obj">The obj.</param>

        /// <param name="fileName">Name of the file.</param>

        public static void SerializeToXmlFile(object obj, string fileName)

        {

            Microsoft.VisualBasic.FileIO.FileSystem.WriteAllText(fileName, Serialize(obj), false);

        }

 

        /// <summary>

        /// Serializes the specified obj to xml.

        /// </summary>

        /// <param name="obj">The obj.</param>

        /// <returns></returns>

        public static string Serialize(object obj)

        {

 

            string returnXml = string.Empty;

 

            var serializer = new XmlSerializer(obj.GetType(), GetObjectTypes(obj).ToArray());

 

            using (StringWriter writer = new StringWriter(CultureInfo.CurrentCulture))

            {

                serializer.Serialize(new XmlTextWriter(writer), obj);

 

                returnXml = writer.ToString();

            }

 

            return returnXml;

        }

 

        private static List<Type> GetObjectTypes(object input)

        {

 

            var types = new List<Type>();

 

            var currentAssembly = input.GetType().Assembly;

 

            Type currentType = input.GetType();

 

            //Query our types. We could also load any other assemblies and

            //query them for any types that inherit from the currentType

 

            foreach (Type tempType in currentAssembly.GetTypes())

            {

                if (!tempType.IsAbstract && !tempType.IsInterface && currentType.IsAssignableFrom(tempType))

                {

                    types.Add(tempType);

                }

            }

 

            return types;

        }



Tip Submitted By: David McCarter


 
Categories: Csharp | Generics | VB.NET | XML

If you are interested in using LINQ to read Xml instead of the older way of the XmlDocument and SelectNodes, the code below is a pretty good example. The code takes in the ISO standard file for country names and codes (see sample below) and turn it into a list of Country objects for use in an application including ComboBoxes.

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>

<ISO_3166-1_List_en xml:lang="en">

   <ISO_3166-1_Entry>

      <ISO_3166-1_Country_name>AFGHANISTAN</ISO_3166-1_Country_name>

      <ISO_3166-1_Alpha-2_Code_element>AF</ISO_3166-1_Alpha-2_Code_element>

   </ISO_3166-1_Entry>

   <ISO_3166-1_Entry>

      <ISO_3166-1_Country_name>Ă…LAND ISLANDS</ISO_3166-1_Country_name>

      <ISO_3166-1_Alpha-2_Code_element>AX</ISO_3166-1_Alpha-2_Code_element>

   </ISO_3166-1_Entry>

</ISO_3166-1_List_en>

Before

Here is the original code that has been in use for a few years. It uses the "easy" way of loading and selecting nodes using the XmlDocument object.

Dim countriesXml As New System.Xml.XmlDocument

countriesXml.LoadXml(My.Resources.CountriesXML)

 

Dim list As New System.Collections.Generic.List(Of Country)

 

For Each country As System.Xml.XmlNode In countriesXml.SelectNodes("//ISO_3166-1_Entry")

 

   Dim tempCountry As New Country

 

   tempCountry.Name = country.SelectSingleNode("ISO_3166-1_Country_name").InnerText

   tempCountry.Code = country.SelectSingleNode("ISO_3166-1_Alpha-2_Code_element").InnerText

 

   list.Add(tempCountry)

 

Next

 

After

This code uses LINQ and the new XElement to make it easy to load the xml from the assemblies resources.

Dim reader = XElement.Parse(My.Resources.Countries)

 

Dim list As New System.Collections.Generic.List(Of Country)

 

Dim data = From c In reader.Elements(XName.Get("ISO_3166-1_Entry")) _

           Order By c.Element(XName.Get("ISO_3166-1_Country_name")).Value _

           Select New Country With {.Name = c.Element(XName.Get("ISO_3166-1_Country_name")).Value, _

                            .Code = c.Element(XName.Get("ISO_3166-1_Alpha-2_Code_element")).Value.ToUpper}

 

As you can see this is much cleaner, fewer lines of code and actually faster (1 millisecond). Also you can see in the Select line that I am actually filling the Country object during the query... pretty cool!

Tip Submitted By: David McCarter

This code can be found in the open source dotNetTips.Utility assembly


 
Categories: LINQ | VB.NET | XML

I came up with some generic methods to do the job:

Public Shared Function DeserializeXMLToObject(ByVal input As String, ByVal type As System.Type) As Object
  Dim result As Object = Nothing
  Dim serializer As New XmlSerializer(type)
  Try
    result = serializer.Deserialize(New XmlTextReader(New StringReader(input)))
  Catch ex As Exception
    Debug.WriteLine(ex.Message)
  End Try
 
  Return result
End Function
Public Shared Function SerializeObjectToXML(ByVal input As Object, ByVal type As System.Type) As String
  Dim returnXML As String = String.Empty
  Dim serializer As New XmlSerializer(type)
  Dim writer As New StringWriter
  Try
    serializer.Serialize(New XmlTextWriter(writer), input)
    returnXML = writer.ToString()
  Catch ex As Exception
    Debug.WriteLine(ex.Message)
  Finally
    writer.Close()
  End Try
  Return returnXML
End Function

There are a few things to watch out for with the XmlSerilizer:

  • When deserializing, if the xml is not well-formed, then an exception will be thrown.
  • Also when deserializing, if there are empty elements like <author></author> or <author/> it seems to throw an exception. I am guessing that it expects empty elements to just not be in the xml. I have not found a way around this yet.
  • When you send in xml that does not match the object you are trying to deserialize, no exception is thrown, the object is basically empty.

When the XmlSerilizer serializes the object it comes out looking something like this:

<?xml version="1.0" encoding="utf-16"?>
<Books xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Book>
    <Title>David McCarter's VB Tips & Techniques</Title>
  </Book>
</Books>

Where I work, we are saving our xml to hardware devices with limited memory. So the <?xml> element and "xmlns" attributes are taking up limited resources and have to be removed. You would think there would be a property in the XmlSerilizer to omit this extra stuff, but there isn't. So I had to roll my own. I changed the SerilizeObjectToXML call a little and added a new method called CleanXML.

Public Shared Function SerializeObjectToXML(ByVal input As Object, ByVal type As System.Type, ByVal clean As Boolean) As String
  Dim returnXML As String = String.Empty
  Dim serializer As New XmlSerializer(type)
  Dim writer As New StringWriter
  Try
    serializer.Serialize(New XmlTextWriter(writer), input)
    returnXML = writer.ToString()
    If clean Then
      returnXML = CleanXML(returnXML)
    End If
    Catch ex As Exception
      Debug.WriteLine(ex.Message)
    Finally
      writer.Close()
  End Try
  Return returnXML
End Function
  
Private Shared Function CleanXML(ByVal input As String) As String
Dim readerXML As New XmlTextReader(New StringReader(input))
Dim writer As New StringWriter
Dim writerXML As New XmlTextWriter(writer)
Dim returnXML As String = String.Empty
  Try
    'writerXML.WriteStartDocument()
    While readerXML.Read()
      Select Case readerXML.NodeType
        Case XmlNodeType.Element
          writerXML.WriteStartElement(readerXML.Name)
          If (readerXML.HasAttributes) Then
            'Cannot just use writer.WriteAttributes,
            'else it will also emit xmlns attribute              
            While readerXML.MoveToNextAttribute()
              If (readerXML.Name.CompareTo("xmlns") = -1) Then
                writerXML.WriteAttributeString(readerXML.Name, readerXML.Value)
              End If
            End While
            readerXML.MoveToElement()
          End If
          If (readerXML.IsEmptyElement) Then
            writerXML.WriteEndElement()
          End If
        Case XmlNodeType.Text
          writerXML.WriteString(readerXML.Value)
        Case XmlNodeType.CDATA
          writerXML.WriteCData(readerXML.Value)
        Case XmlNodeType.ProcessingInstruction
          writerXML.WriteProcessingInstruction(readerXML.Name, readerXML.Value)
        Case XmlNodeType.Comment
          writerXML.WriteComment(readerXML.Value)
        Case XmlNodeType.EntityReference
          writerXML.WriteEntityRef(readerXML.Name)
        Case XmlNodeType.EndElement
          writerXML.WriteEndElement()
      End Select
    End While
    'writerXML.WriteEndDocument()
    writerXML.Flush()
    returnXML = writer.ToString()
  Finally
    writerXML.Close()
    readerXML.Close()
    writer.Close()
  End Try
  Return returnXML
End Function

The readerXML.Name.CompareTo("xmlns") line in the method above will remove the "xmlns" attributes and the commented out WriteStartDocument and WriteEndDocument calls to the writer will remove the <?xml> element. Now your xml is bare!

But be careful when using this CleanXML, as I just found out, if your classes specify the xml namespace by using the XmlRoot or XmlType attributes, then deserializing won't work unless you put the "xmlns" attribute back in (that is my next task to do after I post this tip).

.NET 2.0 And Generics

 Here is the same code but for .NET 2.0 using generics:

Public Shared Function DeserializeXML(Of T)(ByVal xml As String) As T
  Dim serializer As New Serialization.XmlSerializer(GetType(T))
  Return DirectCast(serializer.Deserialize(New XmlTextReader(New IO.StringReader(xml))), T)
End Function
Public Shared Function SerializeToXML(Of T)(ByVal obj As T) As String
  Dim returnXML As String = String.Empty
  Dim serializer As New Serialization.XmlSerializer(GetType(T))
  Using writer As New IO.StringWriter
    serializer.Serialize(New XmlTextWriter(writer), obj)
    returnXML = writer.ToString()
  End Using
  Return returnXML
End Function

Tip Submitted By: David McCarter


 
Categories: .NET | Generics | VB.NET | XML

I came up with some generic methods to do the job:

Public Shared Function DeserializeXMLToObject(ByVal input As String, ByVal type As System.Type) As Object
  Dim result As Object = Nothing
  Dim serializer As New XmlSerializer(type)
  Try
    result = serializer.Deserialize(New XmlTextReader(New StringReader(input)))
  Catch ex As Exception
    Debug.WriteLine(ex.Message)
  End Try
 
  Return result
End Function
Public Shared Function SerializeObjectToXML(ByVal input As Object, ByVal type As System.Type) As String
  Dim returnXML As String = String.Empty
  Dim serializer As New XmlSerializer(type)
  Dim writer As New StringWriter
  Try
    serializer.Serialize(New XmlTextWriter(writer), input)
    returnXML = writer.ToString()
  Catch ex As Exception
    Debug.WriteLine(ex.Message)
  Finally
    writer.Close()
  End Try
  Return returnXML
End Function

There are a few things to watch out for with the XmlSerilizer:

  • When deserializing, if the xml is not well-formed, then an exception will be thrown.
  • Also when deserializing, if there are empty elements like <author></author> or <author/> it seems to throw an exception. I am guessing that it expects empty elements to just not be in the xml. I have not found a way around this yet.
  • When you send in xml that does not match the object you are trying to deserialize, no exception is thrown, the object is basically empty.

When the XmlSerilizer serializes the object it comes out looking something like this:

<?xml version="1.0" encoding="utf-16"?>
<Books xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Book>
    <Title>David McCarter's VB Tips & Techniques</Title>
  </Book>
</Books>

Where I work, we are saving our xml to hardware devices with limited memory. So the <?xml> element and "xmlns" attributes are taking up limited resources and have to be removed. You would think there would be a property in the XmlSerilizer to omit this extra stuff, but there isn't. So I had to roll my own. I changed the SerilizeObjectToXML call a little and added a new method called CleanXML.

Public Shared Function SerializeObjectToXML(ByVal input As Object, ByVal type As System.Type, ByVal clean As Boolean) As String
  Dim returnXML As String = String.Empty
  Dim serializer As New XmlSerializer(type)
  Dim writer As New StringWriter
  Try
    serializer.Serialize(New XmlTextWriter(writer), input)
    returnXML = writer.ToString()
    If clean Then
      returnXML = CleanXML(returnXML)
    End If
    Catch ex As Exception
      Debug.WriteLine(ex.Message)
    Finally
      writer.Close()
  End Try
  Return returnXML
End Function
  
Private Shared Function CleanXML(ByVal input As String) As String
Dim readerXML As New XmlTextReader(New StringReader(input))
Dim writer As New StringWriter
Dim writerXML As New XmlTextWriter(writer)
Dim returnXML As String = String.Empty
  Try
    'writerXML.WriteStartDocument()
    While readerXML.Read()
      Select Case readerXML.NodeType
        Case XmlNodeType.Element
          writerXML.WriteStartElement(readerXML.Name)
          If (readerXML.HasAttributes) Then
            'Cannot just use writer.WriteAttributes,
            'else it will also emit xmlns attribute              
            While readerXML.MoveToNextAttribute()
              If (readerXML.Name.CompareTo("xmlns") = -1) Then
                writerXML.WriteAttributeString(readerXML.Name, readerXML.Value)
              End If
            End While
            readerXML.MoveToElement()
          End If
          If (readerXML.IsEmptyElement) Then
            writerXML.WriteEndElement()
          End If
        Case XmlNodeType.Text
          writerXML.WriteString(readerXML.Value)
        Case XmlNodeType.CDATA
          writerXML.WriteCData(readerXML.Value)
        Case XmlNodeType.ProcessingInstruction
          writerXML.WriteProcessingInstruction(readerXML.Name, readerXML.Value)
        Case XmlNodeType.Comment
          writerXML.WriteComment(readerXML.Value)
        Case XmlNodeType.EntityReference
          writerXML.WriteEntityRef(readerXML.Name)
        Case XmlNodeType.EndElement
          writerXML.WriteEndElement()
      End Select
    End While
    'writerXML.WriteEndDocument()
    writerXML.Flush()
    returnXML = writer.ToString()
  Finally
    writerXML.Close()
    readerXML.Close()
    writer.Close()
  End Try
  Return returnXML
End Function

The readerXML.Name.CompareTo("xmlns") line in the method above will remove the "xmlns" attributes and the commented out WriteStartDocument and WriteEndDocument calls to the writer will remove the <?xml> element. Now your xml is bare!

But be careful when using this CleanXML, as I just found out, if your classes specify the xml namespace by using the XmlRoot or XmlType attributes, then deserializing won't work unless you put the "xmlns" attribute back in (that is my next task to do after I post this tip).

.NET 2.0 And Generics

 Here is the same code but for .NET 2.0 using generics:

Public Shared Function DeserializeXML(Of T)(ByVal xml As String) As T
  Dim serializer As New Serialization.XmlSerializer(GetType(T))
  Return DirectCast(serializer.Deserialize(New XmlTextReader(New IO.StringReader(xml))), T)
End Function
Public Shared Function SerializeToXML(Of T)(ByVal obj As T) As String
  Dim returnXML As String = String.Empty
  Dim serializer As New Serialization.XmlSerializer(GetType(T))
  Using writer As New IO.StringWriter
    serializer.Serialize(New XmlTextWriter(writer), obj)
    returnXML = writer.ToString()
  End Using
  Return returnXML
End Function

Tip Submitted By: David McCarter


 
Categories: XML

To download go to: http://go.microsoft.com/?linkid=5559918

 


 
Categories: .NET | Csharp | dotNetDave | Link | VB.NET | XML

February 25, 2004
@ 05:09 PM

This update contains Microsoft XML (MSXML) functionality that will allow applications using MSXML to continue to function correctly after security update 832894, Security Update for Internet Explorer, has been applied. After you install this item, you may have to restart your computer. Once you have installed this item, it cannot be removed.

 

4.0 Service Pack 2: http://www.microsoft.com/downloads/details.aspx?familyid=341caf5f-0cdd-47a8-af5d-91e14fcf7a0d&displaylang=en

 

2.6: http://www.microsoft.com/downloads/details.aspx?familyid=2b0505c3-8509-4cae-865f-e29a41fe65bf&displaylang=en

 

2.5: http://www.microsoft.com/downloads/details.aspx?familyid=4a14fca1-5c2f-4cf1-993e-5e156c33c083&displaylang=en


 
Categories: XML

This Toolkit offers functionality similar to the MSDN SOAP Toolkit sample, which has been available for several months, but is a fully Microsoft supported product. This Toolkit replaces the old MSDN SOAP Toolkit.

 

http://www.microsoft.com/downloads/details.aspx?familyid=147ed727-0be8-48a1-b1da-d50b1ea582cb&displaylang=en

 

SOAP Toolkit 2.0 Redistributable Files:

http://www.microsoft.com/downloads/details.aspx?familyid=d4490e52-5f6e-4127-9dc7-88b7c8f83b74&displaylang=en


 
Categories: Link | Web Services | XML

October 17, 2003
@ 01:51 AM

SQLXML enables XML support for your SQL Server Database. It allows developers to bridge the gap between XML and relational data. You can create XML View of your existing relational data and work with it as if it was an XML file. SQLXML allows you to:

  • Build Web Services with SQL Server 2000
  • Build Web sites to publish data from SQL Server
  • Query relational database with XPath
  • Update relational data as if it was XML
  • Load XML into SQL Server
  • Query SQL Server via URLs, OLEDB/ADO or .NET Managed Classes

SP2 includes many fixes since the SP1 release including:

Identiy Propagation for XML Bulkload

Null Support for Web Services when used with Visual Studio .NET 2003

http://www.microsoft.com/downloads/details.aspx?FamilyID=4c8033a9-cf10-4e22-8004-477098a407ac&DisplayLang=en


 
Categories: ADO.NET | News | XML

September 9, 2003
@ 01:18 AM
Categories: dotNetDave | Link | Web Services | XML

The Microsoft® XML Parser (MSXML) 3.0 SP4 release offers a number of bug fixes and security fixes over the previous MSXML 3.0 SP releases. All MSXML 3.0 releases provide:

  • Server-safe HTTP access
  • Complete implementation of XSL Transformations (XSLT) and XML Path Language (XPath)
  • Changes to the Simple API for XML (SAX2) implementation, including new SAX2 helper classes with even higher conformance with World Wide Web Consortium (W3C) standards and the OASIS Test Suite.

http://www.microsoft.com/downloads/details.aspx?familyid=c0f86022-2d4c-4162-8fb8-66bfc12f32b0&displaylang=en


 
Categories: XML

One of the great new features with SQL Server 2000 is that it can directly output XML from a stored procedure. Then as a developer, you just need to deal with an xml document and all if the built-in features. The code below makes it easy to get xml from a stored procedure or an SQL statement using "FORM XML AUTO" at the end.

Dim pobjSQLConnection As New SqlClient.SqlConnection(ConfigurationSettings.AppSettings("dbconnect")) 
Dim pobjSQLCommand As SqlClient.SqlCommand
Dim pobjXMLReader As System.Xml.XmlReader
Dim pobjXML As New System.Xml.XmlDataDocument()
  Try
    pobjSQLConnection.Open()
    pobjSQLCommand = pobjSQLConnection.CreateCommand
    pstrSQL = "sp_GETCustomersNamesAsXML"
    pobjSQLCommand.CommandText = pstrSQL.ToString
    pobjXMLReader = pobjSQLCommand.ExecuteXmlReader()
    pobjXMLReader.Read()
    pobjXML.LoadXml(pobjXMLReader.ReadOuterXml)
    pobjSQLConnection.Close()
  Catch
    'Blow by errors if you want
  Finally
    pobjSQLCommand.Dispose()
    pobjSQLConnection.Dispose()
  End Try

Tip Submitted By: David McCarter


 
Categories: ADO.NET | XML