XML Manipulation – ShiVa Engine

XML Manipulation

This tutorial will teach you how to read XML documents, manipulate them, send them to remote PHP scripts, or save them locally. In ShiVa script, XML is just another type of variable.

XML location

A XML document can be loaded from a local or distant location using the API function

bOK = xml.receive ( hXML, sURI ).

For local files, the URI parameter will be prefixed by “file://”.

xml.receive ( this.hMyXML ( ), "file://C:/Program  Files/StoneTrip/ShiVa/Data/Samples/MyXML.xml" )

For distant locations, the URI parameter will be prefixed by “http(s)://”.

xml.receive ( this.hMyXML ( ),  "http://developer.stonetrip.com/tuto/test.xml" )

For both types of location, this operation is asynchronous, so you will have to test the status of the XML before using it.

Loading a document

XML loading should be done in a separate state. We named ours “GetXML” for this sample. Start by calling the load command in _onEnter:

--------------------------------------------------------------------------------
function AIXMLManager.GetXML_onEnter ( )
--------------------------------------------------------------------------------
	xml.receive ( this.hMyXML ( ), "http://developer.stonetrip.com/tuto/test.xml" )
--------------------------------------------------------------------------------
end
--------------------------------------------------------------------------------

The status of an XML document is given by the API function

nStatus =  xml.getReceiveStatus ( hXML )

The nStatus value can be :

  • -3 : Parse failed. The XML is arrived but is not a valid XML.
  • -2 : The request failed. the server doesn’t respond or the URI is wrong (take care of caps in URI)
  • -1 : The XML is not in cache. You never use the receive function for this XML.
  • 0..0.99 : Receiving. (nStatus * 100) is the progress percentage.
  • 1 : the XML is ready to be used.

Check the status of the XML loading process in _onLoop using the nStatus value:

--------------------------------------------------------------------------------
function AIXMLManager.GetXML_onLoop ( )
--------------------------------------------------------------------------------
    local nXMLStatus = xml.getReceiveStatus ( this.hMyXML ( ) )
    if ( nXMLStatus == -3 )
    then
        log.warning ( "XML parse failed." )
        --Leave GetXML state for another state
        this.Idle ( )
    elseif ( nXMLStatus == -2 )
    then
        log.warning ( "XML not found." )
        --Leave GetXML state for another state
        this.Idle ( )
    elseif ( nXMLStatus == 1 )
    then
        log.message ( "XML completed." )
        log.message (  xml.toString ( xml.getRootElement ( this.hMyXML ( ) ) )  )
        --Process XML
        this.DoSomethingWithXML ( )
        --Leave GetXML state for another state
        this.Idle ( )
    end
--------------------------------------------------------------------------------
end
--------------------------------------------------------------------------------

Sending XML

A XML document can be sent to a local or distant location using the API function

bOK = xml.send ( hXML, sURI ).

If you want to store your XML locally as a file on your disk, prefix the location with “file://”:

Sample : xml.send ( this.hMyXML ( ), "file://C:/path/to/MyXML.xml" )

To communicate with a remove server over HTTP, the URI parameter has to be prefixed with “http://”:

xml.send ( this.hMyXML ( ),  "http://developer.stonetrip.com/tuto/Test_GetXML.php" )

This operation is again fully asynchronous, but most of the time, you don’t need to wait for it to end as it will not alter the internal XML variable. However if you must know, use

nStatus = xml.getSendStatus ( hXML )

The waiting loop code is the same as before.
If you use a local URI, the XML will be written into the file you provided. If you use a distant location, the XML will be sent in a POST request to the URL you provided:

<?php
if ( isset($_POST['STContent'] ) )
{
	$xml = $_POST['STContent'];
	echo $xml;
}
?>

Parse XML

An XML document has always only one Root Element which is its entry point. The Root node has one or many child nodes which in turn have one or many child nodes and so on.

 <recipe name="bread" prep_time="5 mins" cook_time="3 hours">
   <title>Basic bread</title>
   <ingredient amount="8" unit="dL">Flour</ingredient>
   <ingredient amount="10" unit="grams">Yeast</ingredient>
   <ingredient amount="4" unit="dL" state="warm">Water</ingredient>
   <ingredient amount="1" unit="teaspoon">Salt</ingredient>
   <instructions>
     <step>Mix all ingredients together.</step>
     <step>Knead thoroughly.</step>
     <step>Cover with a cloth, and leave for one hour in warm room.</step>
     <step>Knead again.</step>
     <step>Place in a bread baking tin.</step>
     <step>Cover with a cloth, and leave for one hour in warm room.</step>
     <step>Bake in the oven at 180(degrees)C for 30 minutes.</step>
   </instructions>
 </recipe>

