Serialize XML for REST API with Correct Accent

Asked

Viewed 1,018 times

5

I am creating a small read-only REST API service on a client system. It uses Spring MVC to fulfill the requests and the purpose of each request is to return a JSON with certain information to another system I’m writing.

To generate JSON, I am using JAXB + Jettison. The creation and return of the object is more or less as follows:

package com.empresa.projeto.financeiro;

import java.io.OutputStreamWriter;
import java.io.Writer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.stream.XMLStreamWriter;

import org.codehaus.jettison.mapped.Configuration;
import org.codehaus.jettison.mapped.MappedNamespaceConvention;
import org.codehaus.jettison.mapped.MappedXMLStreamWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.empresa.projeto.domain.Lancamento;
import com.empresa.projeto.financeiro.marshalling.JSONLancamento;
import com.empresa.projeto.financeiro.marshalling.JSONLancamentos;
import com.empresa.projeto.service.LancamentoService;

@Controller
public class Lancamentos {
    @Autowired private LancamentoService lancamentoService;

    @RequestMapping(value = "/lancamentos/teste", method = RequestMethod.GET)
    public void getLancamentos(HttpServletRequest req, HttpServletResponse resp)
        throws Exception {
        SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
        Calendar dataInicial = Calendar.getInstance();

        try {
            dataInicial.setTime(df.parse(req.getParameter("data_inicial")));
        } catch (ParseException ex) {
            ex.printStackTrace();   
        }

        Calendar dataFinal = Calendar.getInstance();

        try {
            dataFinal.setTime(df.parse(req.getParameter("data_final")));
        } catch (ParseException ex) {
            ex.printStackTrace();   
        }

        ServletOutputStream out = resp.getOutputStream();

        JAXBContext jbc = JAXBContext.newInstance(JSONLancamentos.class);
        Marshaller marshaller = jbc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        Configuration config = new Configuration();
        MappedNamespaceConvention con = new MappedNamespaceConvention(config);
        Writer writer = new OutputStreamWriter(out);
        XMLStreamWriter xmlStreamWriter = new MappedXMLStreamWriter(con, writer);

        JSONLancamentos lancamentos = new JSONLancamentos();

        List<Lancamento> lancamentosOriginais = lancamentoService.selecionarLancamentos(dataInicial, dataFinal);

        for (Lancamento lancOriginal: lancamentosOriginais) {
            JSONLancamento lancamento = new JSONLancamento();

            lancamento.setCategoria(lancOriginal.getCategoria());
            lancamento.setData(lancOriginal.getData());
            lancamento.setDias(lancOriginal.getDias());
            lancamento.setEmpresa(lancOriginal.getEmpresa().getNome());
            lancamento.setId(lancOriginal.getId());
            lancamento.setMotivo(lancOriginal.getMotivo());
            lancamento.setServico(lancOriginal.getServico());
            lancamento.setStatus(lancOriginal.getStatus());
            lancamento.setTipo(lancOriginal.getTipo());
            lancamento.setValor(lancOriginal.getValor());

            lancamentos.getLancamentos().add(lancamento);
        }

        marshaller.marshal(lancamentos, xmlStreamWriter);
    }
}

The classes describing serialization are below:

Jsonlancamentos.java

package com.empresa.projeto.financeiro.marshalling;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement (name="listagemlancamentos")
public class JSONLancamentos {

    private List<JSONLancamento> lancamentos;

    @XmlElement(name="lancamentos")
    public List<JSONLancamento> getLancamentos() {
        if (lancamentos == null) {
            lancamentos = new ArrayList<JSONLancamento>();
        }

        return lancamentos;
    }

    public void setLancamentos(List<JSONLancamento> lancamentos) {
        this.lancamentos = lancamentos;
    }
}

Jsonlancamento.java

package com.empresa.projeto.financeiro.marshalling;

import java.math.BigDecimal;
import java.util.Date;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import com.empresa.projeto.domain.Lancamento;

@XmlRootElement(name="lancamento")
public class JSONLancamento {
    private Long id;
    private char status;
    private Date data;
    private BigDecimal valor;
    private String motivo;
    private String empresa;
    private String localidade;
    private char servico;
    private char tipo;
    private char categoria;
    private boolean faturar = true;
    private int dias = 0;

    private String categoriaExtenso;

    @XmlElement
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @XmlElement
    public char getStatus() {
        return status;
    }

