Skip to content
This repository was archived by the owner on Jan 16, 2019. It is now read-only.

Docs XmlSerializer

Frank Kleine edited this page Apr 7, 2012 · 1 revision

Table of Contents

Creating XML documents with the XML serializer

Applies to Stubbles 1.6.0 or greater. For Stubbles versions before 1.6.0 please see Creating XML documents with the XML serializer.

The XML serializer package enables you to create complex XML documents without needing to write a lot of lines of code. Instead of creating the document tag by tag, you pass the XML serializer any PHP data structure (like an array or any object) and it converts it to XML.

Basic usage

XML serializer is using the XMLStreamWriter package to create the XML documents. This means that it can use any XML extension supported by this package to create the XML documents.

To create an XML document, you always have to follow these simple steps:

  1. Create an instance of stubXMLStreamWriter
  2. Create an instance of stubXMLSerializer
  3. Pass your data and the stream writer to stubXMLSerializer
  4. Extract the created XML from the XMLStreamWriter

In code, this means:

stubClassLoader::load('net::stubbles::xml::stubDomXMLStreamWriter',
                      'net::stubbles::xml::serializer::stubXMLSerializer');
$serializer = new stubXMLSerializer(new stubInjector());
$writer     = new stubDomXMLStreamWriter();
$data       = 'This is my data';

$serializer->serialize($data, $writer);
print $writer->asXML();

The output of this script is:

<?xml version="1.0" encoding="UTF-8"?>
<string>This is my data</string>

Instead of a string you could also pass an integer, boolean, array or even an object.

Handling of different data types

The serialize() method will accept any data-type that you pass in and will try to convert it to XML. The root tag of the document will depend on the type of the data; the following table will give you a short overview of how XmlSerializer handles the different types.

Type Root tag name Example XML
NULL-value null A null value will always be serialized to an empty <null/> tag.
boolean boolean A boolean value will be serialized to <boolean>true</boolean> or <boolean>true</boolean>
string string <string>This is a string</string>
integer integer <integer>42</integer>
double double <double>42.23</double</tt> |- | array | <tt>array An indexed array will be converted to <array><string>A string value</string><array>; the types will be used as tag names. When serializing an associative, the array keys will be used as tag names
object Name of the class See below for examples

Please note that instances of Iterator are serialized like an array.

Options for the serialization

The serialize() method accepts two additional optional parameters that can be used to specify options for the serialization.

$serializer = new stubXMLSerializer(new stubInjector());
$writer     = new stubDomXMLStreamWriter();
$data       = 'This is my data';
$serializer->serialize($data, $writer, 'root');
print $writer->asXML();

Now, the script will output:

<?xml version="1.0" encoding="UTF-8"?>
<root>This is my data</root>

Serializing objects

Serializing objects to XML works exactly the same way as serializing any other data structure:

class MyClass {
    private $ignore = 'ignore';
    public $foo = 'foo';

    public function getBar() {
        return 'bar';
    }

    public function setBar($bar) {
        throw new Exception('setBar has been called');
    }
}

$obj = new MyClass();

$serializer = new stubXMLSerializer(new stubInjector());
$writer     = new stubDomXMLStreamWriter();

$serializer->serialize($obj, $writer);

When serializing an instance of a class, XmlSerializer will export the following data:

  • All public properties
  • The return values of all public methods, that do not require any parameters. This does not include magic methods like __sleep() or the constructor of the class.

The above example will produce the following XML code:

<?xml version="1.0" encoding="UTF-8"?>
<MyClass>
    <foo>foo</foo>
    <getBar>bar</getbar>
</myclass>

By default, XmlSerializer will also use the class name as the tag name.

Influence the serialization using annotations

XmlSerializer allows you to influence how it treats objects by adding annotations to your classes.

The @XMLTag annotation

The @XMLTag annotation can be added to any class to override the name of the XML tag that this class will be serialized to. This enables you to override the default behaviour, where the class name is used as a tag name:

/**
*@XMLTag(tagName='user')
*/
class A_Long_User_Class {
    // ...
}

This enables you to choose any class name without forcing you use the same XML tag names.

You may also attach this annotation to any public property or getter method to define the name of the tag that will be used when serializing the property or return value:

/**
*@XMLTag(tagName='user')
*/
class A_Long_User_Class {
    /**
***@XMLTag(tagName='id')
***/
     public $userId;

    /**
***@XMLTag(tagName='name')
***/
     public function getRealname() {
         // ...
     }
}

When serializing an instance of this class, XmlSerializer will produce the following XML:

<user>
  <id>schst</id>
  <name>Stephan Schmidt</name>
