logo

Easy JavaScript Parsing of Domino's ReadViewEntries XML

Remember the other day, while talking about JSON, I mentioned that parsing XML in JavaScript cross browser was a tedious nightmare. Well, I've found a simple way to get the information you want from Domino's ?ReadViewEntries XML using JavaScript.

Take a URL like this:

names.nsf/($VIMPeople)?ReadViewEntries&startkey=jake&count=10

Here's a cut-down snippet of the XML (restricted to 2 columns for the sake of brevity):

<?xml version="1.0" encoding="UTF-8"?>
 <viewentries toplevelentries="290">
  <viewentry position="113" unid="061...926" noteid="956">
   <entrydata columnnumber="0" name="$1">
    <text>Jake Howlett/ROCKALL</text>
   </entrydata>
   <entrydata columnnumber="1" name="$0">
    <text>Jake</text>
   </entrydata>
  </viewentry>
  <viewentry position="114" unid="791...7DC" noteid="9AE">
   <entrydata columnnumber="0" name="$1">
    <text>James Bond/ROCKALL</text>
   </entrydata>

Let's say we want to go through each of the ten rows and find the Notes username for each person. Here's the JavaScript to do that:

//assume root is the XML document object returned by an Ajax method!
var entries = root.getElementsByTagName("viewentry");
for (var i=0; i<entries.length; i++) {
 columns = entries[i].getElementsByTagName("text");
 alert(columns.item(0).firstChild.nodeValue);
}

Hopefully the code explains itself. It's worth noting though that this looks for the first text column. It wouldn't find the first date-time column as this is not returned as a <text> element but as a <datetime> element. If you're working with number columns or date columns you'd have to alter the getElementsByTagName() part. Note that, to get the third column, you'd go on to look for .item(2).

What might not make sense from this code is the .firstChild.nodeValue part that follows the getElementsByTagName bit. Surely the <text> element we're working on doesn't have a child and in turn this doesn't have a node to get a value of. Au contraire. This is where it all gets really confusing.

In the DOM everything is a node. Even when you've got a hold of the <text> node you're looking for the text within it is still another node. The problem herein is the way different browsers deals with this. Mozilla infamously deals with all whitespace as nodes. Because of these differences it's a nightmare to try and directly access a certain node within each result in the following way, which, in this case, would work, but only in IE:

entries[i].childNodes[0].childNodes[0].firstChild.nodeValue

Trust me. For the sake of your sanity you don't want to try messing with childNodes!! Stick with getElementsByTagName() if you know the type of the column you're looking for.

As a result of this eureka-like moment I've updated the NamePicker demo code and it now works in all browsers. You don't know how long I spent trying to work all this out. Now I prefer JSON more than ever. It's just that using ReadViewEntries XML makes a lot more sense some times.

Finally. While looking for a solution to this I managed to find this dubious Googlewhack, which will probably only last a day or so. Get it while it's hot.

