web-dev-qa-db-ja.com

Jersey + Jackson JSON日付形式のシリアル化-形式を変更する方法またはカスタムJacksonJsonProviderを使用する方法

私は、Jersey + Jacksonを使用して、アプリケーションにREST JSONサービスレイヤーを提供しています。私が抱えている問題は、デフォルトの日付シリアル化形式が次のようになることです。

"CreationDate":1292236718456

最初はUNIXタイムスタンプだと思っていましたが、それでは長すぎます。私のクライアント側のJSライブラリには、この形式の逆シリアル化に問題があります(これは、さまざまな日付形式をサポートしていますが、これはサポートしていません)。ライブラリーで使用できるようにフォーマットを変更したい(たとえば、ISOに)。どうすれば...役立つコードを見つけましたが... Jacksonシリアライザーのインスタンス化を制御しないので、どこに配置しますか(Jerseyはそうしますか)。

objectMapper.configure(
    SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);

カスタムJacksonJsonProviderのこのコードも見つかりました。質問は、..すべてのPOJOクラスでそれを使用するにはどうすればよいですか?

@Provider
public class MessageBodyWriterJSON extends JacksonJsonProvider {

    private static final String DF = "yyyy-MM-dd’T'HH:mm:ss.SSSZ";

    @Override
    public boolean isWriteable(Class arg0, Type arg1, Annotation[] arg2,
            MediaType arg3) {
        return super.isWriteable(arg0, arg1, arg2,
                arg3);
    }
    @Override
    public void writeTo(Object target, Class arg1, Type arg2, Annotation[] arg3,
            MediaType arg4, MultivaluedMap arg5, OutputStream outputStream)
            throws IOException, WebApplicationException {
            SimpleDateFormat sdf=new SimpleDateFormat(DF);

        ObjectMapper om = new ObjectMapper();
        om.getDeserializationConfig().setDateFormat(sdf);
        om.getSerializationConfig().setDateFormat(sdf);
        try {
            om.writeValue(outputStream, target);
        } catch (JsonGenerationException e) {
            throw e;
        } catch (JsonMappingException e) {
            throw e;
        } catch (IOException e) {
            throw e;
        }
    }
}
37
adrin

価値のあるものとして、その数は標準ですJavaタイムスタンプ(JDKクラスで使用); Unixは秒を格納します、Javaミリ秒、それが少し大きい値である理由です。

ObjectMapperをJerseyに注入する方法に関するドキュメントがあることを願っています(提供されたオブジェクトを注入する通常の方法に従う必要があります)。ただし、代わりに、JacksonJaxRsProviderをオーバーライドしてObjectMapperを指定/構成し、登録することもできます。これは、ジャージー自体が行うことであり、それを行う方法は複数あります。

6
StaxMan

私はResteasyの「JAX-RS方式」でそれを行うことができたので、Jerseyのようなすべての準拠実装で動作するはずです(最近JEE7サーバーWildfly 8で正常にテストされました。 API)。

ContextResolverを定義する必要があります(Producesに正しいコンテンツタイプが含まれていることを確認してください)。

import javax.ws.rs.ext.ContextResolver;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.DeserializationConfig;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.Produces; 
import Java.text.SimpleDateFormat;
@Provider
@Produces("application/json")
public class JacksonConfigurator implements ContextResolver<ObjectMapper> {

    private ObjectMapper mapper = new ObjectMapper();

    public JacksonConfigurator() {
        SerializationConfig serConfig = mapper.getSerializationConfig();
        serConfig.setDateFormat(new SimpleDateFormat(<my format>));
        DeserializationConfig deserializationConfig = mapper.getDeserializationConfig();
        deserializationConfig.setDateFormat(new SimpleDateFormat(<my format>));
        mapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
    }

    @Override
    public ObjectMapper getContext(Class<?> arg0) {
        return mapper;
    }

}

次に、javax.ws.rs.core.ApplicationのgetClassesで新しく作成されたクラスを返す必要があります

import javax.ws.rs.core.Application;
public class RestApplication extends Application {

     @Override
     public Set<Class<?>> getClasses() {
         Set<Class<?>> classes = new HashSet<Class<?>>();
         // your classes here
         classes.add(JacksonConfigurator.class);
         return classes;
      }

}

このように、jacksonを介して行われるすべての操作には、選択したObjectMapperが与えられます。

