What happened to the Messenger Plus! forums on msghelp.net?
Shoutbox » MsgHelp Archive » Messenger Plus! for Live Messenger » Scripting » Help with small XML reader

Help with small XML reader
Author: Message:
chrisctx
New Member
*


Posts: 4
Joined: May 2010
O.P. Help with small XML reader
I have no experience with Jscript so I'm struggling quite a bit, and would love some help

Basically I'm making a window with 3 listviews (I would love for it to be one with three columns, but I just cant get it to work), they display different elements of a certain XML file (I generate it via other means).

Anyways, here is what I made so far, I downloaded a few scripts and tried to copy what they where doing, but it didint work out as planned hehe.

code:

function OnEvent_Initialize(MessengerStart)
{
}

function OnEvent_Uninitialize(MessengerExit)
{
}

var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
function loadXML(xmlFile)
{
  xmlDoc.async="false";
  xmlDoc.onreadystatechange=verify;
  xmlDoc.load(xmlFile);
  xmlObj=xmlDoc.documentElement;
}

function OnEvent_MenuClicked (MenuItemId, Location, OriginWnd)
{
    // Captures when the user clicks on the 'WLMBb' menu
    if (MenuItemId=="Manage") fillTable();
}

function onTareasEvent_CtrlClicked (PlusWnd, ControlId)
{
    if (ControlId == "Refresh")
    {
        fillTable();
    }   
}

function fillTable()
{
    // Opens the window and loads the tasks from the XML file.
    var Manage = MsgPlus.CreateWnd("Interface.xml", "Manage");

    loadXML('C:\\Program Files\\Messenger Plus! Live\\Scripts\\wlmbb\\tasks.xml');
    TaskList = xmlObj.getElementsByTagName("tasks")[0].getElementsByTagName("task");
    IntTaskList = TaskList.lenght
   
    for (IntCurrentTask = 0; IntCurrentTask < IntTaskList; IntCurrentTask++)
    {
        CurrentTask = xmlObj.getElementByTagName("tasks")[0].getElementsByTagName("task")[IntCurrentTask];
        IntId = 0;
        Manage.LstView_AddItem("ListViewName", CurrentTask.getElementsByTagName("name")[0].text, IntId++);
        Manage.LstView_AddItem("ListViewExpiration", CurrentTask.getElementsByTagName("name")[0].text, IntId++);
        Manage.LstView_AddItem("ListViewCourse", CurrentTask.getElementsByTagName("name")[0].text, IntId++);
    }
}




And here is an example of the XML it should read:

code:
<?xml version="1.0" encoding='UTF-8'?>
<tasks>
    <task id="1">
        <name>nameofthistask1</name>
        <expiration>may 21 2010</expiration>
        <course>nameofthiscourse1</course>
    </task>
    <task id="2">
        <name>somethingelse</name>
        <expiration>may 22 2010</expiration>
        <course>Cooking class</course>
    </task>
    <task id="3">
        <name>listview</name>
        <expiration>may 23 2010</expiration>
        <course>scripting</course>
    </task>
    <task id="4">
        <name>WLMBb</name>
        <expiration>mayo 24 2010</expiration>
        <course>coolstuff</course>
    </task>
</tasks>



I would really appreciate if you guys could help me, I already made a program that does all of this in c#, but I wanted to surprise my teacher with a program that runs directly on messenger (since the purpose of this script is to share information between us, something we do in messenger a lot).

Thanks in advance.
05-25-2010 05:36 AM
Profile E-Mail PM Find Quote Report
matty
Scripting Guru
*****


Posts: 8336
Reputation: 109
39 / Male / Flag
Joined: Dec 2002
Status: Away
RE: Help with small XML reader
Here is some source code that we use in Screenshot Sender. If it doesn't make sense let me know.

js code:
function EnumControls(File, pPlusWnd) {
    _debug.getfuncname(arguments);
    // Load the XML from the specified file
    var XML = new ActiveXObject('MSXML.DOMDocument');
    XML.load(File);
    // Loop through all of the controls
    var Controls = XML.selectNodes('/Interfaces/Window[@Id=\'' + pPlusWnd.WindowId + '\']/Controls/Control');
    for (i=0; i<Controls.length; i ++) {
        var Id = Controls[i].getAttribute('Id');
        var Type = Controls[i].getAttribute('xsi:type');
        // Controls starting with _ are options but not stired in the registry (ie Debug info is in the ScriptInfo.xml)
        if (Id.charAt(0) !== '_') {
            // Get the value depending on the control's type
            objControls[Id] = {};
            objControls[Id].XsiType = Type;
            objControls[Id].Value = pPlusWnd_GetControlvalue(pPlusWnd, Id, Type);
        }
    }
}
05-25-2010 11:35 AM
Profile E-Mail PM Find Quote Report
chrisctx
New Member
*


