Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Parsing XML with LSL

Jarod Godel
Utilitarian
Join date: 6 Nov 2003
Posts: 729
04-19-2006 16:56
If anyone wants it, I've hacked together a small script that parses basic XML. It's pretty much alphaware -- hence my mentioning it here and not the Scripting Library -- but if anyone is interested if picking art my code, you're welcome it. It's in my LiveJournal.

As soon as I figure out how to handle attributes, I'll dump it in the Library. Also, I apologize if this has been done before. I searched for "xml" in both forums, and nothing that looked like a parser showed up.
_____________________
"All designers in SL need to be aware of the fact that there are now quite simple methods of complete texture theft in SL that are impossible to stop..." - Cristiano Midnight

Ad aspera per intelligentem prohibitus.
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
04-19-2006 17:45
Nice work :)

I posted an incomplete XML parser a long time ago, modeled off of the Java SAX parser:
/54/19/38505/1.html

It still has a few bugs that need squishing. I just started it to see if it could be done. The useful applications of the script never really surfaced, so it got put on the backburner.
==Chris
Eggy Lippmann
Wiktator
Join date: 1 May 2003
Posts: 7,939
04-19-2006 18:06
Good, now get it to read XML in OSMP's format and you got yourself an offline editor thing ;)
Jarod Godel
Utilitarian
Join date: 6 Nov 2003
Posts: 729
04-20-2006 21:44
Okay. I've got it parsing XML with attributes, but it's still just a little prototype. I'm going to crash now and make it more robust and modular tomorrow. For now, here's the protoype code and the XML it's parsing:

LSL code...
CODE
// A very basic XML Parser written in  LSL.

// global variables for reading, holding, and handling the initial XML read
string xmlFileName = "xml-test";
string xmlBody = "";
integer xmlBodyLine = 0;
key xmlBodyKey;

// Chops off the string up to the first, found element
// xml - XML string
// start - where the llSubStringIndex begins
// offset - the length to the right the substring needs to begin
string llReadTo(string xml, integer start, integer offset)
{
integer ReadTo = start + offset;
return llGetSubString(xml, ReadTo, -1);
}

// Returns the inner text of a node.
string llGetNodeText(string xml, string nodeName)
{
string elStart = "<" + nodeName;
string elStop = "</" + nodeName + ">";

integer elCloseTag = llSubStringIndex(xml, elStop);
integer elOpenTag = llSubStringIndex(xml, "/>");

// There has to be some kind of closing tag for the parsing to be valid
if (elCloseTag != -1 || elOpenTag != -1)
{
// For: <tag> ... </tag>
if (llSubStringIndex(xml, elStart) != -1 &&
(elCloseTag < elOpenTag || elOpenTag == -1))
{
integer nodeStart = llSubStringIndex(xml, elStart) + llStringLength(elStart) + 1;
integer nodeStop = llSubStringIndex(xml, elStop) - 1;
string nodeText = llGetSubString(xml, nodeStart, nodeStop);
return nodeText;
}

// For: <tag/>
else if (llSubStringIndex(xml, elStart) != -1 &&
(elCloseTag > elOpenTag || elCloseTag == -1))
{
integer nodeStart = llSubStringIndex(xml, elStart) + llStringLength(elStart) + 1;
integer nodeStop = llSubStringIndex(xml, "/>") - 1;
string nodeText = llGetSubString(xml, nodeStart, nodeStop);
return nodeText;
}

// For: invalid XML
else
{
return "";
}
}

else
{
return "";
}
}

// Counts the number of nodes in a string
integer llCountElements(string xml, string nodeName)
{
string elStart = "<" + nodeName;
integer elStartLen = llStringLength(elStart) + 1;
integer nodeCount = 0;

while (llSubStringIndex(xml, elStart) != -1)
{
// Increments node count
++nodeCount;

// Chops off the string up to the first, found element
xml = llReadTo(xml, llSubStringIndex(xml, elStart), elStartLen);
}

return nodeCount;
}

// Checks for attributes in an element
list llGetElementAttributes(string xml)
{
integer elTagEnd = 0;

if (llSubStringIndex(xml, "/>") == -1)
{
elTagEnd = llSubStringIndex(xml, ">") - 1;
}
else
{
if (llSubStringIndex(xml, " />") == -1)
{
elTagEnd = llSubStringIndex(xml, "/>") - 1;
}
else
{
elTagEnd = llSubStringIndex(xml, "/>") - 2;
}
}

xml = llGetSubString(xml, 0, elTagEnd);

list attributes = llParseString2List(xml, ["=\"", "\""], []);

return attributes;
}

