Using ASP to loop thru XML file and generate HTML

O

otherblandart

Hi All and thanks in advance for any help I can get with this!

I'm very new at ASP/XML/HTML, but pretty comfortable with VB/
VBScript.


I've been tasked with some web content development tasks, and among
them is a 'catalog' style page of images. Rather than hard-coding all
the items, I wanted to use an XML file to store all the image paths
and descriptive text, and just loop thru it to generate the HTML I
need. Later on I'll be adding things like filters on category, etc.,
but for now I just want to get a simple protoype working. Below is
what I've come up with but it just isn't working (keep getting HTTP
500 Internal Server Error with IE 6).


The objects/methods worked pretty well in VB, so I'm wondering if
it's
in my handling of the quotation marks...


Thanks again for the help!


Hal


[XML File]


<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<imgpath>images/success_do.jpg</imgpath>
<described>"Success Means Do.." Poster 24X16</described>
</item>
<item>
<imgpath>images/Think_Twice.jpg</imgpath>
<described>"Think Twice.. Act Once" Poster 18X36</described>
</item>
</items>


[ASP Page Text]


<html>
<%
dim strServer
dim objXML, xNodeList, xNode
dim i , s, sDesc, sPath
strServer = Request.ServerVariables("SERVER_NAME")
%>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=windows-1252">
<meta http-equiv="Content-Language" content="en-us">
<meta name="GENERATOR" content="Microsoft FrontPage 5.0">
<meta name="ProgId" content="FrontPage.Editor.Document">
<meta name="Author" content="Publishing, Desktop" />
<meta name="Contact" content="Publishing, Desktop" />
<title>Order Framed Art</title>
</head>
<body>
<%
set objXML = Server.CreateObject("Microsoft.XMLDOM")
sPath = "http://" & strServer & "/vendor_mgmt/docs/artlist.xml"
objXML.async = False
objXML.load(sPath)


If objXML.parseError.errorcode <> 0 then
Response.Write "Sorry, an error occurred retrieving information."
else
Set xNodeList =
objXML.documentElement.getElementsByTagName("item")
If xNodeList.length > 0 then
For i=0 to xNodeList.length-1
'[imgpath] element/field
sPath = "http://" & strServer & "/vendor_mgmt/" &
xNodelist.Item(i).childNodes(0).text
'[described] element/field
sDesc = xNodelist.Item(i).childNodes(1).text
s = "<a title=" & Chr(34) & sDesc & Chr(34) & " href=" &
Chr(34) & sPath & Chr(34)
s = s & & " target=" & Chr(34) & "_blank" & Chr(34) & ">"
Response.Write s
s = "<img border=" & Chr(34) & "0" & Chr(34) & " src=" &
Chr(34) & sPath & Chr(34) & " width=" & Chr(34) & "50" & Chr(34)
s = s & " height=" & Chr(34) & "50" & Chr(34) & " align="
& Chr(34) & "middle" & Chr(34) & "></a><br />"
Response.Write s
s = "<a href=" & Chr(34) & sPath & Chr(34) & " target=" &
Chr(34) & "_blank" & Chr(34) & ">" & i & ". "
s = s & sDesc & "</a>"
Response.Write s
next
else
'no items exist
end if
end if
%>
</body>
</html>
 
A

Anthony Jones

Hi All and thanks in advance for any help I can get with this!

I'm very new at ASP/XML/HTML, but pretty comfortable with VB/
VBScript.


I've been tasked with some web content development tasks, and among
them is a 'catalog' style page of images. Rather than hard-coding all
the items, I wanted to use an XML file to store all the image paths
and descriptive text, and just loop thru it to generate the HTML I
need. Later on I'll be adding things like filters on category, etc.,
but for now I just want to get a simple protoype working. Below is
what I've come up with but it just isn't working (keep getting HTTP
500 Internal Server Error with IE 6).


The objects/methods worked pretty well in VB, so I'm wondering if
it's
in my handling of the quotation marks...

For some reason I feel the need to write a complete article on this so here
it is:-
[XML File]


<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<imgpath>images/success_do.jpg</imgpath>
<described>"Success Means Do.." Poster 24X16</described>
</item>
<item>
<imgpath>images/Think_Twice.jpg</imgpath>
<described>"Think Twice.. Act Once" Poster 18X36</described>
</item>
</items>

I did have some comments on the XML here but I've removed them for the
moment. I will add a second post which will look at an alternative XML and
the use of XSL which may be more appropriate as you move forward. For now
though I'll consider some of the issues with the ASP/

Don't make any adjustments to your code because later I'll give you a
complete working sample.
[ASP Page Text]


<html>
<%
dim strServer
dim objXML, xNodeList, xNode
dim i , s, sDesc, sPath
strServer = Request.ServerVariables("SERVER_NAME")
%>

It would be better to place variables closer to the code itself. I usually
prefer to put as much code in the top of the page above the <html> element.
That keeps the code and HTML as separate as possible. You would still need
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=windows-1252">
<meta http-equiv="Content-Language" content="en-us">
<meta name="GENERATOR" content="Microsoft FrontPage 5.0">
<meta name="ProgId" content="FrontPage.Editor.Document">
<meta name="Author" content="Publishing, Desktop" />
<meta name="Contact" content="Publishing, Desktop" />