Posts: 4
Joined: May 2010
O.P. RE: Help with small XML reader
Hmm, seems far simpler that way...

So my script should go something like this:

code:
var Tasks = XML.selectNodes('/Tasks/Task');

for (i=0; i<Tasks.length; i ++)
{
Manage.LstView_AddItem("ListViewName", Tasks[i].methodtogetvalueofname, IntId++);

...
}


What would be the method to get the value of <name>myname</name> for a given task?

Really appreciate the help ^_^

This post was edited on 05-25-2010 at 03:41 PM by chrisctx.
05-25-2010 03:39 PM
Profile E-Mail PM Find Quote Report
Matti
Elite Member
*****

Avatar
Script Developer and Helper

Posts: 1646
Reputation: 39
32 / Male / Flag
Joined: Apr 2004
RE: Help with small XML reader
You can use selectSingleNode(strPattern) to select the first descendant node which matches a certain pattern, like a tag selector.
js code:
var Tasks = XML.selectNodes("/Tasks/Task");

for (i=0; i<Tasks.length; i ++) {
    var TaskNode = Tasks[i];
    var NameNode = TaskNode.selectSingleNode("Name");
    Manage.LstView_AddItem("ListViewName", NameNode.text, IntId++);
}
If you need to access lots of child nodes from one task node, you might prefer iterating through the childNodes for each task node iteration.

For a complete overview of all objects, function and properties available in the XML DOM, have a look at DevGuru's reference. You might also want to learn some XPath, the query language of the XML DOM: XPath on W3Schools.
Plus! Script Developer | Plus! Beta Tester | Creator of Countdown Live | Co-developer of Screenshot Sender 5

Found my post useful? Rate me!
05-25-2010 04:03 PM
Profile E-Mail PM Web Find Quote Report
chrisctx
New Member
*


Posts: 4
Joined: May 2010
O.P. RE: Help with small XML reader
Just did a quick read of XPath and it seems quite simple, but I'll stick to the selectSingleNode for now.

I'm currently testing the script but I think that I'm failing to load the actual tasks.xml file (its located inside my script folder)

code:
    var XML = new ActiveXObject('MSXML.DOMDocument');
    XML.load(MsgPlus.ScriptFilesPath + '/tasks.xml');


Any clue on what's wrong?
05-25-2010 06:07 PM
Profile E-Mail PM Find Quote Report
Matti
Elite Member
*****

Avatar
Script Developer and Helper

Posts: 1646
Reputation: 39
32 / Male / Flag
Joined: Apr 2004
RE: Help with small XML reader
Could you provide us with the details from the script debugger window?

Anyway, I think there are two possibilities for this problem:
  1. The ActiveXObject couldn't be created.
    I had this before, for some reason my Windows installation couldn't recognize Microsoft.XMLDOM any more. After some research, I found that MSXML2.DOMDocument still worked, but then again that didn't work on other computers. I decided to implement both of them in a try...catch block so that it would always find a working ActiveXObject.
    So, instead of using new ActiveXObject('MSXML.DOMDocument');, I now call CreateXML() after defining it as followed:
    js code:
    //Creates a new XMLDOM instance
    function CreateXML() {
        try {
            var xml = new ActiveXObject("Microsoft.XMLDOM");
        } catch(e) { try {
            var xml = new ActiveXObject("MSXML2.DOMDocument");
        } catch(e) { return; } }
        xml.async = false;
        return xml;
    }
  2. The path may be faulty.
    I see that you're using a slash in your path, try a backslash instead. Of course, in JScript you have to escape a backslash with a backslash, so that line should look something like this:
    js code:
    XML.load(MsgPlus.ScriptFilesPath + '\\tasks.xml');
  3. The file might still be loading when you try to access it.
    This can be easily fixed by setting async to false, forcing the XML DOM object to load the XML file synchronously. As you can see above, my function does this automatically since I don't use asynchronous XML loading very often in my scripts.