</user>

Without the annotation, the XML would be:

<A_Long_User_Class>
  <userId>schst</userid>
  <getRealnameStephan Schmidt</getrealname>
</a_long_user_class>

Serializing array properties

If a property contains an indexed array, the @XMLTag annotation can also be used to override the name of the tag that will be used for the elements in the indexed array:

/**
*@XMLTag(tagName='user')
*/
class A_Long_User_Class {
    /**
***@XMLTag(tagName='groups',elementTagName='groupId')
***/
     public $groups = array('users', 'admins');
}

An instance of this class will be serialized to:

<user>
  <groups>
    <group>users</group>
    <group>admins</group>
  </groups>
</user>

Without the elementTagName property, the same object would be serialized to:

<user>
  <groups>
    <string>users</string>
    <string>admins</string>
  </groups>
</user>

If you do not want to create a container tag, this can be accomplished by setting the tagName property to false:

/**
*@XMLTag(tagName='user')
*/
class A_Long_User_Class {
    /**
***@XMLTag(tagName=false,elementTagName='groupId')
***/
     public $groups = array('users', 'admins');
}

This will result in:

<user>
  <group>users</group>
  <group>admins</group>
</user>

The @XMLAttribute annotation

The @XMLAttribute annotation can be used to tell XmlSerializer that a scalar property must not be serialized as an XML tag, but an attribute of the current open tag:

/**
*@XMLTag(tagName='user')
*/
class A_Long_User_Class {
    /**
***@XMLAttribute(attributeName='id')
***/
     public $userId;

    /**
***@XMLAttribute(attributeName='name')
***/
     public function getRealname() {
         // ...
     }
}

If you pass an instance of this class to XmlSerializer, it will produce XML like this:

<user id="schst" name="Stephan Schmidt"/>

This annotation cannot be applied to classes, as objects are no scalar types. By default, empty properties or method return values are not serialized to empty attributes. However, this can be changed by setting the skipEmpty property to false;

The @XMLFragment annotation

The @XMLFragement annotation is used to import an XML string, that is stored in a property or computed by a method into the generated XML document. This can be useful if you need to import XHTML or any other XML dialect, that is not created from an object structure into your document:

/**
*@XMLTag(tagName='entry')
*/
class BlogEntry {
    /**
***@XMLAttribute(attributeName='title');
***/
    public $title = 'Stubbles 0.1.0 released.';

    /**
***@XMLFragement(tagName='content');
***/
    public $content = '<a href="http://www.stubbles.net">Stubbles 0.1.0</a> has been released today';
}

If you serialize an instance of this class to XML, you will get the following document:

<entry title="Stubbles 0.1.0 released.">
  <content>
    <a href="http://www.stubbles.net">
      Stubbles 0.1.0
    </a>
    has been released today
  <content>
</entry>

The value of the $content property is not treated as a string, but as a part of the XML document. If you do not want a container tag for the fragment, set the tagName property to false.

The @XMLIgnore annotation

By default, XmlSerializer will export all public properties and all public methods that do not require any parameters. If you do not want to export a property or method return value, you may attach the @XMLIgnore annotation:

/**
*@XMLTag(tagName='user')
*/
class A_Long_User_Class {
    /**
***@XMLAttribute(attributeName='id')
***/
     public $userId;

    /**
***We do not want to export the e-mail address to XML
     * 
***@XMLIgnore
***/
     public $email
}

When serializing an instance of this class, it will produce:

<user id="schst"/>

If you omit the @XMLIgnore annotation, the e-mail address will be exported as XML tag:

<user id="schst">
  <email>foo@bar.com</email>
</user>

Serializing objects in a user-defined way

In case the default object serializing mechanism does not work for you there is another option for serializing objects. First create a new implementation of the net::stubbles::xml::serializer::stubObjectXmlSerializer interface. It contains one method, serialize($object, stubXMLSerializer $xmlSerializer, stubXMLStreamWriter $xmlWriter, $tagName. It receives the instance to serialize in the $object parameter, and instances of the serializer and the xml stream writer and is free to serialize the given object to XML as it likes.

In order to use a specific serializer implementation for an object other than the default one, the object class has to be annotated with the @XmlSerializer annotation:

/**
*@XmlSerializer(my::example::app::xml::UserSerializer.class)
*/
class AnotherUserExampleClass {
    /**
***id of user
***/
     public $userId;

    /**
***mail address
***/
     public $email
}

Now the XmlSerializer will create an instance of my::example::app::xml::UserSerializer and use this for serializing all instances of AnotherUserExampleClass.

Clone this wiki locally