org.opentripplanner.api.model.JSONObjectMapperProvider Maven / Gradle / Ivy
package org.opentripplanner.api.model;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* AgencyAndId is a third-party class in One Bus Away which represents a GTFS element's ID,
* including an agency name as identifier scope information since more than one feed may be loaded
* at once.
*
* While this works when there is only one agency per feed, the true scope of identifiers is the
* feed, and the same agency could appear in multiple feeds. We don't want the key "agencyId" to
* appear in the final OTP API because it will eventually not represent an agency.
*
* See this ticket: https://github.com/opentripplanner/OpenTripPlanner/issues/1352
*
* And this proposal to gtfs-changes:
* https://groups.google.com/d/msg/gtfs-changes/zVjEoNIPr_Y/4ngWCajPoS0J
*
* Our solution is to serialize the FeedScopedId as a single string with a separator character
* between the agency and ID. In future versions this scoped identifier will actually represent a
* feed and ID. The important thing is that the API will remain the same, and identifiers fetched
* from one API result can be used in another request with no conflicts.
*
* TODO - This is no longer the case - the FeedScopedId can be annotated.
* Since FeedScopedId is a third-party class, we can't modify it with a custom serialization method
* or annotations. Instead, we have to let Jackson know which custom serializer class applies to the
* third-party type. According to http://wiki.fasterxml.com/JacksonHowToCustomSerializers "Jackson
* 1.7 added ability to register serializers and deserializes via Module interface. This is the
* recommended way to add custom serializers."
*
* A Jackson "Module" is a group of extensions to default functionality, used for example to support
* serializing new data types. Modules are registered with an ObjectMapper, which constructs
* ObjectWriters, which are used to do the final JSON writing. In OTP the ObjectWriter construction
* and JSON writing are performed automatically by Jersey.
*/
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JSONObjectMapperProvider implements ContextResolver {
private final ObjectMapper mapper;
/**
* Pre-instantiate a Jackson ObjectMapper that will be handed off to all incoming Jersey
* requests, and used to construct the ObjectWriters that will produce JSON responses.
*/
public JSONObjectMapperProvider() {
// Create a module, i.e. a group of one or more Jackson extensions.
// Our module includes a single class-serializer relationship.
// Constructors are available for both unnamed, unversioned throwaway modules
// and named, versioned reusable modules.
mapper = new ObjectMapper()
.registerModule(FeedScopedIdSerializer.makeModule())
.setSerializationInclusion(Include.NON_NULL); // skip null fields
}
/**
* When serializing any kind of result, use the same ObjectMapper. The "type" parameter will be
* the type of the object being serialized, so you could provide different ObjectMappers for
* different result types.
*/
@Override
public ObjectMapper getContext(Class> type) {
return mapper;
}
}