2015-11-25 2 views
2

(Jackson 2.6.1, Джерси 2.21.0)Повторное использование Джексон ObjectMapper и JsonFactory экземпляры

Мой веб-приложение использует JAX-RS интерфейсы и Джексон JSon вспомогательные классы. Я не напрямую импортирую классы Джерси, планирую сохранить его в отношении к моему приложению.

Я хочу повторно использовать экземпляры ObjectMapper и JsonFactory, поскольку рекомендации Jackson рекомендуют, но не могут получить ссылку на них внутри класса MyBeanService.

// resolver returns null 
ContextResolver<ObjectMapper> resolver = providers.getContextResolver(ObjectMapper.class, MediaType.APPLICATION_JSON_TYPE); 
ContextResolver<JacksonJaxbJsonProvider> resolverB = providers.getContextResolver(JacksonJaxbJsonProvider.class, MediaType.WILDCARD_TYPE); 
// returns org.glassfish.jersey.server.ResourceConfig$WrappingResourceConfig wrapper 
// also I wish not to import directly any jersey classes 
ContextResolver<Application> resolverC = providers.getContextResolver(Application.class, MediaType.WILDCARD_TYPE); 

// resolver was null so getters don't work 
ObjectMapper mapper = resolver.getContext(ObjectMapper.class); 
JsonGenerator jsonG = mapper.getFactory().createGenerator(os, JsonEncoding.UTF8); 

Должен ли я просто предоставить функцию public static ObjectMapper MyApplication.getObjectMapper()? MyApplication установит личное статическое поле objectmapper в методе getSingletons()?

Это моя конфигурация и классы, чтобы обеспечить путь сервлета restjson.

mywebapp/WEB-INF/web.xml

экземпляра приложения приведены в файле web.xml.

... 
<servlet><servlet-name>myapp.rest.MyApplication</servlet-name></servlet> 
<servlet-mapping> 
    <servlet-name>myapp.rest.MyApplication</servlet-name> 
    <url-pattern>/rest/*</url-pattern> 
</servlet-mapping> 

MyApplication.java

Установить остальные услуги и настройки ObjectMapper экземпляра, такие как формат даты и времени поля.

package myapp.rest; 

import java.util.*; 
import java.text.*; 
import javax.ws.rs.core.Application; 
import com.fasterxml.jackson.databind.ObjectMapper; 
import com.fasterxml.jackson.databind.SerializationFeature; 
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; 
import com.fasterxml.jackson.jaxrs.cfg.Annotations; 

public class MyApplication extends Application { 

    @Override public Set<Class<?>> getClasses() { 
     Set<Class<?>> list = new HashSet<Class<?>>(); 
     list.add(MyBeanService.class); 
     return list; 
    } 

    @Override public Set<Object> getSingletons() { 
     Set<Object> list = new HashSet<Object>(); 

     ObjectMapper mapper = new ObjectMapper();  
     mapper.disable(SerializationFeature.INDENT_OUTPUT); 
     //mapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // enable=1433435279692 (utcMillis) 
     //mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // disable=2015-06-04T16:25:27.056+0000 (ISO8601_utc) 
     DateFormat dtf = SimpleDateFormat.getDateTimeInstance(); 
     ((SimpleDateFormat)dtf).applyPattern("yyyy-MM-dd'T'HH:mm:ssZ"); // 2015-06-04T19:25:27+0300 (custom+tzOffset) 
     mapper.setDateFormat(dtf); 
     JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider(
       mapper, 
       new Annotations[] { Annotations.JACKSON, Annotations.JAXB }); 
     list.add(provider);  
     return list; 
    } 

} 

MyBeanService.java

Это сервис обработки класса путей остальные URL.

import java.io.*; 
import java.util.*; 
import javax.inject.Singleton; 
import javax.servlet.http.HttpServletRequest; 
import javax.ws.rs.*; 
import javax.ws.rs.core.StreamingOutput; 
import javax.ws.rs.core.*; 
import javax.ws.rs.ext.*; 

@Path("") @Singleton 
public class MyBeanService { 
    //@Context private Providers providers; 
    //@Context private Application application; 

    @GET @Path("/{serverName}/beans/{id}") 
    @Produces({"application/json;charset=UTF-8"}) 
    public Response getBean (
      @Context HttpServletRequest req, 
      @PathParam("serverName") String serverName, 
      @PathParam("id") long id) { 

     final MyServer server= JPADB.getServer(serverName); 
     final MyBean bean = JPADB.getBean(server, id); 

     StreamingOutput stream = new StreamingOutput() { 
      @Override public void write(OutputStream os) throws IOException, WebApplicationException { 
       //*** I want to reuse JsonFactory and ObjectMapper, but 
       // currently create new factory each time 
       JsonFactory jsonF = new JsonFactory(); 
       JsonGenerator jsonG = jsonF.createGenerator(os, JsonEncoding.UTF8); 
       try { 
       jsonG.writeStartObject(); 
       jsonG.writeNumberField("id", bean.getId()); 
       jsonG.writeStringField("title", bean.getTitle()); 
       jsonG.writeStringField("serverId", server.getId()); 
       jsonG.writeStringField("serverName", server.getName()); 
       ..various json fields, some optional, some at runtime autogen.. 
       jsonG.writeEndObject(); 
       jsonG.flush(); 
       } finally { 
       jsonG.close(); 
       } 
      } 
     }; 

     CacheControl cc = new CacheControl(); 
     cc.setNoCache(true); 
     return Response.ok().type("application/json;charset=UTF-8") 
       .cacheControl(cc).entity(stream).build(); 
    } 
} 

ответ

4

ContextResolver равна нулю, потому что вы на самом деле не имеют в ContextResolver. Это то, что вам нужно написать.

@Provider 
@Produces("application/json") 
@Consumes("application/json") 
public class MapperContextResolver implements ContextResolver<ObjectMapper> { 

    private final ObjectMapper mapper; 

    public MapperContextResolver() { 
     mapper = new ObjectMapper(); 
     // do any configurations to mapper 
    } 

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

Затем просто зарегистрируйте его в приложении. Как работает Jackson(Jaxb)JsonProvider, так это то, что сначала он ищет ObjectMapper, который был передан конструктору. Если не найдено, то он ищет ContextResolver для ObjectMapper. Если не найден, он просто будет использовать свои собственные ObjectWriter и ObjectReader.

Так что вам не нужно создавать JacksonJaxbJsonProvider с ObjectMapper. Просто используйте default constructor, и поставщик будет искать ваш распознаватель.

+0

Совместное использование Singleton для ObjectMapper, созданного ContextResolver, отлично работает. Все эти Resolver и whatnot интерфейсы иногда бывают слишком многого, чтобы понять на практике, читая javadocs. – Whome

+0

Привет, пожалуйста, посмотрите на этот вопрос (http://stackoverflow.com/questions/36546087/jacksonjaxbjsonprovider-default-objectmapper-mapping). Я хотел бы использовать «JacksonJaxbJsonProvider» для использования ObjectMapper по умолчанию для некоторых моделей. как это возможно – ulab

Смежные вопросы