Comments

    • avatar
    • Michael
    • Thu 22 Jun 2006 08:06 PM

    As I said the other day, ?ReadViewEntries with all of the extra arguments beats ?OpenView hands-down. I haven't had time to work on any server-side xsl prepackaging/view prepping with tags yet. I think there's a solution in there somewhere which can help us avoid the parsing nightmares. If I hit the proverbial dead end, I might try the web services route and mess about with the LS in there.

    IBM... make JSON an output option for ?ReadViewEntries !!

  1. Better yet, IBM should create XML that does not place the data type as the node name, it should be an attribute. Let the end user cast the data type if they want to.

    <entrydata columnnumber="0" name="$134">

    <value type="datetime">20011219T180000,00+00</value>

    </entrydata>

    Just another thing Domino developers have to compensate for.

  2. Fabulous Jake! I tried the demo in Safari and I was unable to delete names I had added until after I saved the form.

    Great stuff. Would you be up for a podcast with Julian and myself?

    Warm regards,

    Bruce

    • avatar
    • Michael
    • Fri 23 Jun 2006 12:23 AM

    I'd be inclined to go old school and treat the xml as a stream (read: document collection). We're even supplied with the document levels for determining if there are categories within the view.

    Can't use M$ XMLDOM parser or the minis running Safari will be left out. It's just a node tree, so it can be traversed using recursion. It's just unencrypted tagged data and I would take this any day over working with RPG-II. :)

  3. I found I was constantly getting employee info out of our company Notes database so I wrote the below script to get every node/value out of the view and put it all in an object. I use the sarissa javascript package for cross-browser compatabilty

    /***********************************************************

    convert XML data into JavaScript object

    pass url of xml - works only with Notes style xml

    !! Important - requires previous loading of sarissa javascript library

    ************************************************************/

    function NotesXmltoObject(xmlpath){

    var xmldoc = Sarissa.getDomDocument();

    retObject = new Object();

    xmldoc.async = false;

    xmldoc.load(xmlpath);

    if( xmldoc.parseError != 0) {

    retObject.Error = 'xml format error'

    }else {

    var NoOfNodes = xmldoc.getElementsByTagName('viewentry')[0].childNodes.length;

    for (i = 0; i < NoOfNodes; i++) {

    //loop through all Nodes in viewentries

    var NoOfAttributes = xmldoc.getElementsByTagName('entrydata')[i].attributes.length

    for (j = 0; j < NoOfAttributes; j++) {

    //loop through all attributes associated with current Node

    if (xmldoc.getElementsByTagName('entrydata')[i].attributes[j].nodeName == 'name') {

    // if the current attribute has a value of "name" then get it's value

    var xmlNode = xmldoc.getElementsByTagName('entrydata')[i].attributes[j].nodeValue

    //also get the value of the text associated with this Node

    var xlmNodeValue = xmldoc.getElementsByTagName('entrydata')[i].text

    retObject[xmlNode] = xlmNodeValue //put them in an object for easy retreval

    }

    }

    }

    }

    xmldoc = null;

    return retObject;

    };

    Call it with

    retObj = NotesXmltoObject(xmlpath);

    Then you can populate field values with :

    f.CCManager.value = retObj.Manager;

    f.ClaimentsName.value = retObj.Name;

    f.CostCentre.value = retObj.CostC;

    • avatar
    • Jake Howlett
    • Fri 23 Jun 2006 03:56 AM

    Jeff. Even better, what about:

    <entrydata columnnumber="0" name="$134" type="datetime">

    20011219T180000,00+00

    </entrydata>

    Bruce. Run out of people to ask? ;o)

  4. That would be fine for single values, but what about lists? Then you would have multiple "value" nodes. Might as well always use the value node.

    • avatar
    • Jake Howlett
    • Fri 23 Jun 2006 10:28 AM

    Jeff. Does Notes View XML cater for lists as it stands?

  5. Yup, uglier still.

    <entrydata columnnumber="5" name="$147">

    <textlist>

    <text>First Value</text>

    <text>Second Value</text>

    <text>Third Value</text>

    </textlist>

    </entrydata>

    *sigh*

    • avatar
    • Bruce
    • Fri 23 Jun 2006 03:55 PM

    LOL - @Jake I didn't say we would podcast anytime soon now did I :-). Ping me offline so we can discuss.

    Warm regards,

    Bruce

  6. &preformat formats datetime into text

    xPath example:

    columnNode=xmldoc.selectNodes("/viewentries/viewentry/entrydata[@columnnumber='0']");

    for(i=0;i<columnNode.length;i++){

    alert(columnNode[i].text);

    }

    How to support selectNodes in FireFox:

    {Link}

    • avatar
    • David Schmidt
    • Tue 27 Jun 2006 01:41 AM

    Google AJAXSLT: {Link}

    {Link}

  7. The solution you have given is good. But how can we add a separator like comma between the list of items? I'm thankful if you give a solution.

  8. I've often wanted just basic XML from a view without having to reinvent it for every view. Here's an agent that takes a view and spits out basic, nested XML using the column titles. All categorized columns must come first. I haven't allowed for XML that needs CDATA tags yet. The URL must contain the viewname you want to get data from and can optionally contain the tag name for individual records.

    Example URL:

    {Link}

    Sub Initialize

    Dim session As New NotesSession

    Dim db As NotesDatabase

    Dim nav As NotesViewNavigator

    Dim vc As NotesViewColumn

    Dim entry As notesviewentry

    Dim nextentry As notesviewentry

    Dim firstentry As notesviewentry

    Dim lastentry As NotesViewEntry

    Dim view As NotesView

    'get viewname and record descriptor parameters from the query string

    Dim qs As String, viewname As String, recordname As String

    qs = session.DocumentContext.Query_String(0)

    param = "viewname"

    arg2 = Instr(qs, "&" + param + "=" ) + Len(param) + 2

    paramval = Mid ( qs, arg2)

    If Instr(paramval, "&") > 0 Then

    paramval = Left (paramval, Instr(paramval, "&")-1)

    End If

    viewname = paramval

    param = "record"

    arg2 = Instr(qs, "&" + param + "=" ) + Len(param) + 2

    paramval = Mid ( qs, arg2)

    If Instr(paramval, "&") > 0 Then

    paramval = Left (paramval, Instr(paramval, "&")-1)

    End If

    record = paramval

    If record = "" Then

    record = "record"

    End If

    Dim i As Integer

    Dim leveldeep As Integer

    Set db = session.CurrentDatabase

    Set view = db.GetView(viewname)

    Set nav= view.CreateViewNav

    Dim closingtags(80) As String

    'get an array of column headings zero-based

    Dim charr () As String

    For i = 0 To view.ColumnCount-1

    Redim Preserve charr(i) As String

    Set vc = view.Columns(i)

    charr(i) = vc.Title

    Next

    'determine how many of those columns are categories

    Dim catcount As Integer, noncatcount As Integer

    catcount = 0

    Forall c In view.columns

    If c.IsCategory Then

    catcount = catcount + 1

    End If

    End Forall

    noncatcount = view.ColumnCount - catcount

    Print |Content-type: text/xml|

    Print |<?xml version="1.0" encoding="UTF-8"?>|

    Print |<viewentries>|

    Set entry = nav.GetFirst

    leveldeep = 0

    While Not entry Is Nothing

    'print categories

    If entry.IsCategory Then

    Print |<| + charr(entry.indentlevel) +| value="| + entry.ColumnValues(entry.indentlevel) +|">|

    levelsdeep = entry.indentlevel

    Else

    'the tag that surrounds the data items is provided in the URL

    Print |<|+record+|>|

    'print all the rest of the columns

    For i = catcount To noncatcount+1

    Print |<| + charr(i) +|>|

    Print entry.ColumnValues(i)

    Print |</| + charr(i) +|>|

    Next

    Print |</|+record+|>|

    End If

    Set nextentry = nav.GetNext(entry)

    If nextentry Is Nothing Then

    'special case for end of view

    For i = entry.IndentLevel-1 To 0 Step -1

    Print |</| + charr(i) + |>|

    Next

    Else

    If nextentry.IndentLevel < entry.indentlevel Then

    For i = entry.IndentLevel -1 To nextentry.IndentLevel Step -1

    Print |</| + charr(i) + |>|

    Next

    End If

    End If

    'loop to next entry

    Set entry = nav.GetNext(entry)

    Wend

    Print |</viewentries>|

    End Sub

    Results in:

    <?xml version="1.0" encoding="UTF-8" ?>

    - <viewentries>

    - <colorcat value="Normal">

    - <language value="English">

    - <data>

    <color>blue</color>

    <city>9/11/2007 6:45:42 PM</city>

    <state>90</state>

    </data>

    - <data>

    <color>yellow</color>

    <city>9/14/2007 5:21:58 PM</city>

    <state>82</state>

    </data>

    </language>

    - <language value="French">

    - <data>

    <color>rouge</color>

    <city>9/11/2007 6:45:30 PM</city>

    <state>110</state>

    </data>

    - <data>

    <color>narange</color>

    <city>9/14/2007 5:17:25 PM</city>

    <state>92</state>

    </data>

    - <data>

    <color>verde</color>

    <city>9/14/2007 5:18:06 PM</city>

    <state>90</state>

    </data>

    </language>

    - <language value="Spanish">

    - <data>

    <color>naranja</color>

    <city>9/11/2007 6:46:52 PM</city>

    <state>105</state>

    </data>

    - <data>

    <color>rojo</color>

    <city>9/14/2007 5:16:08 PM</city>

    <state>102</state>

    </data>

    - <data>

    <color>azul</color>

    <city>9/14/2007 5:16:48 PM</city>

    <state>90</state>

    </data>

    - <data>

    <color>verde</color>

    <city>9/14/2007 5:17:50 PM</city>

    <state>91</state>

    </data>

    - <data>

    <color>amarillo</color>

    <city>9/14/2007 5:22:06 PM</city>

    <state>84</state>

    </data>

    </language>

    </colorcat>

    - <colorcat value="Special">

    - <language value="English">

    - <data>

    <color>fuscia</color>

    <city>9/11/2007 6:45:50 PM</city>

    <state>93</state>

    </data>

    - <data>

    <color>mauve</color>

    <city>9/11/2007 6:45:57 PM</city>

    <state>92</state>

    </data>

    - <data>

    <color>crimson</color>

    <city>9/14/2007 5:21:48 PM</city>

    <state>84</state>

    </data>

    - <data>

    <color>sky blue</color>

    <city>9/14/2007 5:22:16 PM</city>

    <state>85</state>

    </data>

    </language>

    - <language value="French">

    - <data>

    <color>mauve</color>

    <city>9/14/2007 5:17:09 PM</city>

    <state>91</state>

    </data>

    - <data>

    <color>chartruse</color>

    <city>9/14/2007 5:22:50 PM</city>

    <state>85</state>

    </data>

    </language>

    - <language value="Spanish">

    - <data>

    <color>azul cielo</color>

    <city>9/14/2007 5:22:35 PM</city>

    <state>87</state>

    </data>

    </language>

    </colorcat>

    </viewentries>

Your Comments

Name:
E-mail:
(optional)
Website:
(optional)
Comment:


About This Page

Written by Jake Howlett on Fri 23 Jun 2006

Share This Page

# ( ) '

Comments

The most recent comments added:

Skip to the comments or add your own.

You can subscribe to an individual RSS feed of comments on this entry.

Let's Get Social


About This Website

CodeStore is all about web development. Concentrating on Lotus Domino, ASP.NET, Flex, SharePoint and all things internet.

Your host is Jake Howlett who runs his own web development company called Rockall Design and is always on the lookout for new and interesting work to do.

You can find me on Twitter and on Linked In.

Read more about this site »

Elsewhere

Here are the external links posted on the same day.

More links are available in the archive »

More Content