It would help if you posted the full error message though. :)

Hint: these forums can do syntax highlighting by wrapping your code in [code=js] ... [/code] tags (for JScript, that is), it makes your code easier to read for others. ;)
Plus! Script Developer | Plus! Beta Tester | Creator of Countdown Live | Co-developer of Screenshot Sender 5

Found my post useful? Rate me!
05-25-2010 06:25 PM
Profile E-Mail PM Web Find Quote Report
chrisctx
New Member
*


Posts: 4
Joined: May 2010
O.P. RE: Help with small XML reader
Tried everything suggested, the script seems to just ignore my function calls =/


Here is my entire code:

js code:

function OnEvent_Initialize(MessengerStart)
{
}

function OnEvent_Uninitialize(MessengerExit)
{
}

function OnEvent_MenuClicked (MenuItemId, Location, OriginWnd)
{
    // Captures when the user clicks on the 'WLMBb' menu
    if (MenuItemId=="Manage") fillTable();
}

function CreateXML() {
    try {
        var xml = new ActiveXObject("Microsoft.XMLDOM");
    } catch(e) { try {
        var xml = new ActiveXObject("MSXML2.DOMDocument");
    } catch(e) { return; } }
    xml.async = false;
    return xml;
}

function onManageEvent_CtrlClicked (PlusWnd, ControlId)
{
    if (ControlId == "Refresh")
    {
        fillTable();   
    }   
}

function fillTable()
{
    // Opens the window and loads the tasks from the XML file.
    var Manage = MsgPlus.CreateWnd("Interface.xml", "Manage");

    var XML = CreateXML();
        XML.load('tasks.xml');
    var Tasks = XML.selectNodes('/Tasks/Task');
    IntTaskList = Tasks.lenght
    IntId = 0;
    for (i=1; i<Tasks.length; i ++)
    {
       
        var TaskNode = Tasks[i];
        var NameNode = TaskNode.selectSingleNode("Name");
        var ExpiNode = TaskNode.selectSingleNode("Expiration");
        var CrseNode = TaskNode.selectSingleNode("Course");
        Manage.LstView_AddItem("ListViewName", NameNode.text, IntId++);
        Manage.LstView_AddItem("ListViewExpiration", ExpiNode.text, IntId++);
        Manage.LstView_AddItem("ListViewCourse", CrseNode.text, IntId++);   
    }
}


And this is all the output that the debug window displays

code:
Script is starting
Script is now loaded and ready
Function called: OnEvent_Initialize
Function called: OnEvent_MenuClicked
Function called: OnManageEvent_CtrlClicked


I appreciate the help tremendously, and I now love you to death for introducing me to XPath, that thing is brilliant :D
05-26-2010 01:10 AM
Profile E-Mail PM Find Quote Report
Matti
Elite Member
*****

Avatar
Script Developer and Helper

Posts: 1646
Reputation: 39
32 / Male / Flag
Joined: Apr 2004
RE: Help with small XML reader
How many tasks are in the tasks.xml? I can already see one issue, namely that you're starting from index 1 for your loop. In JScript (and actually all languages), arrays and collections start at index 0 so you're skipping the first element with that loop. I believe that you have just one task in the XML for testing this and therefore the for-loop immediately exits when it checks the condition.

Also, you don't need those IntTaskList and IntId variables. The length of the tasks list is only used once (for the loop condition) and the identifier is unnecessary and even incorrectly used. You're using it to determine the position of the inserted list-view item, however you're actually incrementing it three times for each item. For the first list-view, you increment the position, but you're also doing this for the second and third list-view.

The positions you expect:
code:
Task index   ListViewName   ListViewExpiration   ListViewCourse
   0            0              0                    0
   1            1              1                    1
   2            2              2                    2
   3            3              3                    3
The positions you'd get:
code:
Task index   ListViewName   ListViewExpiration   ListViewCourse
   0            0              1                    2
   1            3              4                    5
   2            6              7                    8
   3            9             10                   11
The correct way to get this would be:
js code:
    var IntId = 0;
    for (var i=0; i<Tasks.length; i++)
    {
        var TaskNode = Tasks[i];
        var NameNode = TaskNode.selectSingleNode("Name");
        var ExpiNode = TaskNode.selectSingleNode("Expiration");
        var CrseNode = TaskNode.selectSingleNode("Course");
>>>        Manage.LstView_AddItem("ListViewName", NameNode.text, IntId);<<<
>>>        Manage.LstView_AddItem("ListViewExpiration", ExpiNode.text, IntId);<<<
>>>        Manage.LstView_AddItem("ListViewCourse", CrseNode.text, IntId);<<<
>>>        IntId++;<<<
    }
