|
MiniXML API Overview and Demo
MiniXML allows you to easily parse and generate XML
using PHP or Perl.
Here is an overview of the API and, below
that, a demonstration. This text was originally
written with only PHP in mind, but the API is
practically identical for Perl (the main differences lying in the class names and the lack
of a funky =& reference assignment operator in perl - see the
module's perldoc for details). I must warn you that the demo code and program
proceed in a rather roundabout manner as this is example code. And
don't worry, it's not really that long, it's just that I'm quite
verbose in the comments.
Overview
The Basics
All your interactions with MiniXML begin with a MiniXMLDoc object.
To create one, simply:
$xmlDoc = new MiniXMLDoc();
Now that we have an object, we can use it to parse (read in) existing
XML and/or to create new XML.
Creating XML.
There are two ways for you to generate XML with MiniXML:
Using Arrays to create XML
Using nested associative arrays is the easiest way to create XML using miniXML. It is
perfect in cases where you are modeling objects. You can construct your array any manner
you like and then convert it to XML, like so:
// Create an array to represent our pal Bob
$bob = array(
'name' => array(
'first' => 'Bob',
'last' => 'Roberts'
),
'age' => 35,
'email' => 'bob@example.com',
'location' => array(
'streetaddr' => '123 Skid Row',
'city' => 'Dark City',
'state' => 'DN',
'country' => 'XNE',
),
);
// Another assosiative array contains a few things about Mary
$mary = array(
'name' => array(
'first' => 'Mary',
'last' => 'Zlipsakis'
),
'age' => 94,
'location' => array(
'streetaddr' => '54343 Park Ave',
'city' => 'SmallVille',
'state' => 'DN',
'country' => 'XNE',
),
'icecream' => 'vanilla',
);
// Now we'll create a big array that contains all our people
$xmlArray = array();
$xmlArray["people"]["person"] = array();
array_push($xmlArray["people"]["person"], $mary);
array_push($xmlArray["people"]["person"], $bob);
// With our array complete, we can create a MiniXMLDoc and fill it
// using fromArray()
$xmlDoc->fromArray($xmlArray);
print $xmlDoc->toString();
Which will display:
<?xml version="1.0"?>
<people>
<person>
<name>
<first> Mary </first>
<last> Zlipsakis </last>
</name>
<age> 94 </age>
<location>
<streetaddr> 54343 Park Ave </streetaddr>
<city> SmallVille </city>
<state> DN </state>
<country> XNE </country>
</location>
<icecream> vanilla </icecream>
</person>
<person>
<name>
<first> Bob </first>
<last> Roberts </last>
</name>
<age> 35 </age>
<email> bob@example.com </email>
<location>
<streetaddr> 123 Skid Row </streetaddr>
<city> Dark City </city>
<state> DN </state>
<country> XNE </country>
</location>
</person>
</people>
|
Manipulating MiniXML objects directly
To use the second MiniXML method to generate XML, you get hold of an element (an instance of
MiniXMLElement) and set attributes, add content or child elements.
Since our document is empty, the only available element is the
"root" element.
$xmlRoot =& $xmlDoc->getRoot();
Notice the reference assignment operator, =&. This is to ensure
that we work with the element itself instead of a mere copy.
Now we can add elements to our document root by creating children:
$childElement =& $xmlRoot->createChild('achild');
Elements can have attributes and content (data or child elements). Here we
set a few attributes and content.
$childElement->attribute('name', 'annie');
$childElement->attribute('eyes', '#0000FF');
$childElement->attribute('hair', '#FF0000');
$childElement->text('This element has attributes
and children, such as this');
$image =& $childElement->createChild('image');
$image->attribute('location',
'http://psychogenic.com/image.png');
$childElement->text('image and little');
You can also create orphan elements (that have no assigned parents), using the
MiniXMLDoc object:
$orphan =& $xmlDoc->createElement('song');
$orphan->text('tomorrow, tomorrow');
When you are done, make sure to link the orphan to some parent element (or it
will be lost forever).
$childElement->appendChild($orphan);
To have a look at the current structure, we can convert our document to an XML
string with
print $xmlDoc->toString();
Which will display:<?xml version="1.0" ?>
<achild name="annie" eyes="#0000FF" hair="#FF0000">
This element has attributes and children, such as this
<image location="http://psychogenic.com/image.png"/>
image and little
<song>
tomorrow, tomorrow
</song>
</achild>
|
Parsing XML and accessing data
Parsing XML with MiniXML is easy. When you've got a string of XML, you can
initialise a MiniXMLDoc from that string using the aptly named fromString()
method.
Create a MiniXMLDoc object:
$parsedDoc = new MiniXMLDoc();
Call fromString()
$parsedDoc->fromString($stringOfXML);
That's all. Now we can access the elements and their data using appropriate methods.
To access elements, start by getting the root element.
$rootEl =& $parsedDoc->getRoot();
Then use any of these methods:
getElement(NAME)
Will return the first child (or subchild) found with name NAME.
$returnedElement =& $rootEl->getElement('elementName');
getElementByPath(PATH)
If you wish to access a particular sub element, getElementByPath()
will return the matching element if found. For instance, with the example
XML generated above we could access the image element from the root element by
calling $returnedElement =& $rootEl->getElementByPath('achild/image');
$returnedElement =& $rootEl->getElementByPath('rel/path/to/elementName');
getAllChildren([NAME])
In cases where an element has many children with the same name and path, you can
access an array of all immediate children, eg
$elChildren =& $rootEl->getAllChildren();
for($i = 0; $i < $rootEl->numChildren(); $i++)
{
if ($elChildren[$i]->name() == 'aname')
{
/* We've found a child we're looking for */
/* do stuff... */
$itsValue = $elChildren[$i]->getValue();
print "$itsValue\n";
/* ... */
}
}
The optional NAME parameter will return only children with name NAME,
so the if name() == 'aname' step could be skipped by calling
$rootEl->getAllChildren('aname');
|
Example code
The mission
You need to communicate with xmlpartsserver.com to query for part
numbers and prices. They have set up a daemon that will accept TCP/IP
connections and talks XML.
Preparing a request
Now you use MiniXML to prepare this request:
// Use the MiniXML library
require_once('minixml.inc.php');
/* In this example, these are the part numbers we are interested in. */
$partNumbers = array('DA42', 'D99983FFF', 'ss-839uent');
/* Create a new MiniXMLDoc object. This document will be your
** interface to all the XML elements.
*/
$xmlDoc = new MiniXMLDoc();
/* XML is created in a hierarchical manner, like a tree. To start
** creating our request, we need this tree's root.
**
** NOTICE: That weirdo '=&' is not an ordinary assignment - it's PHP's
** way of asking that $xmlRoot be a REFERENCE to the Root Element.
** If you don't use =&, you'll be working on a copy and you'll need
** to $xmlDoc->setRoot($xmlRoot) when you're done.
**
*/
$xmlRoot =& $xmlDoc->getRoot();
/* I've imagined a fictitious structure for this request but
** they're usually something like this...
**
** Let's start by adding a partRateRequest element (as a
** child of the root element) and then we'll create some
** children of it's own.
**
** Again, note the use of the '=&'. The alternative is to
** use '=' and the $parent->appendChild($child) but be
** careful as the append is easy to forget...
*/
$rateReq =& $xmlRoot->createChild('partRateRequest');
/* Now we'll create a vendor and a parts list element for the
** request and fill those up.
*/
$vendor =& $rateReq->createChild('vendor');
$accessid =& $vendor->createChild('accessid');
/* Set up a few attributes for this element.
** notice that accessid will have attributes but no
** content (text or whatever) or children.
*/
$accessid->attribute('user', 'myusername');
$accessid->attribute('password', 'mypassword');
/* Now we list the parts we are interested. This element is
** directly under the partRateRequest element.
*/
$partList =& $rateReq->createChild('partList');
/* Now, we add a <partnum>XXX</partnum> element for
** each part in our array.
**
** Just for fun, here I'm using the createElement/appendChild
** method, instead of $partList->createChild()
*/
for($i=0; $i < count($partNumbers); $i++)
{
/* using MiniXMLDoc's createElement to create
** an element with no parent
*/
$aPart = $xmlDoc->createElement('partNum');
/* Set a text value to this element */
$aPart->text($partNumbers[$i]);
/* Now, don't forget to append this element to a parent
** or it will simply dissappear
*/
$partList->appendChild($aPart);
}
/* OK, we have our request in the xmlDoc. To pass it along to the
** server, we stringify it with:
*/
$xmlString = $xmlDoc->toString();
|
The preceding code will produce an XML document that looks like
this:
MiniXML toString() output
<?xml version="1.0" ?>
<partRateRequest>
<vendor>
<accessid user="myusername" password="mypassword"/>
</vendor>
<partList>
<partNum>
DA42
</partNum>
<partNum>
D99983FFF
</partNum>
<partNum>
ss-839uent
</partNum>
</partList>
</partRateRequest>
|
Parsing the response
Now let's assume we've recieved a valid but messed up reply from
the server. It looks like:
Ugly XML response from server
<partsRateReply> <status><code>1 </code> <message>
OK
</message></status> <partList><part num="DA42" models
=
"LS AR DF HG KJ"
update="2001-11-22"><name
>Camshaft end bearing
retention circlip
</name><image drawing=
"RR98-dh37" type= "SVG"
x="476"
y="226"/><
maker id
="RQ778">
Ringtown Fasteners Ltd</maker>
<price>
<currency>
USD </currency>
<amount> $389.99
</amount>
</price>
<notes>
Angle-nosed insertion tool
<tool id="GH25"/>
is required for the removal and replacement of this item.
</notes>
</part>
<part num="D99983FFF"
models
= "HG KJ"
><name>Thingamajig for guys with mustaches
</name><image drawing="RR9ou8-aoao92" type= "SVG"
x="3556" y="33"/><
maker id ="PQSMSM8"> RingWorm Nose Fasteners Inc</maker>
<price>
<currency>
USD </currency>
<amount> $292.00
</amount>
</price>
</part>
</partList>
</partsRateReply>
|
We start by creating a new MiniXMLDoc and setting it up by using
fromString() with the received string.
We will then be able to see a cleaned up version (by calling toString()
on the document) and will try out the methods associated with fetching
elements and their data.
/* We create a new MiniXMLDoc object */
$returnedXMLDoc = new MiniXMLDoc();
/* And parse the returned string (assume it was stored in
** $xmlString
*/
$returnedXMLDoc->fromString($xmlString);
/* Now to verify that the document has been initialised from
** the string, we use toString() to output an XML document.
*/
print $returnedXMLDoc->toString();
/* We can also get the data from the XML using the getValue()
** method. This will return all contents but no meta-data
** (ie strip the tags).
*/
print $returnedXMLDoc->getValue();
/* We can now query the document, fetching elements by name
** (and path) or as a list of children.
**
** Let's start by fetching the partsRateReply element.
**
** WARNING:
** Normally, you'd be verifying that the element was found
** but in order to keep this light and to make sure nobody
** gets into the habit of good error checking, I'll skip
** it.
**
**
** getElement() returns the first element with a matching name.
** if there are multiple elements with the same name, use one
** of the methods below.
*/
/* Note: the '=&' operator here is optional: since we won't
** be modifying the response it doesn't matter if we work on
** a copy.
*/
$rateResp =& $returnedXMLDoc->getElement('partsRateReply');
/* We can now use the rateResponse element to get access
** to it's children.
*/
$status =& $rateResp->getElement('status');
$statusMessage =& $status->getElement('message');
print "Status message is " . $statusMessage->getValue() ;
/* We can also access the elements by 'path' (if it is unique)
** To do so, use the getElementByPath method on an element. The parameter
** is a path relative to this element (ie you may only access this element
** and any of it children/subchildren).
**
**
** Here we use the MiniXMLDoc object (thus the root element), so we pass
** the 'full path' as a parameter.
*/
$statusCode = $returnedXMLDoc->getElementByPath('partsRateReply/status/code');
print "Status Code is " . $statusCode->getValue();
/* Sometimes, for instance when you have an element with
** multiple children with the same name like partNum, it's
** best to simply have access to all the children at once.
*/
/* First get the element */
$partList =& $returnedXMLDoc->getElementByPath('partsRateReply/partList');
/* Now get the list of children */
$partListChildren = $partList->getAllChildren();
/* we can use the returned array to play with all the children */
for($i=0; $i < $partList->numChildren(); $i++)
{
print "+++++++++++++++++++++++++++++++++++\n";
$name = $partListChildren[$i]->getElement('name');
$maker = $partListChildren[$i]->getElement('maker');
$priceAmmount = $partListChildren[$i]->getElementByPath('price/amount');
$partNum = $partListChildren[$i]->attribute('num');
print "Part $partNum Name: " . $name->getValue();
print "Part $partNum Maker:(" . $maker->attribute('id') . ") " . $maker->getValue() ;
print "Part $partNum Cost: " . $priceAmmount->getValue() ;
print "\n\n";
}
|
The output from this code is included below.
MiniXML output
Call to : $returnedXMLDoc->toString() returns:
<?xml version="1.0" ?>
<partsRateReply>
<status>
<code>
1
</code>
<message>
OK
</message>
</status>
<partList>
<part num="DA42" models="LS AR DF HG KJ" update="2001-11-22">
<name>
Camshaft end bearing
retention circlip
</name>
<image drawing="RR98-dh37" type="SVG" x="476" y="226"/>
<maker id="RQ778">
Ringtown Fasteners Ltd
</maker>
<price>
<currency>
USD
</currency>
<amount>
$389.99
</amount>
</price>
<notes>
Angle-nosed insertion tool
<tool id="GH25"/>
is required for the removal and replacement of this item.
</notes>
</part>
<part num="D99983FFF" models="HG KJ">
<name>
Thingamajig for guys with mustaches
</name>
<image drawing="RR9ou8-aoao92" type="SVG" x="3556" y="33"/>
<maker id="PQSMSM8">
RingWorm Nose Fasteners Inc
</maker>
<price>
<currency>
USD
</currency>
<amount>
$292.00
</amount>
</price>
</part>
</partList>
</partsRateReply>
Call to : $returnedXMLDoc->getValue() returns:
1 OK Camshaft end bearing retention circlip
Ringtown Fasteners Ltd USD $389.99 Angle-nosed insertion tool
is required for the removal and replacement of this item.
Thingamajig for guys with mustaches RingWorm Nose Fasteners
Inc USD $292.00
Status message is OK
Status Code is 1
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Part DA42 Name: Camshaft end bearing
retention circlip
Part DA42 Maker:(RQ778) Ringtown Fasteners Ltd
Part DA42 Cost: $389.99
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Part D99983FFF Name: Thingamajig for guys with mustaches
Part D99983FFF Maker:(PQSMSM8) RingWorm Nose Fasteners Inc
Part D99983FFF Cost: $292.00
|
Hooray. We've gotten through more than what you need to know about
MiniXML in this "Overview" :)
If you want to try it out yourself, download
MiniXML now or check out all the API
docs.
|
|