Menu

Using custom serializers in XmlRpcClient

2010-03-08
2013-04-25
  • Steven Kordik

    Steven Kordik - 2010-03-08

    Currently XmlRpcClient uses a private serializer, with no get/set methods.  This means it's impossible to register any XmlRpcCustomSerializer's in the client.

    Is there a way to do this that I am not aware of?  Or could methods be added to expose the serializer?

    I need this because the built in serializer doesn't seem to be able to serialize simple objects, for example:

    Public class Message {
        public String timestamp;
        public String userName;
        public Integer userId;
        public String message;
    }
    
     
  • Steven Kordik

    Steven Kordik - 2010-03-08

    A little research later and I discovered that the introspection looks for getters, so clearly it can't serialize the class above.

    I changed my class, but the client still throws redstone.xmlrpc.XmlRpcException: Could not serialize property:  when I try to call a method that returns a Message() object.

    My new Message:

    public class Message {
        private String timestamp;
        private String userName;
        private Integer userId;
        private String message;
    
        public void setTimestamp(String timestamp) {
            this.timestamp = timestamp;
        }
        public String getTimestamp() {
            return timestamp;
        }
        public void setUserName(String userName) {
            this.userName = userName;
        }
        public String getUserName() {
            return userName;
        }
        public void setUserId(Integer userId) {
            this.userId = userId;
        }
        public Integer getUserId() {
            return userId;
        }
        public void setMessage(String message) {
            this.message = message;
        }
        public String getMessage() {
            return message;
        }
    }
    
     
  • Steven Kordik

    Steven Kordik - 2010-03-09

    So I have this Message class:

    package org.cyberdeck.common.objects;
    public class Message {
        private String userName;
        private Integer userId;
        private String message;
    
        public void setuserName(String userName) {
            this.userName = userName;
        }
        public String getuserName() {
            return userName;
        }
        public void setuserId(Integer userId) {
            this.userId = userId;
        }
        public Integer getuserId() {
            return userId;
        }
        public void setmessage(String message) {
            this.message = message;
        }
        public String getmessage() {
            return message;
        }
    }
    

    It serializes without error on the server side (both client and server are using Redstone XMLRPC):

    The response is:

    <methodResponse>
    <params>
    <param> <value><struct>
    <member><name>message</name>
    <value><string>test</string></value>
    </member>
    <member><name>userId</name>
    <value><i4>1</i4></value>
    </member>
    <member><name>userName</name>
    <value><string>cezero</string></value>
    </member>
    </struct></value> </param>
    </params>
    </methodResponse>
    

    However, when my client receives this response, it cannot de-serialize it.  It throws: Exception java.lang.ClassCastException: redstone.xmlrpc.XmlRpcStruct cannot be cast to org.cyberdeck.common.objects.Message

    What am I doing wrong?  Or is object serialization simply broken in XMLRPC?  I'd use a custom serializer, but although I can configure that on the server side, there is no way to configure it on the client side currently (or at least, no way that I can find…)

     
  • Steven Kordik

    Steven Kordik - 2010-03-09

    Ok, after extensive searching of the code, I think I've figured it out.

    XMLRPC as a client cannot deserialize complex objects.  Instead, it returns them as XMLRpcStructs.  However, the XmlRpcProxy will try to cast these XMLRpcStructs into whatever object the interface it was created with returns.  Clearly this does not work.

    It also is a glaring feature gap in this XML RPC client.  The server side works great to serialize custom objects, and even supports custom serialization if required.  The client? Well, it can serialize objects on the way out the door, ie: as parameters (but with no custom serialization support at this time).  But it cannot deserialize them if they are passed by the server as a result.  And of course, the documentation is so sparse that this isn't clear to the end user up-front, and in fact, due to the way the client returns objects as an XmlRpcStruct, it means that you cannot use the same Interfaces for the client and server.

     
  • Greger Ohlson

    Greger Ohlson - 2010-03-09

    Sorry for the late reply, we seem to be in quite different timezones.

    I had a look at the library code and you are quite right that the XmlRpcClient is missing access to the XmlRpcSerializer object which is required to register custom serializers. It does however include the IntrospectingSerializer which uses introspection to pull values out of any POJO when serializing arguments to a server.

    XmlRpcSerializer, however, is only used to convert various types of Java-objects to XML-RPC values which only allows a small set of primitives and arrays and structs of primitives. Once a set of Java objects have been converted to XML-RPC all type information of the original object types is gone.

    Once an XML-RPC messages (either on the server, sent from the client, or on the client returned from the server) is received the XmlRpcParser converts the message to Java objects. Since no type information is available in the message apart from what the XML-RPC spec includes (and the message may come from any XML-RPC implementation in any language), there is no way for the parser to construct any other type of object than what it already knows, and where structs and arrays become XmlRpcStruct and XmlRpcArray. The documentation mentions this briefly:

    "Note: Although the serialization mechanism allows custom serializers to serialize any kind of object the original type information is not conveyed in the XML-RPC message since XML-RPC only supports generic structs and arrays. Once the message is deserialized by whatever implementation is used at the terminating end, it will be converted to the data types used to represents <struct> and <array>s. For Redstone XML-RPC, this means that the invocation handlers will accept java.util.List and java.util.HashMap (in addition to the basic datatypes supported by XML-RPC, like integers, doubles, Strings, Dates, booleans, and bytes."

    That said, I guess it could be possible to construct an XmlRpcParser that works specifically with XmlRpcProxy that has information about a particular client side return type. The parser could in those cases try to convert XmlRpcStructs and XmlRpcArrays, recursively, to whatever value type that is needed using Introspection. This functionality is not available though, but I can have a look if it is feasible.

    In the meantime the only way forward is unfortunately to have all server side handlers accept primitives and XmlRpcStruct (or java.util.HashMap), and XmlRpcArray (or java.util.ArrayList) and nested values of these types. The same goes for the client side if you are using XmlRpcProxy - the return types in the interface can only be of these known types. XmlRpcClient also always convert return types to the known object types (XmlRpcStruct, …) since it has no type information whatsoever to use during parsing - only the XML-RPC message which contains no datatype info. This is the way all XML-RPC implementations I've seen so far works.

    I'll have a look at the XmlRpcParser and see if it is possible to make it smarter and use Introspection.

    Best Regards,
    Greger.

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.