Frontpage has probably added these, I wouldn't include any of them. I would
especially avoid http-equiv. In ASP you have the ability to set the actual
http headers. In this case all you really need is to specify the char set
you are using:-

Response.CharSet = "Window-1252"
<title>Order Framed Art</title>
</head>
<body>
<%
set objXML = Server.CreateObject("Microsoft.XMLDOM")

Always use a version specific ProgID to create an XML DOM. I normally use
MSXML2.DOMDocument.3.0 because you can pretty certain that that is installed
on all servers.

If you want the latest DOM install MSXML6 and use MSXML2.DOMDocument.6.0.
Don't use 4.0 or 5.0.
sPath = "http://" & strServer & "/vendor_mgmt/docs/artlist.xml"

There is no need to try to fetch XML via HTTP when your code is running in
an application on the server that it resides in. The following would be a
better way to set sPath:-

sPath = Server.MapPath("/vendor_mgmt/docs/artlist.xml")

Now sPath contains the on disk physical file path that the XML DOM can load.
objXML.async = False
objXML.load(sPath)

Note that calling procedures when you aren't using a return value in
VBScript is done without parantheses. E.g.:-

objXML.load sPath

If objXML.parseError.errorcode <> 0 then
Response.Write "Sorry, an error occurred retrieving information."

When asserting that conditions are ok to continue in this way it is
acceptable to use Response.End(). Hence you could follow the above with:-

Response.End()
End If

The main body of your code doesn't therefore start off indented and doesn't
leave a reader of the code wondering what the additonal End If at the bottom
of the code is doing. BTW, from a code construction point of view its
better that the "Then" protion of an If Then Else EndIf construct contain
the nominal path of the code and the "Else" portion contain the exceptional
portion of code.

else
Set xNodeList =
objXML.documentElement.getElementsByTagName("item")

A problem with using getElementsByTabName is that it will return all
descendant elements with the specified tag name not just the direct
children. If at a later time you added a deeper hierarchy which just
happens to use a tagName of "item" for a different purpose then the method
above would include those elements as well. Better:-

Set oNodeList = objXML.documentElement.selectNodes("item")

That selects only child item elements.
If xNodeList.length > 0 then

This test is unnecessary at this point since the following For loop does
nothing if length is 0 anyway.
For i=0 to xNodeList.length-1

A better way to loop through a list of nodes is to use the For Each
construct:-

For Each oNode In oNodeList

You could then replace all use of xNodelist.Item(i) with simply oNode. This
would be quicker (you only retrieve the node reference once) and simpler.
'[imgpath] element/field
sPath = "http://" & strServer & "/vendor_mgmt/" &
xNodelist.Item(i).childNodes(0).text

There is no need to prefix the /vendor_mgmt/ path with the protocol or
server name. The browser will assume those to be the same as the current
page.

..childNodes(0) is retrieving the imgpath element on the assumption that it
is the first child element of item. That may not always be true. I use
this sort of function for this purpose:-

Function GetNodeText(roParent, rsPath, rsDef)
Dim oNode : Set oNode = roParent.selectSingleNode(rsPath)
If Not oNode Is Nothing Then
GetNodeText = oNode.text
Else
GetNodeText = rsDef
End If
End Function

Hence the line becomes:-

sPath = "/vendor_mgmt/" & GetNodeText(oNode, "imgpath",
"images/noimageavail.jpg")
'[described] element/field
sDesc = xNodelist.Item(i).childNodes(1).text

What happens if sDesc contains a character that has special meaning in HTML
such as & or <

The output will be broken such characters need to be encoded correctly.
This is done with the Server.HTMLEncode :-

sDesc = Server.HTMLEncode(GetNodeText(oNode, "described", "No description"))
s = "<a title=" & Chr(34) & sDesc & Chr(34) & " href=" &
Chr(34) & sPath & Chr(34)
s = s & & " target=" & Chr(34) & "_blank" & Chr(34) & ">"
Response.Write s

I suppose it may be a matter of preference but I think using "" in a string
as an escaped " is better than using a separate concatenation of & Chr(34)
&; if you look at the code closely you can see a syntax error which is easy
to do with so much contatenation.

