Share via


Web Q&A;: XML Data Islands, Updategrams, Stored Procedures, and More

MSDN Magazine

XML Data Islands, Updategrams, Stored Procedures, and More
Edited by Nancy Michell

Q I have a problem involving JScript® and ActiveX® objects. My code uses Visual Basic® to create an ActiveX object inside a JScript class. The problem is that when the page is refreshed, ActiveX is not released so the memory consumption grows.
      I've found some workarounds. The first releases references to class methods in other objects:

  function ObjectBranchSelection (TxtCode) {
  
TxtCode.onblur = _onBlur;
var.objMem = new ActiveXObject('TestMem.Class1');

function _onBlur()
{
alert ( objMem.Count );
}
}

var obj = new ObjectBranchSelection (textCodeP);
obj = null;
TxtCode.onblur = null

 

Another workaround is to declare objMem as a member of the class:

  function ObjectBranchSelection (TxtCode) {
  
TxtCode.onblur = _onBlur;
this.objMem = new ActiveXObject('TestMem.Class1');

function _onBlur()
{
alert ( objMem.Count );
}
}

var obj = new ObjectBranchSelection (textCodeP);
obj = null;

 

So I'd like to know why this problem occurs and why these workarounds work.

A
Try moving the _onBlur function outside of the OBS function. Are you aware that nesting the functions has closure semantics? Closures are expensive—do not use them unless you need to.
      _onBlur will hold references to the two ActiveX objects, because it forms a closure over the local variables of the enclosing function.
      The first workaround works because after you set TxtCode.onblur to null, the reference to the nested function _blur goes away, which means the stack frame it is holding (which includes the reference to your ActiveX object) also goes away.
      The second workaround works because the ActiveX object is no longer a local variable in the outer function, therefore a copy of it is not stored in the closure formed by _blur.

Q
I have an XML data island (called xmlDVD) to which I am trying to bind a table. Using the XML tag name as the datafld in each table cell, I am not getting any data back. What is wrong with the syntax of the island that keeps it from working correctly?

A
The problem is that data islands don't search recursively through the XML document to find your field (see Figure 1). They only look at the children of the element the table is bound to. You didn't specify a datafld property for your table, so it is binding to the root (the METADATA element), and it will only find elements that are children of this element.
      You need to set a datafld property on the table to get to the MOVIE element:

  <table border=2 bordercolor=red datasrc="#xmlDVD" datafld="MOVIE">
  

 

This will get you the first field that you're after, but not the others since they are another level down. To get to these, you need to create a subtable that binds to the titleInfo element. So the HTML should be something like this:

  <table border=2 bordercolor=red datasrc="#xmlDVD" datafld="MOVIE">
  
<tr>
<td><span datafld="movieTitle"></span></td>
<td><table border=1 bordercolor=red datasrc="#xmlDVD"
datafld="titleInfo">
<tr>
<td><span datafld="studio"></span></td>
<td><span datafld="leadPerformer"></span></td>
</tr>
</table>
</td>
</tr>
</table>

 

Q Given an XML data island, I want to insert a row called chapter and a table snippet. I need a function that I can run that will get down to the third level of the XML tree. Do you have any ideas or suggestions for this?

A
Assuming you have three-level nested tables as shown in Figure 2, here is the script that will get to the chapter level:

  var oNewChapterNode, oLastChapterNode; 
  

xmlDVD.XMLDocument.setProperty("SelectionLanguage", "XPath");
oLastChapterNode = xmlDVD.XMLDocument.documentElement.
selectSingleNode("//titleInfo/chapter[last()]");

if (oLastChapterNode)
{
oNewChapterNode = oLastChapterNode.cloneNode(true);
oLastChapterNode.parentNode.insertBefore(oNewChapterNode, null);
}

 

Q I get the following error in my ASPX file when I try to copy a node (with children) into a newly created XML document:

  Exception Details: System.ArgumentException: 
  
The node to be inserted is from a different document context.

 

Why is this happening?

A
You have to import the node to the current document before appending it. So, if you wanted to copy a node from doc2 to doc1, you can do something like the following:

  XmlNode tmpNode = doc1.ImportNode(doc2.DocumentElement.LastChild);
  
doc1.DocumentElement.AppendChild(tmpNode);

 

In straight C++, using IXMLDOMDocument interfaces, this works fine without an ImportNode call of any kind. MSXML does support moving nodes between documents. When MSXML originally shipped, the W3C had not yet published a method for moving nodes between documents, and this was the Microsoft® solution.
      If you're employing the .NET Framework, System.Xml does not let you move nodes between documents. Instead, you need to use the ImportNode method on XmlDocument. This is the W3C solution, published in DOM Level 2 (https://www.w3c.org/DOM).

