3

I am using RestTemplate for android client. I am using Simple XML annotated object and same java object with JaxB annotation on server side. I am successful in sending/recieving String and other primitive types but for byte array having problems. The byte array I am sending from Simple XML is converted to something else when I see on JaxB side on server. Below is the code..

JaxB annotated object on REST server

import java.io.Serializable;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Device implements Serializable
{
    private final static long serialVersionUID = 1L;
    protected byte[] imageRef;

    public Device() {
        super();
    }

    public byte[] getImageRef() {
        return imageRef;
    }

    public void setImageRef(byte[] imageRef) {
        this.imageRef = imageRef;
    }
}

Here is how the Rest server looks like.. I am using Apache CXF for that.

<bean id="xmlBeanProvider" class="org.apache.cxf.jaxrs.provider.XMLBeansElementProvider"/>
    <bean id="jacksonJsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"/>

    <jaxrs:server id="dataRESTService" address="/">
         <jaxrs:serviceBeans>
            <ref bean="MyDataRESTService"/>
        </jaxrs:serviceBeans>
        <jaxrs:providers>
            <ref bean="xmlBeanProvider"/>
            <ref bean="jacksonJsonProvider"/>
        </jaxrs:providers>
    </jaxrs:server>
    <context:component-scan base-package="com.xxx.restservices" />

and the controller

@Path("/mydata")
@Produces(MediaType.APPLICATION_XML)
@Consumes(MediaType.APPLICATION_XML)
@Service
public class MyDataRESTService 
{   
    @POST
    @Path("/testpost")
    public String testPost(Device device)
    {
        //device.getImageRef() returns [-41, 109, -8] for the bytes
    }
}

Here is how the Android client side looks

Simple XML Annotated object on Android

import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;

@Root
public class Device implements Serializable
{
    private final static long serialVersionUID = 1L;
    @Element
    protected byte[] imageRef;

    public Device() {
        super();
    }

    public byte[] getImageRef() {
        return imageRef;
    }

    public void setImageRef(byte[] imageRef) {
        this.imageRef = imageRef;
    }   
}

RestTemplate client with Simple XML converters

public void testPost() 
 {

          RestTemplate restTemplate = new RestTemplate();
          Serializer serializer = new Persister();
      restTemplate.getMessageConverters().add(new          SimpleXmlHttpMessageConverter(serializer));
       restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
       restTemplate.getMessageConverters().add(new FormHttpMessageConverter());

       Device device = new Device();
       device.setImageRef(new byte[]{1,2,3,4,5});
       String response =  restTemplate.postForObject("http://10.0.0.3:8080/rest/mydata/testpost", device, String.class);
        assertNotNull(response);

 }

So basically when I send byte array of byte[]{1,2,3,4,5} on server I get this byte array [-41, 109, -8]. I tried to decode with Base64 on server side thinking that may be simple xml is encoding it but couldn't figure out what is going on?

Similarly if I do a Get operation instead of Post then I get a NumberFormat exception in android client.. org.springframework.http.converter.HttpMessageNotReadableException Caused by: java.lang.NumberFormatException: Invalid int: "AQ==" at java.lang.Integer.invalidInt(Integer.java:138) at java.lang.Integer.parse(Integer.java:375) at java.lang.Integer.parseInt(Integer.java:366) at java.lang.Byte.parseByte(Byte.java:214) at java.lang.Byte.parseByte(Byte.java:195) at java.lang.Byte.valueOf(Byte.java:264) at org.simpleframework.xml.transform.ByteTransform.read(ByteTransform.java:55)

Any help will be greatly appreciated.

3 Answers 3

1

I followed up on @Blaise Doughan advice to write a custom converter. I wrote one on Simple XML side. However it is only working for sending data to server but for receiving it doesn't work. Here is how the converter looks. I think this may be a Simple XML issue and as such the answer to this post is that we have to write a custom converter. I posted a follow-up question for Simple XML guys to see why the read method is not called when we serialize the xml back to object? See the question here custom converter for simple xml is not working properly

I put a break point in the read method and looks like it was never called. Simple XML parser fails way before this exception org.springframework.http.converter.HttpMessageNotReadableException: Could not read [class cs.core.mobile.database.JaxBResponse]; nested exception is java.lang.NumberFormatException: Invalid int: "AAECAwQ="

