Using ASP to loop thru XML file and generate HTML

Discussion in 'ASP General' started by otherblandart@hotmail.com, Nov 30, 2007.

  1. Guest

    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>
    , Nov 30, 2007
    #1
    1. Advertising

  2. <> wrote in message
    news:...
    > 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
    some code in <% %> interspersed in the HTML to effect loops and output
    values etc but the idea is to keep that sort of thing to a minimum.

    > <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:-

    Response.Write "<img src=""" & sPath & """ /></a>"


    > 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.


    --
    Anthony Jones - MVP ASP/ASP.NET
    Anthony Jones, Dec 1, 2007
    #2
    1. Advertising

  3. Guest

    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
    , Dec 3, 2007
    #3
  4. Guest

    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
    , Dec 3, 2007
    #4
  5. <> wrote in message
    news:...
    > 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).

    --
    Anthony Jones - MVP ASP/ASP.NET
    Anthony Jones, Dec 3, 2007
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Weng Tianxiang
    Replies:
    5
    Views:
    1,300
    Christophe
    Feb 16, 2006
  2. Replies:
    5
    Views:
    590
    benben
    Jan 31, 2006
  3. THTB
    Replies:
    0
    Views:
    174
  4. Max Williams
    Replies:
    3
    Views:
    158
    Robert Klemme
    Jan 6, 2009
  5. Isaac Won
    Replies:
    9
    Views:
    354
    Ulrich Eckhardt
    Mar 4, 2013
Loading...

Share This Page