I have an old application, developed in an old architecture that I need to bring into the modern world; the old architecture had features that I'm having problems replicating. The idea is this: I am a data provider. Customers will have specific format requirements for the data. In order to facilitate this, customers build "xml document templates" and through this mechanism my application populates the data into the exact format they need. Consider this arbitrary sample customer supplied xml:
<xml><data><people>
<replacetag1><person><name>$NAME$</name><address>$ADDRESS$</address></person></replacetag1>
</people></data></xml>
This document is read, the code finds <replacetag1>, takes the content inside of said tag and generates a <person> or <person>s. This leaves the entirety of the formatting of the document up to the customer and their requirements.
The application contains the following underlying function(vb.net but could have implemented it similarly in c#):
Dim oDoc = XElement.Load(FileName)
For Each oDesc In oDoc.Descendants
If oDesc.Name.LocalName.StartsWith(TagPrefix, StringComparison.OrdinalIgnoreCase) Then
Dim sVal = String.Concat(oDesc.Nodes)
RaiseEvent TagReplace(oDesc.Name.LocalName, sVal)
oDesc.ReplaceWith(sVal)
End If
Next
Instead of looking specifically for tag <replacetag1> the code matches on the beginning of the tag name (TagPrefix) in anticipation it will need to do multiple replacements for multiple reasons, and all of this RaiseEvent/ReplaceWith code functions exactly as expected on the first pass. If I set a breakpoint after the oDesc.ReplaceWith, the document (oDoc) looks perfect at that point.
But at that point because of the underlying document change, the iterator is broken and I receive a NullReferenceException at "Next". Because of this error, I could not catch a second occurrence of another <replacetag1> (uncommon but possible) or another type of replacement (ie <replacetag2>) within the same document. I need to continue iterating the rest of the document looking for more replacements and need to have it finish gracefully.
This is using Xml.Linq but DOM may be better to handle such sorts of on-the-fly document modification; I am a novice at best with both technologies.
Edit:
In order to handle replace tags that come out of the content itself (and also because of the original issue), I've decided to redesign this in order to do the recursive scan and replacement inside of a recursive sub itself. Then the entire document gets rescanned from the beginning each time but it has the opportunity to handle replace tags inside the replacement content, and can simply do an Exit For before the offending error occurs:
Dim oDoc = XElement.Load(FileName)
CheckReplacements(oDoc)
Private Sub CheckReplacements(XE As XElement)
For Each oDesc In XE.Descendants
If oDesc.Name.LocalName.StartsWith(TagPrefix, StringComparison.OrdinalIgnoreCase) Then
Dim sVal = String.Concat(oDesc.Nodes)
RaiseEvent TagReplace(oDesc.Name.LocalName, sVal)
oDesc.ReplaceWith(XElement.Parse(sVal))
CheckReplacements(XE)
Exit For
End If
Next
End Sub
This technique works great and will work going forward for my solution. However if no markup is pushed into an element in which to wrap the output, this fails with an XmlException on the XElement.Parse(). Any suggestions on how to better handle sVal being either text or an xml fragment than would also handle the encoding how I expect?