Response.Write "<a title=""" & sDesc & """ href=""" & sPath & """
target=""_blank"">"
s = "<img border=" & Chr(34) & "0" & Chr(34) & " src=" &
Chr(34) & sPath & Chr(34) & " width=" & Chr(34) & "50" & Chr(34)
s = s & " height=" & Chr(34) & "50" & Chr(34) & " align="
& Chr(34) & "middle" & Chr(34) & "></a><br />"
Response.Write s

Now this it getting really ugly primarily down to the Chr(34) as I've
mentioned but also the use of the deprecated style attributes isn't helping.
Attributes such as border, width, height and align should now be applied
using the style attribute which takes the corresponding CSS attributes
border, width, height and vertical-align. Further since these are common to
a whole set of imgs it would be better to place the style in a selector in
an inpage CSS style element.

In the <head> of the page you could do this:-

<style type="text/css">
img {width:50px; height:50px; vertical-align:middle; border:none}
</style>

Now the code above becomes:-

s = "<a href=" & Chr(34) & sPath & Chr(34) & " target=" &
Chr(34) & "_blank" & Chr(34) & ">" & i & ". "
s = s & sDesc & "</a>"
Response.Write s

If you got your code working I think you will be disappointed with the
layout of the result. I think the <br /> previously output is in the wrong
place, I suspect you wanted the img followed by the text then the break.
Since img and the text are adjacent to each other there really isn't any
need for multiple <a></a>, just one the covers both image and text would be
sufficient.

Now having said all that the it worth noting that only three things vary per
loop sDesc, sPath and i. Yet most of the code is involved in pasting
strings together and writing them to the response. This a case where a HTML
block would be better:-

i = 0
For Each oNode In oNodeList
sPath = "/vendor_mgmt/" & GetNodeText(oNode, "imgpath",
"images/noimageavail.jpg")
sDesc = Server.HTMLEncode(GetNodeText(oNode, "described", "No
description"))
i = i + 1
%>
<div class="item">
<a href="<%=sPath%>" title="<%=sDesc%>" target="_blank">
<img src="<%=sPath%>">
<span><%=i%>. <%=sDesc%></span>
</a>
</div>
<%
Next


The above code is shorter, faster, more robust and easier to understand.
next
else
'no items exist
end if

Its here you might just want to test If oNodeList.length = 0 and output some
HTML to indicate no items were found.
end if
%>
</body>
</html>

Putting it all together then we get:-

<%
Dim i, sPath, sDesc
Dim oXML, oNodeList, oNode

Response.CharSet = "Window-1252"

Set oXML = Server.CreateObject("MSXML2.DOMDocument.3.0")
oXML.async = False
oXML.Load Server.MapPath("test.xml")

If oXML.parseError.errorCode <> 0 Then
Err.Raise 1001, "Loading DOM", oXML.parseError.reason
Response.End()
End If

%>
<html>
<head>
<style type="text/css">
div.item {margin-bottom:5px}
div.item img
{
width:50px; height:50px;
vertical-align:middle;
margin-right:2px;
border:none
}
div.noitems {font-size:16pt}
</style>
</head>
<body>
<%
Set oNodeList = oXML.documentElement.selectNodes("item")
i = 0
For Each oNode In oNodeList
sPath = "/vendor_mgmt/" & GetNodeText(oNode, "imgpath",
"images/noimageavail.jpg")
sDesc = Server.HTMLEncode(GetNodeText(oNode, "described", "No
description"))
i = i + 1
%>
<div class="item">
<a href="<%=sPath%>" title="<%=sDesc%>" target="_blank">
<img src="<%=sPath%>">
<span><%=i%>. <%=sDesc%></span>
</a>
</div>
<%
Next

If oNodeList.length = 0 Then
%>
<div class="noitem">No items found</div
<%
End If
%>
</body>
<html>
<%
Function GetNodeText(roParent, rsPath, rsDef)
Dim oNode : Set oNode = roParent.selectSingleNode(rsPath)
If Not oNode Is Nothing Then
GetNodeText = oNode.text
Else
GetNodeText = rsDef
End If
End Function
%>


Hope you find this helpful. I'll make another posting later which will look
at the structure of the XML and using XSL to generate the output.
 
O

otherblandart

Anthony,

Thanks for the help, I'd been going off of syntax I found elsewhere on
the web, so I appreciate the shortcuts to make it easier to read/code.

I wasn't able to get it to work with server scripting (<%%>), but once
I wrapped it in a VBScript <script> it seems to be working fine (with
a few syntactical changes, of course). My employer apparently does
some weird things with their servers, so I'm just going to roll with
it this way.

Thanks again!

H
 
O

otherblandart

Anthony,

Thanks for the help. Most of the syntax I'd posted I'd followed from
instructions I'd found elsewhere on the web, the tips for making it
easier to read/code are greatly appreciated!

For some reason I couldn't get the syntax to work as laid out in your
reply, but had some success when I tried switching from asp scripting
(<%-%>) to using VBScript (<script></script>). Some syntactical
changes were required, but it's working more or less the way I want it
to now. Not sure why I had to go that route, but apparently my
employer does some weird things with the settings on their web
servers, so it's anybody's guess.

Thanks again!

H
 
A

Anthony Jones

Anthony,

Thanks for the help. Most of the syntax I'd posted I'd followed from
instructions I'd found elsewhere on the web, the tips for making it
easier to read/code are greatly appreciated!

For some reason I couldn't get the syntax to work as laid out in your
reply, but had some success when I tried switching from asp scripting
(<%-%>) to using VBScript (<script></script>). Some syntactical
changes were required, but it's working more or less the way I want it
to now. Not sure why I had to go that route, but apparently my
employer does some weird things with the settings on their web
servers, so it's anybody's guess.

Probably because the default language is set to something other that
VBScript. Most like JScript (which given the choice would be my preference
as well).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,525
Members
44,997
Latest member
mileyka

Latest Threads

Top