package com.fasterxml.jackson.jaxrs.json;
import java.lang.annotation.Annotation;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.jaxrs.base.ProviderBase;
import com.fasterxml.jackson.jaxrs.cfg.Annotations;
Basic implementation of JAX-RS abstractions (MessageBodyReader
, MessageBodyWriter
) needed for binding JSON ("application/json") content to and from Java Objects ("POJO"s). Actual data binding functionality is implemented by ObjectMapper
: mapper to use can be configured in multiple ways:
- By explicitly passing mapper to use in constructor
- By explictly setting mapper to use by
ProviderBase<JacksonJsonProvider,ObjectMapper,JsonEndpointConfig,JsonMapperConfigurator>.setMapper
- By defining JAX-RS
Provider
that returns ObjectMapper
s. - By doing none of above, in which case a default mapper instance is
constructed (and configured if configuration methods are called)
The last method ("do nothing specific") is often good enough; explicit passing
of Mapper is simple and explicit; and Provider-based method may make sense
with Depedency Injection frameworks, or if Mapper has to be configured differently
for different media types.
Note that the default mapper instance will be automatically created if one of explicit configuration methods (like ProviderBase<JacksonJsonProvider,ObjectMapper,JsonEndpointConfig,JsonMapperConfigurator>.configure
) is called: if so, Provider-based introspection is NOT used, but the
resulting Mapper is used as configured.
Note: version 1.3 added a sub-class (JacksonJaxbJsonProvider
) which is configured by default to use both Jackson and JAXB annotations for configuration (base class when used as-is defaults to using just Jackson annotations)
Author: Tatu Saloranta
/**
* Basic implementation of JAX-RS abstractions ({@link MessageBodyReader},
* {@link MessageBodyWriter}) needed for binding
* JSON ("application/json") content to and from Java Objects ("POJO"s).
*<p>
* Actual data binding functionality is implemented by {@link ObjectMapper}:
* mapper to use can be configured in multiple ways:
* <ul>
* <li>By explicitly passing mapper to use in constructor
* <li>By explictly setting mapper to use by {@link #setMapper}
* <li>By defining JAX-RS <code>Provider</code> that returns {@link ObjectMapper}s.
* <li>By doing none of above, in which case a default mapper instance is
* constructed (and configured if configuration methods are called)
* </ul>
* The last method ("do nothing specific") is often good enough; explicit passing
* of Mapper is simple and explicit; and Provider-based method may make sense
* with Depedency Injection frameworks, or if Mapper has to be configured differently
* for different media types.
*<p>
* Note that the default mapper instance will be automatically created if
* one of explicit configuration methods (like {@link #configure})
* is called: if so, Provider-based introspection is <b>NOT</b> used, but the
* resulting Mapper is used as configured.
*<p>
* Note: version 1.3 added a sub-class ({@link JacksonJaxbJsonProvider}) which
* is configured by default to use both Jackson and JAXB annotations for configuration
* (base class when used as-is defaults to using just Jackson annotations)
*
* @author Tatu Saloranta
*/
@Provider
@Consumes(MediaType.WILDCARD) // NOTE: required to support "non-standard" JSON variants
@Produces(MediaType.WILDCARD)
public class JacksonJsonProvider
extends ProviderBase<JacksonJsonProvider,
ObjectMapper,
JsonEndpointConfig, JsonMapperConfigurator>
{
public final static String MIME_JAVASCRIPT = "application/javascript";
public final static String MIME_JAVASCRIPT_MS = "application/x-javascript";
Default annotation sets to use, if not explicitly defined during
construction: only Jackson annotations are used for the base
class. Sub-classes can use other settings.
/**
* Default annotation sets to use, if not explicitly defined during
* construction: only Jackson annotations are used for the base
* class. Sub-classes can use other settings.
*/
public final static Annotations[] BASIC_ANNOTATIONS = {
Annotations.JACKSON
};
/*
/**********************************************************
/* General configuration
/**********************************************************
*/
JSONP function name to use for automatic JSONP wrapping, if any;
if null, no JSONP wrapping is done.
Note that this is the default value that can be overridden on
per-endpoint basis.
/**
* JSONP function name to use for automatic JSONP wrapping, if any;
* if null, no JSONP wrapping is done.
* Note that this is the default value that can be overridden on
* per-endpoint basis.
*/
protected String _jsonpFunctionName;
/*
/**********************************************************
/* Context configuration
/**********************************************************
*/
Injectable context object used to locate configured instance of ObjectMapper
to use for actual serialization. /**
* Injectable context object used to locate configured
* instance of {@link ObjectMapper} to use for actual
* serialization.
*/
@Context
protected Providers _providers;
/*
/**********************************************************
/* Construction
/**********************************************************
*/
Default constructor, usually used when provider is automatically
configured to be used with JAX-RS implementation.
/**
* Default constructor, usually used when provider is automatically
* configured to be used with JAX-RS implementation.
*/
public JacksonJsonProvider() {
this(null, BASIC_ANNOTATIONS);
}
Params: - annotationsToUse – Annotation set(s) to use for configuring
data binding
/**
* @param annotationsToUse Annotation set(s) to use for configuring
* data binding
*/
public JacksonJsonProvider(Annotations... annotationsToUse) {
this(null, annotationsToUse);
}
public JacksonJsonProvider(ObjectMapper mapper) {
this(mapper, BASIC_ANNOTATIONS);
}
Constructor to use when a custom mapper (usually components
like serializer/deserializer factories that have been configured)
is to be used.
Params: - annotationsToUse – Sets of annotations (Jackson, JAXB) that provider should
support
/**
* Constructor to use when a custom mapper (usually components
* like serializer/deserializer factories that have been configured)
* is to be used.
*
* @param annotationsToUse Sets of annotations (Jackson, JAXB) that provider should
* support
*/
public JacksonJsonProvider(ObjectMapper mapper, Annotations[] annotationsToUse) {
super(new JsonMapperConfigurator(mapper, annotationsToUse));
}
Method that will return version information stored in and read from jar
that contains this class.
/**
* Method that will return version information stored in and read from jar
* that contains this class.
*/
@Override
public Version version() {
return PackageVersion.VERSION;
}
/*
/**********************************************************
/* JSON-specific configuration
/**********************************************************
*/
public void setJSONPFunctionName(String fname) {
_jsonpFunctionName = fname;
}
/*
/**********************************************************
/* Abstract method impls
/**********************************************************
*/
Helper method used to check whether given media type is supported by this provider. Current implementation essentially checks to see whether getSubtype.getSubtype
returns "json" or something ending with "+json". Or "text/x-json" (since 2.3) Since: 2.2
/**
* Helper method used to check whether given media type
* is supported by this provider.
* Current implementation essentially checks to see whether
* {@link MediaType#getSubtype} returns "json" or something
* ending with "+json".
* Or "text/x-json" (since 2.3)
*
* @since 2.2
*/
@Override
protected boolean hasMatchingMediaType(MediaType mediaType)
{
/* As suggested by Stephen D, there are 2 ways to check: either
* being as inclusive as possible (if subtype is "json"), or
* exclusive (major type "application", minor type "json").
* Let's start with inclusive one, hard to know which major
* types we should cover aside from "application".
*/
if (mediaType != null) {
// Ok: there are also "xxx+json" subtypes, which count as well
String subtype = mediaType.getSubtype();
// [Issue#14]: also allow 'application/javascript'
return "json".equalsIgnoreCase(subtype) || subtype.endsWith("+json")
|| "javascript".equals(subtype)
// apparently Microsoft once again has interesting alternative types?
|| "x-javascript".equals(subtype)
|| "x-json".equals(subtype) // [Issue#40]
;
}
/* Not sure if this can happen; but it seems reasonable
* that we can at least produce JSON without media type?
*/
return true;
}
@Override
protected ObjectMapper _locateMapperViaProvider(Class<?> type, MediaType mediaType)
{
if (_providers != null) {
ContextResolver<ObjectMapper> resolver = _providers.getContextResolver(ObjectMapper.class, mediaType);
/* Above should work as is, but due to this bug
* [https://jersey.dev.java.net/issues/show_bug.cgi?id=288]
* in Jersey, it doesn't. But this works until resolution of
* the issue:
*/
if (resolver == null) {
resolver = _providers.getContextResolver(ObjectMapper.class, null);
}
if (resolver != null) {
return resolver.getContext(type);
}
}
return null;
}
@Override
protected JsonEndpointConfig _configForReading(ObjectReader reader,
Annotation[] annotations) {
return JsonEndpointConfig.forReading(reader, annotations);
}
@Override
protected JsonEndpointConfig _configForWriting(ObjectWriter writer,
Annotation[] annotations) {
return JsonEndpointConfig.forWriting(writer, annotations,
_jsonpFunctionName);
}
Deprecated: Since 2.2 use hasMatchingMediaType(MediaType)
instead
/**
* @deprecated Since 2.2 use {@link #hasMatchingMediaType(MediaType)} instead
*/
@Deprecated
protected boolean isJsonType(MediaType mediaType) {
return hasMatchingMediaType(mediaType);
}
}