Q
I have an ASP page that posts an updategram to my SQLXML virtual directory and returns the new IDs generated [see Figure 3]. Is there a way to format the return XML with XSL?

A
Add an XSL hidden parameter to your form—it will automatically be applied to the resultset of your updategram. You can add additional SQLXML queries to the updategram to bring back additional data if necessary:

  <INPUT type="hidden" name="xsl" value="template/MyTemplate.xsl" />
  

 

Q When I try to save data with a SQL updategram, I occasionally get back error information. How can I access this with XSL? The error messages do not seem to be in XML format:

  <ROOT xmlns:updg="urn:schemas-microsoft-com:xml-updategram">
  
<?MSSQLError HResult="0x80040e2f" Source="Microsoft OLE DB
Provider for SQL Server" Description=
"The statement has been terminated."?>
<?MSSQLError HResult="0x80040e2f" Source="Microsoft OLE DB
Provider for SQL Server" Description="Cannot insert duplicate
key row in object 'Client' with unique index
'IDX_Client_ClientName'."?>
<returnid>
<ClientID>21</ClientID>
<AddrID>21</AddrID>
</returnid>
</ROOT>

 

A The error is returned as a processing instruction, which is part of XML. XSLT provides functions for getting the content of such processing instructions by name, in this case MSSQLError. The content itself is unparsed—you get the whole content and you then need to parse the HResult yourself with scripting or some XPath string functions.
      If you look again at the template, you'll see that each node is explicitly compared while traversing the tree and templates are applied with explicit match instructions. If there is no PI in the document (there was no error), the PI template will be ignored and you should have some returnid elements to match against, and vice versa. Also, the <xsl:apply-templates match="returnid" /> element will keep matching all returnids until none are left.
      The XSLT is shown in Figure 4. The only problem is that the stylesheet requires two passes over the document, which doesn't scale well to very large results. This may be sufficient in some scenarios, however.
      If the returnid information is incorrect and causes an error, you won't want to display it. Can you still do this XSLT processing in one pass? Since errors are streamed as they occur, the only way to do it in one pass would be to build up the correct output until either an error is detected (in which case the error message will be displayed and the output discarded) or the end of the stream is reached. Since XSLT alone does not support multiple outputs, currently this cannot be done.
      You could fake it, though (if streaming performance is really so important). Just mark the output in the HTML with a particular class, say class="results," and when you hit the error, output a <SCRIPT> block that sets the results style to "hidden." Or generate a script block that uses document.write to wipe out whatever has been displayed so far. Or, since the <?MSSQLError?> tags can occur anywhere, not just as the first children of an element, you could improve streaming performance by just checking the first child and seeing if it's an appropriate PI. Then you wouldn't have to walk through the whole list of children looking for errors.

  <xsl:when test="ROOT/node()[not(self::text())][1]
  
[self::processing-instruction('MSSQLError')]">

 

Q I have a stored procedure that returns an XML string in the following format:

  <?xml version="1.0" encoding="utf-8" ?>
  
<root>
....elements, nodes, etc....
</root>

 

I need to know if there's any way to have the stored procedure insert the following into the output

  <?xml-stylesheet type="text/xsl" href="InsuranceProviderAdd.xsl"?>
  

 

so that it comes out like this:

  <?xml version="1.0" encoding="utf-8" ?>
  
<?xml-stylesheet type="text/xsl" href="InsuranceProviderAdd.xsl"?>
<root>
....elements, nodes, etc....
</root>

 

A The short answer is that you must manually prepend the stylesheet instruction to the XML returned from the stored procedure. Depending on how the database is being accessed, there are various ways to do this, but one way is to add SQL like the following in front of the stored procedure:

  select '<?xml-stylesheet type="text/xsl" 
  
href="InsuranceProviderAdd.xsl"?>'

 

Note that the XML declaration at the beginning of the XML that's returned is not inserted until the encoding is determined (and the encoding is determined in the middle tier); it's not inserted on the server side.
      The longer answer is that the Books Online documentation accompanying SQL Server™ describes various approaches to applying XSL to the results from SQL XML. XSL can be applied on the middle tier or the client side (or both), depending on the specific needs that you have.

Got a question? Send questions and comments to webqa@microsoft.comThanks to the following Microsoft developers for their technical expertise: Rhonda Bailey, Michael Brundage, Aaron Cushner, Derek Denny-Brown, Monica DeZulueta, Thuy Doan, Mike Hatch, Bill Lange, Eric Lippert, Brian Manthos, Jonathan Marsh, Oscar Morales, Eduard Rusanovschi, Michael Rys, Luis Gomez Sanchez, Peter Torr.

From the May 2002 issue of MSDN Magazine