編集:最近、RestEasy 2.0.1(したがってJackson 1.5.3)を使用して、JacksonConfiguratorを拡張してカスタムマッピングを追加することにした場合、奇妙な動作があることを自分の費用で知りました。

import javax.ws.rs.core.MediaType;
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class MyJacksonConfigurator extends JacksonConfigurator

このようにするだけで(そしてもちろんRestApplicationに拡張クラスを置くと)、親クラスのマッパーが使用されます。つまり、カスタムマッピングが失われます。それを正しく動作させるために、私はそれ以外の場合は役に立たないと思われる何かをしなければなりませんでした:

public class MyJacksonConfigurator extends JacksonConfigurator implements ContextResolver<ObjectMapper> 
32
Riccardo Cossu

独自のObjectMapperを構成するには、ContextResolver <ObjectMapper>を実装する独自のクラスを注入する必要があります

ジャージにこれを拾わせる正確な方法は、IOC(spring、guice)に依存します。私はspringを使用し、クラスは次のようになります。

import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig.Feature;
import org.codehaus.jackson.map.deser.CustomDeserializerFactory;
import org.codehaus.jackson.map.deser.StdDeserializerProvider;
import org.codehaus.jackson.map.ser.CustomSerializerFactory;
import org.springframework.stereotype.Component;

// tell spring to look for this.
@Component
// tell spring it's a provider (type is determined by the implements)
@Provider
public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
    @Override
    public ObjectMapper getContext(Class<?> type) {
        // create the objectMapper.
        ObjectMapper objectMapper = new ObjectMapper();
        // configure the object mapper here, eg.
           objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
        return objectMapper;
    }
}
16
Mark

以下のコードは私のために働いた-JAX-RS 1.1、Jersy 1.8

import Java.text.SimpleDateFormat;

import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;

import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;


@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JsonProvider extends JacksonJaxbJsonProvider {
  private static final ObjectMapper objectMapper = new ObjectMapper();
  static {
    // allow only non-null fields to be serialized
    objectMapper.getSerializationConfig().setSerializationInclusion(Inclusion.NON_NULL);

    SerializationConfig serConfig = objectMapper.getSerializationConfig();
    serConfig.setDateFormat(new SimpleDateFormat(<your date format>));
    DeserializationConfig deserializationConfig = objectMapper.getDeserializationConfig();
    deserializationConfig.setDateFormat(new SimpleDateFormat(<your date format>));
    objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);

  }

  public JsonProvider() {
    super.setMapper(objectMapper);
  }
}
1

同じ問題が発生しました(Jersey + Jackson + Jsonを使用)。クライアントは日付を送信していましたが、データがオブジェクトにマップされたときにサーバーで変更されていました。

このリンクを読んで、これを解決するために他のアプローチに従いました: http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html 、その日付に気付いたとき受け取ったのはタイムスタンプ(彼の質問のアドリンと同じ:"creationDate":1292236718456

VOクラスでは、この注釈を属性@XmlJavaTypeAdapterに追加し、XmlAdapterを拡張した内部クラスも実装しました。

@XmlRootElement
public class MyClassVO {
   ...
   @XmlJavaTypeAdapter(DateFormatterAdapter.class) 
   Date creationDate;
   ...

   private static class DateFormatterAdapter extends XmlAdapter<String, Date> {
      @Override
      public Date unmarshal(final String v) throws Exception {
         Timestamp stamp = new Timestamp(new Long(v));
         Date date = new Date(stamp.getTime());
         return date;
      }
}

それがあなたにも役立つことを願っています。

1
Ignacio Rubio

サーバーでJoda DateTimeオブジェクトを使用することを選択し、ISO8601にシリアル化する場合は、 JacksonのJodaModule を使用できます。次のようにJerseyプロバイダーを登録できます。

import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.joda.JodaModule;

@Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {

  final ObjectMapper objectMapper;

  public MyObjectMapperProvider() {
    objectMapper = new ObjectMapper();
    /* Register JodaModule to handle Joda DateTime Objects. */
    objectMapper.registerModule(new JodaModule());
    /* We want dates to be treated as ISO8601 not timestamps. */
    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
  }

  @Override
  public ObjectMapper getContext(Class<?> arg0) {
    return objectMapper;
  }
}

詳細情報は JerseyのWebサイト で入手できます。

1
lorcan