как отправить массив байтов из простой формы xml на сервер REST

Я использую RestTemplate для клиента Android. Я использую аннотированный объект Simple XML и тот же java-объект с аннотацией JaxB на стороне сервера. Я успешно отправляю/получаю String и другие примитивные типы, но для массива байтов возникают проблемы. Массив байтов, который я отправляю из Simple XML, преобразуется во что-то другое, когда я вижу на стороне JaxB на сервере. Ниже приведен код..

Аннотированный объект JaxB на сервере REST

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;
    }
}

Вот как выглядит Rest-сервер. Для этого я использую Apache CXF.

<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" />

и контроллер

@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
    }
}

Вот как выглядит клиентская часть Android

Простой XML-аннотированный объект на 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 с конвертерами Simple XML

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);

 }

Таким образом, когда я отправляю массив байтов byte[]{1,2,3,4,5} на сервер, я получаю этот массив байтов [-41, 109, -8]. Я пытался декодировать с помощью Base64 на стороне сервера, думая, что это может быть простой xml, но не мог понять, что происходит?

Точно так же, если я выполняю операцию Get вместо Post, я получаю исключение NumberFormat в клиенте Android. в java.lang.Integer.invalidInt(Integer.java:138) в java.lang.Integer.parse(Integer.java:375) в java.lang.Integer.parseInt(Integer.java:366) в java.lang. Byte.parseByte(Byte.java:214) в java.lang.Byte.parseByte(Byte.java:195) в java.lang.Byte.valueOf(Byte.java:264) в org.simpleframework.xml.transform.ByteTransform .read(ByteTransform.java:55)

Любая помощь будет оценена.


person lazyguy    schedule 09.07.2013    source источник


Ответы (3)


Реализация JAXB ожидает, что byte[] будет представлено как base64Binary в XML. На основании вашего комментария Simple XML имеет следующее представление для byte[].

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

Вам нужно будет выбрать, хотите ли вы использовать представление base64Binary из JAXB или проприетарное представление из Simple XML, и использовать адаптер/преобразователь для другой стороны, чтобы он понял выбранное вами представление.

person bdoughan    schedule 09.07.2013
comment
Да, я также пытался закодировать байт [] с помощью Base64 в Android, но JaxB все еще не мог сделать это правильно. Я думаю, это потому, что, как вы сказали, Simple XML снова преобразует его за кулисами в свое собственное представление. Как я могу это решить? Моя настоящая цель - отправить изображение (~ 64 КБ) внутри этого объекта. Я имею в виду, что другой способ - просто использовать простую строку со всем изображением, закодированным как base64, но не уверен, что это будет эффективным решением. - person lazyguy; 09.07.2013
comment
@lazyguy - Вы пытались упорядочить объект с помощью массива байтов, заполненного SimpleXML, чтобы увидеть, что он делает? - person bdoughan; 09.07.2013
comment
да, я тоже пробовал это и вижу это ‹device› ‹imageRef length=5›1, 2, 3, 4, 5‹/imageRef› ‹/device› Это звонит в колокол??? - person lazyguy; 09.07.2013
comment
@lazyguy - вам нужно будет решить, какое представление вы хотите для представления byte[] JAXB base64Binary или проприетарного представления Simple, а затем написать преобразователь для другой стороны. - person bdoughan; 09.07.2013
comment
Спасибо за указатель @Blaise Doughan. Я написал собственный конвертер на стороне Simple XML. В то время как метод write() конвертера вызывается при публикации объекта, и я получаю правильные байты на стороне сервера. Однако при чтении ответа Simple XML никогда не вызывает мой преобразователь и выходит из строя задолго до него. Итак, теперь я собираюсь попробовать написать конвертер на стороне JAXB... Как добраться.. :) - person lazyguy; 09.07.2013

Я последовал совету @Blaise Doughan написать собственный конвертер. Я написал один на стороне Simple XML. Однако он работает только для отправки данных на сервер, но не для получения. Вот как выглядит преобразователь. Я думаю, что это может быть проблема простого XML, и поэтому ответ на этот пост заключается в том, что нам нужно написать собственный конвертер. Я опубликовал дополнительный вопрос для парней из Simple XML, чтобы понять, почему метод чтения не вызывается, когда мы сериализуем xml обратно в объект? См. вопрос здесь пользовательский конвертер для простого xml не работает должным образом < /а>

Я поставил точку останова в методе чтения и похоже, что он никогда не вызывался. Простой синтаксический анализатор XML дает сбой задолго до этого исключения org.springframework.http.converter.HttpMessageNotReadableException: Не удалось прочитать [класс cs.core.mobile.database.JaxBResponse]; вложенным исключением является 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));     
    }



}
person lazyguy    schedule 10.07.2013
comment
Нашел проблему в приведенном выше коде. Я думаю, что вы не можете писать преобразователи для примитивных типов, поэтому я написал обертку вокруг byte[], и это сработало. См. ниже решение. - person lazyguy; 11.07.2013

Ура... У меня все работает. уф.. Итак, я написал этот ByteArrayConverter в простом xml. Я создал класс-оболочку вокруг byte[] с именем ByteArrayWrapper и заменил Converter на 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));     
    }

}

Вот мой простой класс ByteArrayWrapper

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

    public ByteArrayWrapper()
    {
        super();
    }

    getters..
        setters..
}

Вот как выглядит класс Device

@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;
    }   
}

и, наконец, клиент RestTemplate

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);
 }

Все работает отлично! Спасибо @Blaise Doughan за ваш указатель.

person lazyguy    schedule 11.07.2013