If you look closely, you don't even need an extra variable for this. You can just use the i variable you declare in the for-loop.

And if you look even closer, you don't need anything of this at all. If you don't specify an insert position, Plus! will simply add the new item to the end of the list (also in the documentation). So, you don't need to give it a position and you definitely don't need IntId for this!

Some other remarks:
  • Since you're also using this function as refresh function when the window is already open, it is better to move the window creation outside the fillTable function. Otherwise, you'll be opening new windows every time you click Refresh.
  • Also for your refreshing, you need to clear the list-views before re-adding the items. The fastest way to do this is by sending LVM_DELETEALLITEMS to the three controls.
  • The for-loop can be optimized by storing the length of the collection and by using pre-incrementation instead of post-incrementation. This is most of the time not very important, but it's good practice to use efficient loops.

Thus, all of this together:
js code:
function OnEvent_MenuClicked(MenuItemId, Location, OriginWnd)
{
    // Captures when the user clicks on the 'WLMBb' menu
    if (MenuItemId === "Manage")
    {
        // Open the window
        var Manage = MsgPlus.CreateWnd("Interface.xml", "Manage");
        // Refresh the table
        fillTable();
    }
}

function CreateXML()
{
    try    {
        var xml = new ActiveXObject("Microsoft.XMLDOM");
    } catch(e) { try {
        var xml = new ActiveXObject("MSXML2.DOMDocument");
    } catch(e) { return; } }
    xml.async = false;
    return xml;
}

function OnManageEvent_CtrlClicked(PlusWnd, ControlId)
{
    if (ControlId == "Refresh")
    {
        // Refresh the table
        fillTable();
    } 
}

function fillTable()
{
    // Clear all list-views before adding new items
    var LVM_DELETEALLITEMS = 0x1009; // = LVM_FIRST + 9
    Manage.SendControlMessage("ListViewName", LVM_DELETEALLITEMS, 0, 0);
    Manage.SendControlMessage("ListViewExpiration", LVM_DELETEALLITEMS, 0, 0);
    Manage.SendControlMessage("ListViewCourse", LVM_DELETEALLITEMS, 0, 0);

    // Load the tasks from the XML file
    var XML = CreateXML();
    XML.load('tasks.xml');
    var Tasks = XML.selectNodes('/Tasks/Task');
    // Optimized the loop: start at 0, store length in len and use pre-increment
    for (var i=0, len=Tasks.length; i<len; ++i)
    {
        var TaskNode = Tasks[i];
        var NameNode = TaskNode.selectSingleNode("Name");
        var ExpiNode = TaskNode.selectSingleNode("Expiration");
        var CrseNode = TaskNode.selectSingleNode("Course");
        // No need to pass item position as parameter, Plus! will handle that
        Manage.LstView_AddItem("ListViewName", NameNode.text);
        Manage.LstView_AddItem("ListViewExpiration", ExpiNode.text);
        Manage.LstView_AddItem("ListViewCourse", CrseNode.text); 
    }
}

Note: Actually, there's still an even faster way to iterate through a node collection. Since all node objects are "truthy" (equal true), you can iterate through them with:
js code:
for(var i = 0, TaskNode; TaskNode = Tasks[i++]; ) {
    // ...
}
or using pre-incrementation:
js code:
for(var i = -1, TaskNode; TaskNode = Tasks[++i]; ) {
    // ...
}
However, that's some pretty advanced JScripting there. :P
Plus! Script Developer | Plus! Beta Tester | Creator of Countdown Live | Co-developer of Screenshot Sender 5

Found my post useful? Rate me!
05-26-2010 11:31 AM
Profile E-Mail PM Web Find Quote Report
« Next Oldest Return to Top Next Newest »


Threaded Mode | Linear Mode
View a Printable Version
Send this Thread to a Friend
Subscribe | Add to Favorites
Rate This Thread:

Forum Jump:

Forum Rules:
You cannot post new threads
You cannot post replies
You cannot post attachments
You can edit your posts
HTML is Off
myCode is On
Smilies are On
[img] Code is On