To reach XML elements, you will have to retrieve a handle to their individual parent. Example: “Read only the amount of ingredient elements and their name” looks as follows:

local hRootelement = xml.getRootElement ( this.hMyXML ( ) )
if ( hRootelement )
then
    local hXMLEntry = xml.getElementFirstChildWithName ( hRootelement, "ingredient" )
    while ( hXMLEntry ~= nil )
    do
        local sIngredientName = xml.getElementValue ( hXMLEntry )
        local sIngredientAmount = "None"
        local hAmountAttribut = xml.getElementAttributeAt ( hXMLEntry, 0 )
        if ( hAmountAttribut )
        then
            sIngredientAmount = xml.getAttributeValue ( hAmountAttribut )
        end
        log.message ( "Ingredient : ", sIngredientName, " amount : ", sIngredientAmount )
        --next element
        hXMLEntry = xml.getElementNextSiblingWithName ( hXMLEntry, "ingredient" )
    end
end

This will output:

  • Ingredient : Flour amount : 8
  • Ingredient : Yeast amount : 10
  • Ingredient : Water amount : 4
  • Ingredient : Salt amount : 1

Example: “Read all children of instructions looks like this:

local hRootelement = xml.getRootElement ( this.hMyXML ( ) )
if ( hRootelement )
then
    local hXMLEntry = xml.getElementFirstChildWithName ( hRootelement, "instructions" )
    if ( hXMLEntry ~= nil )
    then
        for i = 0, xml.getElementChildCount ( hXMLEntry )-1
        do
            local hXMLChildEntry = xml.getElementChildAt ( hXMLEntry, i )
            if ( hXMLChildEntry ~= nil )
            then
                log.message ( "Step " .. (i+1) .. " : " .. xml.getElementValue ( hXMLChildEntry ) )
            end
        end
    end
end

This will output:

  • Step 1 : Mix all ingredients together.
  • Step 2 : Knead thoroughly.
  • Step 3 : Cover with a cloth, and leave for one hour in warm room.
  • Step 4 : Knead again.
  • Step 5 : Place in a bread baking tin.
  • Step 6 : Cover with a cloth, and leave for one hour in warm room.
  • Step 7 : Bake in the oven at 180(degrees)C for 30 minutes.

Build XML

You can build an XML from scratch by adding children to the Root element or modify values of existing elements. The name of the element and of the attributes must not contain spaces or symbols.
Example: “Add children to an existing element” looks as follows:

local hRootelement = xml.getRootElement ( this.hMyXML ( ) )
if ( hRootelement )
then
    local hXMLEntry = xml.getElementFirstChildWithName ( hRootelement, "instructions" )
    if ( hXMLEntry ~= nil )
    then
        --insert a new element at first place
        xml.insertElementChildAt ( hXMLEntry, 0, "step", "Buy the ingredients" )
        --add a new element after all children
        xml.appendElementChild   ( hXMLEntry, "step", "Eat" )
        for i = 0, xml.getElementChildCount ( hXMLEntry )-1
        do
            local hXMLChildEntry = xml.getElementChildAt ( hXMLEntry, i )
            if ( hXMLChildEntry ~= nil )
            then
                log.message ( "Step " .. (i+1) .. " : " .. xml.getElementValue ( hXMLChildEntry ) )
            end
        end
    end
end

This will output:

  • Step 1 : Buy the ingredients
  • Step 2 : Mix all ingredients together.
  • Step 3 : Knead thoroughly.
  • Step 4 : Cover with a cloth, and leave for one hour in warm room.
  • Step 5 : Knead again.
  • Step 6 : Place in a bread baking tin.
  • Step 7 : Cover with a cloth, and leave for one hour in warm room.
  • Step 8 : Bake in the oven at 180(degrees)C for 30 minutes.
  • Step 9 : Eat

  • slackBanner