Serialize XML to REST API with Correct Accentuation

5

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

To generate JSON, I'm 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 that describe serialization are below:

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

JSONLanking.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 properties that return String are being serialized with the wrong accent. Characters like "ã", "ó" and "é" do not appear correctly in the result.

Is there a setting that needs to be made to return this result with correct encoding?

The fonts are all in UTF-8.

    
asked by anonymous 14.05.2014 / 09:20

3 answers

1

In order to work, I needed to change the serializer. Jettison did not work at all. I moved 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>
    
09.07.2014 / 09:09
2

Dude, I've seen problem similar to yours using Jaxb on accentuation, what I indicated to person was to pass the bytes and check which charset was being used before, and look that it was in the hand UTF-8 , before parsing to xml.

I passed this useful class 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;
    }


}
    
14.05.2014 / 10:17
1

If you are going to return the JSON at the endpoint / launch / test (although void), there is an @RequestMapping attribute that is "produces":

@RequestMapping(value = "/lancamentos/teste", method=RequestMethod.GET, produces={"application/json; charset=UTF-8"})
    
15.05.2014 / 02:18