If you have used xml-rpc extensively, you will know that one of the most annoying aspect of XML-RPC is the fact that NULL values are not supported as part of the core spec. This is an issue, because NULL is such a key concept in the mainstream languages such as Python, Java and .NET – not to mention that NULL is also a supported data type in mainstream relational DB such as MySQL and Postgres. This means that if you write an xml-rpc service that publishes a data service on top of a database, your code have to handle what to do with NULL values that are stored in the db.
For this, we rely on the vendor extension. Now, Python’s extension uses <nil />. Jayrock’s .NET xml rpc extension for null is also <nil />. However for Apache’s xml-rpc implementation, the null extension is expressed as <ex:nil />. If you have stumbled across this, fear not. You CAN tell apache xml-rpc to use both <nil /> and <ex:nil /> for Null:
XmlRpcTypeNil : The implementation
import org.apache.ws.commons.util.NamespaceContextImpl; import org.apache.xmlrpc.common.TypeFactoryImpl; import org.apache.xmlrpc.common.XmlRpcController; import org.apache.xmlrpc.common.XmlRpcStreamConfig; import org.apache.xmlrpc.parser.NullParser; import org.apache.xmlrpc.parser.TypeParser; import org.apache.xmlrpc.serializer.NullSerializer; import org.apache.xmlrpc.serializer.TypeSerializer; import org.xml.sax.SAXException; /** * This class extends Type Factory Impl to provide concrete type factory for handling * Null values. In this case the tag comparison uses NullSerializer.NIL_TAG as opposed * to EX_NIL_TAG * * @author zsugiart * */ public class XmlRpcTypeNil extends TypeFactoryImpl { public XmlRpcTypeNil(XmlRpcController pController) { super(pController); } public TypeParser getParser(XmlRpcStreamConfig pConfig, NamespaceContextImpl pContext, String pURI, String pLocalName) { if (NullSerializer.NIL_TAG.equals(pLocalName) || NullSerializer.EX_NIL_TAG.equals(pLocalName) )return new NullParser(); else return super.getParser(pConfig, pContext, pURI, pLocalName); } public TypeSerializer getSerializer(XmlRpcStreamConfig pConfig, Object pObject) throws SAXException { if (pObject instanceof XmlRpcTypeNil) return new NullSerializer(); else return super.getSerializer(pConfig, pObject); } } |
Client & Server configuration
The most important thing here is the XmlRpcController’s setTypeFactory method. Also remember that both XmlRpcClient and XmlRpcServer extends from XmlRpcController. Which means:
On the client end..
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); config.setServerURL("http://xmlrpc.url"); XmlRpcClient client = new XmlRpcClient(); client.setTypeFactory(new XmlRpcTypeNil(client)); // yes it's this easy! client.setConfig(config); // client.execute(...) |
And on the server end…
XmlRpcServer xmlRpcServer = this.webServer.getXmlRpcServer(); // or however it is setup xmlRpcServer.setTypeFactory(new XmlRpcTypeNil(this.xmlRpcServer)); |
Conclusion
This is such a simple trick, but it took me a while to work out how to work the apache xml-rpc API to make it do this. The benefit is also quite significant, with this implementation alone, we now can enable NULL data to be transported transparently between xml-rpc services implemented in Python, Java, .NET and other implementation that supports <nil /> for Null.