VB XML Cookbook, Recipe 5: The “Halloween” Problem (Doug Rothaus)

VBTeam

In the last two XML cookbook entries, we talked about the technique of using the ReplaceWith method to perform an identity transform. While this technique may meet your needs, it can introduce a problem in your code commonly referred to as the “Halloween” problem. Let’s take a look at what the problem is, and how to solve it. (For details on the “Halloween” problem and recommended solutions, see this topic in the documentation.)

The “Halloween” problem describes a scenario where you have a set of data that is updated in some fashion while enumerating through that data set. As a result, you can encounter a null reference exception, or worse, you can end up modifying the wrong data. For example, consider this snippet of code based on the previous two XML cookbook recipe posts: 

  Private Sub Recipe5(ByVal xmlPath As String)

    Dim xmlDoc = XDocument.Load(xmlPath)

 

    Dim info = xmlDoc.<Contacts>.<Contact>.<aci:AdditionalContactInfo>

 

    ‘ Replace e-mail address tags with mailto links.

    For Each email In info…<act:eMail>

      TransformEmail(email)

    Next

  End Sub

 

  Private Sub TransformEmail(ByVal email As XElement)

    Dim emailHtml = <div class=Email>

                      <a href=<%= “mailto:” & email.<act:eMailAddress>.Value %>>

                        <%= email.<act:eMailAddress>.Value %>

                      </a>

                    </div>

 

     email.ReplaceWith(emailHtml)

  End Sub

If you ran this code, you would encounter a null reference error as shown here:

Null Reference Exception

The reason for the error is that the For…Each loop is looping through the results of a query for <eMail> elements. During the first call to the TransformEmail function, the <eMail> element is replaced with HTML. As a result, the query references an XML element that no longer exists.

How do you solve the problem then? There are a couple of solutions. The first is what you may have noticed in the previous cookbook entries: return the query results as a List using the ToList method. That is, the following code:

    For Each email In info…<act:eMail>

Is changed to…

    For Each email In info…<act:eMail>.ToList()

Returning a List means that we’re no longer dealing with a query. Hence, an update that would affect the query, such as replacing an XML element with HTML, does not affect the List.

The other common solution to the “Halloween” problem is to write your code so that it does not modify the original document. Instead, you project your results into a new XML document. In the next cookbook post, we’ll look at an example of how to do this.

0 comments

Leave a comment

Feedback usabilla icon