2015-11-25 51 views
2

(Jackson 2.6.1,Jersey 2.21.0)Reuse Jackson ObjectMapper和JsonFactory實例

我的webapp使用JAX-RS接口和Jackson json helper類。我不直接導入澤西班,計劃保持它與我的應用無關。

我想像Jackson文檔推薦的那樣重用ObjectMapper和JsonFactory實例,但無法在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會在getSingletons()方法中設置私有靜態objectmapper字段?

這是我配置和提供restjson servlet路徑的類。

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爲null,因爲你實際上並不一個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。如果沒有找到,它將使用它自己的ObjectWriterObjectReader

因此,您不需要使用ObjectMapper創建JacksonJaxbJsonProvider。只需使用default constructor,供應商將尋找您的解析器。

+0

由ContextResolver支持的ObjectMapper共享單例使用現在可以完美工作。通過閱讀javadocs,所有那些Resolver和那些接口往往在實踐中往往太難掌握。 – Whome

+0

嗨,請看看這個[問題](http://stackoverflow.com/questions/36546087/jacksonjaxbjsonprovider-default-objectmapper-mapping)我想強制'JacksonJaxbJsonProvider'使用默認的ObjectMapper只爲一些模型。這怎麼可能 – ulab

相關問題