import org.restlet.engine.util.Base64;
import org.simpleframework.xml.convert.Converter;
import org.simpleframework.xml.stream.InputNode;
import org.simpleframework.xml.stream.OutputNode;

public class ByteArrayConverter implements Converter<byte[]>
{

    @Override
    public byte[] read(InputNode node) throws Exception 
    {
        String value = node.getValue();
        return Base64.decode(value);
    }

    @Override
    public void write(OutputNode node, byte[] byteArray) throws Exception 
    {       
        node.setValue(Base64.encode(byteArray, false));     
    }



}
Sign up to request clarification or add additional context in comments.

1 Comment

Found the problem in above code. I think you can't write Converters for primitive types and hence I wrote a wrapper around byte[] and it worked. See below the solution.
1

Yay... I got it working. phew.. So I wrote this ByteArrayConverter in simple xml. I created a wrapper class around byte[] called ByteArrayWrapper and replace Converter with Converter

public class ByteArrayConverter implements Converter<ByteArrayWrapper>
{
    @Override
    public ByteArrayWrapper read(InputNode node) throws Exception 
    {
        InputNode nextnode = node.getNext();
        return new ByteArrayWrapper(Base64.decode(nextnode.getValue()));
    }

    public void write(OutputNode node, ByteArrayWrapper byteArray) throws Exception 
    {       
        OutputNode byteArrayNode = node.getChild("byteArray");
        byteArrayNode.setValue(Base64.encode(byteArray.getByteArray(), false));     
    }

}

Here is my simple ByteArrayWrapper class

@Root
public class ByteArrayWrapper
{
    @Element
    protected byte[] byteArray;

    public ByteArrayWrapper()
    {
        super();
    }

    getters..
        setters..
}

Here is how the Device class looks

@Root
public class Device implements Serializable
{
    private final static long serialVersionUID = 1L;
    @Element
    @Convert(ByteArrayConverter.class)
    private ByteArrayWrapper imageRef;

    public Device() {
        super();
    }

    public ByteArrayWrapper getImageRef() {
        return imageRef;
    }

    public void setImageRefByteArrayWrapper imageRef) {
        this.imageRef = imageRef;
    }   
}

and finally the RestTemplate client

public void testPost() 
 {

      RestTemplate restTemplate = new RestTemplate();          
      Strategy strategy = new AnnotationStrategy();
      Serializer serializer = new Persister(strategy);
      restTemplate.getMessageConverters().add(new     SimpleXmlHttpMessageConverter(serializer));
       restTemplate.getMessageConverters().add(new StringHttpMessageConverter());


       Device device = new Device();
       device.setImageRef(new byte[]{1,2,3,4,5});
       String response =  restTemplate.postForObject("http://10.0.0.3:8080/rest/mydata/testpost", device, String.class);
        assertNotNull(response);
 }

Everything is working great! Thanks @Blaise Doughan for your pointer.

Comments

0

A JAXB implementation will expect a byte[] to be represented as base64Binary in the XML. Based on your comment Simple XML has the following representation for byte[].

<imageRef length="5">1, 2, 3, 4, 5</imageRef>

You will need to choose whether you want to use the base64Binary representation from JAXB, or the proprietary representation from Simple XML and use an adapter/converter for the other side to make it understand the representation you choose.

5 Comments

Yeah I also tried to encode the byte[] with Base64 in Android but JaxB still couldn't get it right. I think it is because as you said Simple XML is converting it again behind the scene to its own representation. How can I solve this? My real goal is to send an image (~64 kb) within this object. I mean the other way is to just use a simple String with the whole image encoded as base64 but not sure if that would be an efficient solution..
@lazyguy - Have you tried marshalling the object with the byte array popluated with SimpleXML to see what its doing?
yes I tried that as well and see this <device> <imageRef length="5">1, 2, 3, 4, 5</imageRef> </device> Does this ring any bell???
@lazyguy - You will need to decide which representation you want for the byte[] JAXB's base64Binary representation or Simple's proprietary representation and then write a converter for the other side.
Thanks for the pointer @Blaise Doughan. I wrote a custom converter on Simple XML side. While the converter's write() method is called while posting the object and I do get correct bytes on server side. However while reading the response Simple XML never calls my converter and fails way before it. So now I am going to try writing a converter on JAXB side... Getting there.. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.