Help with small XML reader - Printable Version
-Shoutbox (https://shoutbox.menthix.net)
+-- Forum: MsgHelp Archive (/forumdisplay.php?fid=58)
+--- Forum: Messenger Plus! for Live Messenger (/forumdisplay.php?fid=4)
+---- Forum: Scripting (/forumdisplay.php?fid=39)
+----- Thread: Help with small XML reader (/showthread.php?tid=94645)
Help with small XML reader by chrisctx on 05-25-2010 at 05:36 AM
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.
RE: Help with small XML reader by matty on 05-25-2010 at 11:35 AM
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);
}
}
}
RE: Help with small XML reader by chrisctx on 05-25-2010 at 03:39 PM
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 ^_^
RE: Help with small XML reader by Matti on 05-25-2010 at 04:03 PM
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.
RE: Help with small XML reader by chrisctx on 05-25-2010 at 06:07 PM
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?
RE: Help with small XML reader by Matti on 05-25-2010 at 06:25 PM
Could you provide us with the details from the script debugger window?
Anyway, I think there are two possibilities for this problem:
- 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;
}
- 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');
- 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.
RE: Help with small XML reader by chrisctx on 05-26-2010 at 01:10 AM
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
RE: Help with small XML reader by Matti on 05-26-2010 at 11:31 AM
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.
|