Previous Section Next Section

Data Encoding/Decoding

Switching back to RPC, so far we've deferred the entire notion of how the data is converted between Java objects and the XML. This section will go into the steps involved in creating customized serializers and deserializers that can be used in Axis. Although the concept of what serializers and deserializers do is not terribly complex, writing one for Axis requires a good working knowledge of how to use a SAX parser. Chapter 2 had a good, but brief, discussion of how SAX parsers work and how to use them—this section will assume that you are well versed enough with SAX that usage of SAX terms, without the details behind them, will be appropriate.

As we discussed in Chapter 2, the concepts of serializers and deserializers are really quite simple—they are just pieces of code that will convert data (in its native state, such as a Java object) into XML in the serializing case, and from XML back into the data's native state in the deserializing case. If your classes are Java beans, then you can use the Bean serializer and deserializer that comes with Axis. To do this, all you have to do is tell Axis to use the Bean serializer/deserializer when it encounters your class. For example, on the client side the code might look like the following:

// register the PurchaseOrder class
String  URL = "http://localhost:8080/axis/servlet/AxisServlet";
ServiceClient client = new ServiceClient(URL);
QName qn = new QName("http://www.skatestown.com/ns/po", "po");
Class cls = com.skatestown.data.PO.class;
client.addSerializer(cls, qn, new BeanSerializer(cls));
client.addDeserializerFactory(qn, cls, BeanSerializer.getFactory());

In this code, the addSerializer() method will create a serializer association between the class PO and its namespace, http://www.skatestown.com/ns/po, and the BeanSerializer. Then we do the same thing for the deserializing side.

While on the server, a beanMapping would need to be deployed (for example, in the XML file passed to the AdminClient):

<beanMappings xmlns:bid=" http://www.skatestown.com/ns/po">
  <bid:po classname="com.skatestown.data.PO"/>
</beanMappings>

However, sometimes the BeanSerializer isn't enough, and you'll need an even more customized serializer/deserializer and specialized code that manually examines or creates the XML. In this case, it is just a matter of writing a few Java classes.

You need a serializing class that should implement the Serializer interface. This interface has just one method:

public interface Serializer extends java.io.Serializable {
    public void serialize(QName name, Attributes attributes,
                          Object value, SerializationContext context)
        throws IOException;
}

This method should, construct a block of XML with a root element with the given name and attributes, and the body should be the serialized version of value. This method assumes that SAX events will be generated against the SerializationContext passed to it. The SerializationContext object is a utility class that provides functions necessary for writing XML to a Writer, including maintaining namespace mappings, serialization of data objects, and automatic handling of multi-ref encoding of object graphs.

Next, you need a class that implements the DeserializationFactory interface. This interface has just one method, as well:

public interface DeserializerFactory extends java.io.Serializable
{
    public Deserializer getDeserializer(Class cls);
}

This method should return an instance of the Deserializer class. Whether it returns the same instance or a new instance each time is an implementation choice—you use a factory because the deserializers are processing SAX events, so they will need to maintain some state information between each SAX event callback. If a single deserializer existed and multiple threads were deserializing objects at the same time, they would override each other's work.

The final class you need to write is the deserializer class (the class the deserializer factory returns). This class should extend the Deserializer class. Inside this class should be any of the SAX callback methods needed to deserialize the incoming SAX event stream. The exact implementation of these methods is left completely up to you. The only requirement is that when it is done, the methods set a field in the base Deserializer class called value to the Java object represented by the XML. As a quick example, Axis comes with a Base64 serializer and deserializer. The deserializer is very small, and simply implements the SAX characters() method:

static class Base64Deser extends Deserializer {
    public void characters(char[] chars, int start, int end) throws
                          SAXException {
        setValue(Base64.decode(chars, start, end));
    }
}

The Base64.decode, method does the actual decoding and just returns a Java object.

    Previous Section Next Section