    public void setStatus(char status) {
        this.status = status;
    }

    ...
}

The problem is that the properties that return String are being serialized with the wrong accentuation. Characters like "ã", "ó" and "é" do not appear correctly in the result.

Is there any configuration that should be done for this result to be returned with the correct encoding?

The sources are all in UTF-8.

3 answers

2

Man I’ve seen trouble like yours wearing Jaxb about accentuation, what I indicated to person was to pass the bytes and check which charset was being used before, and look who was in the hand UTF-8, before parsing the xml.

I passed this useful class on to the person. And it worked.

public class CharsetUtil {

    public static Charset detectCharset(File file) {

        String[] charsets = { "UTF-8", "ISO-8859-1"};

        for (String charsetName : charsets) {

            if (detectCharset(file, Charset.forName(charsetName))) {
                return Charset.forName(charsetName);
            }
        }

        return null;
    }

    private static boolean detectCharset(File file, Charset charset) {

        try (BufferedInputStream input = new BufferedInputStream(new FileInputStream(file))) {

            CharsetDecoder decoder = charset.newDecoder();
            decoder.reset();

            byte[] buffer = new byte[512];
            boolean identified = false;

            while ((input.read(buffer) != -1) && (!identified)) {
                identified = identify(buffer, decoder);
            }

            return identified;

        } catch (Exception e) {

            return false;
        }
    }

    private static boolean identify(byte[] bytes, CharsetDecoder decoder) {

        try {
            decoder.decode(ByteBuffer.wrap(bytes));
        } catch (CharacterCodingException e) {
            return false;
        }
        return true;
    }


}
  • I’ll test later, but note that I don’t exactly have a file: I have a XMLStreamWriter who go through a Marshaller. What do I need to do to achieve this conversion using XMLStreamWriter?

  • I posted from example the code, as said I didn’t work with it properly, only unmarshall, but grab the bytes.

1


To work, I had to change the serializer. Jettison didn’t work at all. I switched to Jackson and it worked:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/* Outros imports relativos ao projeto em si */

@RequestMapping(value = "/rest/lancamentos/teste2", method = RequestMethod.GET, produces = { "application/json; charset=UTF-8" })
public void getLancamentos2(HttpServletRequest req, HttpServletResponse resp)
        throws Exception {
    SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
    Calendar dataInicial = Calendar.getInstance();

    try {
        dataInicial.setTime(df.parse(req.getParameter("data_inicial")));
    } catch (ParseException ex) {
        ex.printStackTrace();
    }

    Calendar dataFinal = Calendar.getInstance();

    try {
        dataFinal.setTime(df.parse(req.getParameter("data_final")));
    } catch (ParseException ex) {
        ex.printStackTrace();
    }

    ObjectMapper mapper = new ObjectMapper();

    ServletOutputStream out = resp.getOutputStream();

    JSONLancamentos lancamentos = new JSONLancamentos();

    List<Lancamento> lancamentosOriginais = lancamentoService
            .selecionarLancamentos(dataInicial, dataFinal);

    for (Lancamento lancOriginal : lancamentosOriginais) {
        JSONLancamento lancamento = new JSONLancamento();

        lancamento.setCategoria(lancOriginal.getCategoria());
        lancamento.setData(lancOriginal.getData());
        lancamento.setDias(lancOriginal.getDias());
        lancamento.setEmpresa(lancOriginal.getEmpresa().getNome());
        lancamento.setId(lancOriginal.getId());
        lancamento.setMotivo(lancOriginal.getMotivo());
        lancamento.setServico(lancOriginal.getServico());
        lancamento.setStatus(lancOriginal.getStatus());
        lancamento.setTipo(lancOriginal.getTipo());
        lancamento.setValor(lancOriginal.getValor());
        lancamento.setEmpresaId(lancOriginal.getEmpresa().getId());

        lancamentos.getLancamentos().add(lancamento);
    }

    mapper.setSerializationInclusion(Inclusion.ALWAYS);
    mapper.writeValue(out, lancamentos);
}

pom.xml

    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-mapper-asl</artifactId>
        <version>1.9.13</version>
    </dependency>

1

If you will return JSON at endpoint /lancings/test (although it is void), there is an @Requestmapping attribute which is "produces":

@RequestMapping(value = "/lancamentos/teste", method=RequestMethod.GET, produces={"application/json; charset=UTF-8"})
  • I ran the tests here with the produces and the result unfortunately was the same.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.