好的,最后我成功地建立了一个完全符合我要求的机制。但是,我不确定是否有诸如性能或此类事情的负面后果。
首先,我定义了一个可以同时接受 List 或 Single Object 的类:
public class RootWrapper<T> {
private List<T> list;
private T object;
}
然后,我需要一个自定义的反序列化器,它能够知道要反序列化哪种 T 类型并处理集合或单个对象。
public class RootWrapperDeserializer extends JsonDeserializer<CollectionWrapper<?>> {
private Class contentType;
public RootWrapperDeserializer(Class contentType) {
this.contentType = contentType;
}
@Override
public RootWrapper deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// Retrieve the object mapper and read the tree.
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode root = mapper.readTree(jp);
RootWrapper wrapper = new RootWrapper();
// Check if the root received is an array.
if (root.isArray()) {
List list = new LinkedList();
// Deserialize each node of the array using the type expected.
Iterator<JsonNode> rootIterator = root.getElements();
while (rootIterator.hasNext()) {
list.add(mapper.readValue(rootIterator.next(), contentType));
}
wrapper.setList(list);
}
// Deserialize the single object.
else {
wrapper.setObject(mapper.readValue(root, contentType));
}
return wrapper;
}
}
据我所知,我尝试只手动反序列化根级别,然后让杰克逊负责接下来的操作。我只需要知道我希望在 Wrapper 中出现的真实类型。
在这个阶段,我需要一种方法来告诉 Jersey/Jackson 使用哪个反序列化器。我发现的一种方法是创建一种反序列化器注册表,其中存储要使用正确的反序列化器反序列化的类型。我为此扩展了 Deserializers.Base 类。
public class CustomDeserializers extends Deserializers.Base {
// Deserializers caching
private Map<Class, RootWrapperDeserializer> deserializers = new HashMap<>();
@Override
public JsonDeserializer<?> findBeanDeserializer(JavaType type,
DeserializationConfig config, DeserializerProvider provider,
BeanDescription beanDesc, BeanProperty property) throws JsonMappingException {
// Check if we have to provide a deserializer
if (type.getRawClass() == RootWrapper.class) {
// Check the deserializer cache
if (deserializers.containsKey(type.getRawClass())) {
return deserializers.get(type.getRawClass());
}
else {
// Create the new deserializer and cache it.
RootWrapperDeserializer deserializer =
new RootWrapperDeserializer(type.containedType(0).getRawClass());
deserializers.put(type.getRawClass(), deserializer);
return deserializer;
}
}
return null;
}
}
好的,然后我有我的反序列化器注册表,它只根据需要创建新的反序列化器,并在创建后保留它们。我不确定这种方法是否存在任何并发问题。我知道杰克逊做了很多缓存,并且一旦 findBeanDeserializer 在特定的反序列化上下文中第一次被调用,就不会每次都调用它。
现在我已经创建了不同的类,我需要做一些管道来将所有东西组合在一起。在我创建 ObjectMapper 的提供程序中,我可以将反序列化器注册表设置为创建的对象映射器,如下所示:
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JsonObjectMapper implements ContextResolver<ObjectMapper> {
private ObjectMapper jacksonObjectMapper;
public JsonObjectMapper() {
jacksonObjectMapper = new ObjectMapper();
// Do some custom configuration...
// Configure a new deserializer registry
jacksonObjectMapper.setDeserializerProvider(
jacksonObjectMapper.getDeserializerProvider().withAdditionalDeserializers(
new RootArrayObjectDeserializers()
)
);
}
@Override
public ObjectMapper getContext(Class<?> arg0) {
return jacksonObjectMapper;
}
}
然后,我还可以定义我的 @ApplicationPath,它是我的 REST 应用程序,如下所示:
public abstract class AbstractRestApplication extends Application {
private Set<Class<?>> classes = new HashSet<>();
public AbstractRestApplication() {
classes.add(JacksonFeature.class);
classes.add(JsonObjectMapper.class);
addResources(classes);
}
@Override
public Set<Class<?>> getClasses() {
return classes;
}
@Override
public Set<Object> getSingletons() {
final Set<Object> singletons = new HashSet<>(1);
singletons.add(new JacksonJsonProvider());
return singletons;
}
private void addResources(Set<Class<?>> classes) {
classes.add(SomeRestResource.class);
// ...
}
}
现在,一切就绪,我可以编写这样的 REST 资源方法:
@POST
@Path("somePath")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response create(RootWrapper<SpecificClass> wrapper) {
if (wrapper.isObject()) {
// Do something for one single object
SpecificClass sc = wrapper.getObject();
// ...
return Response.ok(resultSingleObject).build();
}
else {
// Do something for list of objects
for (SpecificClass sc = wrapper.getList()) {
// ...
}
return Response.ok(resultList).build();
}
}
就这样。不要犹豫,评论解决方案。非常欢迎反馈,特别是关于反序列化过程的方式,我真的不确定它对性能和并发性是否安全。