Hatred's Log Place

DON'T PANIC!

Mar 21, 2012 - 2 minute read - programming

RESTEasy, JAXB, XML, JSON и другие

Столкнулся с непонятной проблемой в JBoss и RESTEasy:

Когда сервис принимает или отдаёт данные в JSON формате Jackson (сериализатор/десериализатор JSON) игнорирует JAXB аннотации @XmlElement(name = "bla_bla") вместо указанного имя поля всегда используется имя поля в классе, т.е. такое:

@XmlRootElement
class SimpleJson
{
  @XmlElement(name = "my_name")
  public String megaName;
}

сериализуется в это:

{
  "megaName" : ""
}

а не, как ожидается, в это:

{
  "my_name" : ""
}

А так же игнорируется @XmlJavaTypeAdapter, что есть пичалька.

Тут по ходу сочинения заметки пришло в голову, что Джексон не настроен использовать JaxbAnnotationIntrospector - повод рассмотреть.

Пока же использую work-around, в виде дополнительного навешивания Jackson-аннотаций вроде:

@XmlRootElement
class SimpleJson
{
  @XmlElement(name = "my_name")
  @XmlJavaTypeAdapter(Iso8601DateAdapter.class)
  @JsonProperty(value = "my_name")
  @JsonSerialize(using = JsonIso8601DateSeializer.class)
  @JsonDeserialize(using = JsonIso8601DateDeserializer.class)
  public Date megaName;
}

Подкатом, бонусом, классы Iso8601DateAdapter, JsonIso8601DateSeializer, JsonIso8601DateDeserializer.

package net.homelinux.hatred.ws.common.data;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;

import javax.xml.bind.annotation.adapters.XmlAdapter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Iso8601DateAdapter extends XmlAdapter<String, Date>
{
    // Logger
    private static final Logger log = LoggerFactory.getLogger(Iso8601DateAdapter.class);

    private final static DateFormat _df  = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
    
    @Override
    public String marshal(Date date)
    {
        String result = null;
        try
        {
            result = _df.format(date);
            // Add ':' to TZ
            result = result.substring(0, 22) + ":" + result.substring(22, result.length());

            log.info("Convert date to string: {}", result);
        }
        catch (Exception e)
        {
            log.error("Could not date to string '{}'", date.toString());
        }
        return result;
    }

    @Override
    public Date unmarshal(String string)
    {
        Date result = null;
        try 
        {
            // Strip ':' from TZ
            string = string.substring(0, 22) + string.substring(23, string.length());
            result = _df.parse(string);

            log.info("Convert string to date: {}", result);
        } 
        catch (Exception e) 
        {
            log.error(String.format("Could not parse date string '%s'", string));
        }
        return result;
    }
}
package net.homelinux.hatred.ws.common.data;

import java.io.IOException;
import java.util.Date;

import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;

/**
 * Jackson ISO8601 date serializer
 *
 * @author Alexander 'hatred' Drozdov
 *         <p/>
 *         Date: 20.03.12
 *         Time: 15:15
 */
public class JsonIso8601DateSeializer extends JsonSerializer<Date>
{
    // Reuse exists adapter for JaxB
    private static final Iso8601DateAdapter adapter = new Iso8601DateAdapter();

    @Override
    public void serialize(Date date,
                          JsonGenerator jsonGenerator,
                          SerializerProvider serializerProvider)
            throws IOException, JsonProcessingException
    {
        String serialized = adapter.marshal(date);
        jsonGenerator.writeString(serialized);
    }
}
package net.homelinux.hatred.ws.common.data;

import java.io.IOException;
import java.util.Date;

import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;

/**
 * Jackson ISO8601 date deserializer
 *
 * @author Alexander 'hatred' Drozdov
 *         <p/>
 *         Date: 20.03.12
 *         Time: 15:21
 */
public class JsonIso8601DateDeserializer extends JsonDeserializer<Date>
{
    // Reuse exists adapter for JaxB
    private static final Iso8601DateAdapter adapter = new Iso8601DateAdapter();

    @Override
    public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
            throws IOException, JsonProcessingException
    {
        String dateString = jsonParser.getText();
        Date deserialized = adapter.unmarshal(dateString);
        return deserialized;
    }
}