// Gets an attribute value, based of an attribute's name, from apaired list
string llGetAttributeValue(list attributes, string name)
{
string value = "";
integer isName = 0;
integer isCntr = 0;

// isName is switched to 1 when the loop finds a list valuen with the attribute name
// in the next iteration, the next list value is assigned to the variable "value"
// and isName is reset to 0
while (llList2String(attributes, isCntr))
{
if (isName == 1)
{
value = llList2String(attributes, isCntr);
isName = 0;
}

else
{
if ((isCntr % 2) == 0)
{
if (llList2String(attributes, isCntr) == name)
{
isName = 1;
}
}
}

++isCntr;
}

return value;
}

// Gets a list of nodes
list llGetNodeList(string xml, string nodeName)
{
list nodeList;
integer elementCount = llCountElements(xml, nodeName);

// If nodes exist with the XML string, proceed
if (elementCount > 0)
{
integer nodeCntr;

for (nodeCntr = 0; nodeCntr < elementCount; nodeCntr++)
{
string tempText = llGetNodeText(xml, nodeName);

nodeList = nodeList + [tempText];


//if (llSubStringIndex(tempText, "<" + nodeName) != -1)
//{
// tempText = tempText + "</" + nodeName + ">";
//}
//else if (llSubStringIndex(tempText, "</") == -1)
//{
// tempText = tempText + "/>";
//}

xml = llReadTo(xml, llSubStringIndex(xml, tempText), llStringLength(tempText));
xml = llGetSubString(xml, llSubStringIndex(xml, "<" + nodeName), -1);

}
}

// If there are no such nodes in the string, return a one element
// list with "No nodes" inside.
else
{
nodeList = ["No nodes"];
}

return nodeList;
}

// This has to be here
default
{
state_entry()
{
if (xmlBody == "")
{
state readfile;
}

else
{
llSay(0, xmlBody);
}
}

touch_start(integer total_number)
{
list spies = llGetNodeList(xmlBody, "spy");
integer spyCntr = 0;

while (llList2String(spies, spyCntr) != "")
{
string xmlTemp = llList2String(spies, spyCntr);

list spyName = llGetElementAttributes(xmlTemp);

string nameSpy = llGetAttributeValue(spyName, "name");

if (nameSpy != "Sam")
{
string color = llGetNodeText(xmlTemp, "suit");

llSay(0, nameSpy + " has a " + color + " uniform.");
}

else
{
llSay(0, "I'm not sure what color " + nameSpy + " wears.");
}

++spyCntr;
}
}
}

// This simply reads XML from a Notecard.
// This can be removed when grabbing information from the web.
state readfile
{
state_entry()
{
// Makes sure xmlBody is empty
xmlBody = "";

// Starts the reading of the notecard
xmlBodyKey = llGetNotecardLine(xmlFileName, xmlBodyLine);
}

dataserver(key query_id, string data)
{
if (query_id == xmlBodyKey)
{
// Perform the following while there is text
if (data != EOF)
{
// Removes leading spaces or tabs
if (llSubStringIndex(data, "<") > 0)
{
data = llDeleteSubString(data, 0, (llSubStringIndex(data, "<") - 1));
}

// Appends the next line to xmlBody
xmlBody = xmlBody + data;

// Increments the line counter
++xmlBodyLine;

// Reads the next line
xmlBodyKey = llGetNotecardLine(xmlFileName, xmlBodyLine);
}

// If you've gotten to the end of the notecard, go back
else
{
state default;
}

}
}

state_exit()
{
xmlBodyLine = 0;
}
}


XML file named xml-test...
CODE
<?xml version="1.0" encoding="ISO-8859-1"?>
<spies>
<spy name="Clover" hair="blond">
<suit>red</suit>
</spy>
<spy name="Sam" hair="red" />
<spy name="Alex" hair="dark brown">
<suit>yellow</suit>
<sex>female</sex>
</spy>
</spies>


As always, my code is in the public domain. Have at it, y'all!
_____________________
"All designers in SL need to be aware of the fact that there are now quite simple methods of complete texture theft in SL that are impossible to stop..." - Cristiano Midnight

Ad aspera per intelligentem prohibitus.