org.codehaus.jackson.xc.JaxbAnnotationIntrospector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
package org.codehaus.jackson.xc;
import java.beans.Introspector;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.Versioned;
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonTypeInfo;
import org.codehaus.jackson.map.*;
import org.codehaus.jackson.map.annotate.JsonCachable;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.map.introspect.*;
import org.codehaus.jackson.map.jsontype.NamedType;
import org.codehaus.jackson.map.jsontype.TypeResolverBuilder;
import org.codehaus.jackson.map.jsontype.impl.StdTypeResolverBuilder;
import org.codehaus.jackson.map.util.BeanUtil;
import org.codehaus.jackson.map.util.ClassUtil;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.util.VersionUtil;
/**
* Annotation introspector that leverages JAXB annotations where applicable to JSON mapping.
*
* The following JAXB annotations are not supported yet (but some may be supported in future)
*
* - {@link XmlAnyAttribute} not yet used (as of 1.5) but may be in future (as an alias for @JsonAnySetter?)
*
- {@link XmlAnyElement} not yet used, may be as per [JACKSON-253]
*
- {@link javax.xml.bind.annotation.XmlAttachmentRef}: JSON does not support external attachments
*
- {@link XmlElementDecl}
*
- {@link XmlElementRefs} because Jackson doesn't have any support for 'named' collection items -- however,
* this may become partially supported as per [JACKSON-253].
*
- {@link XmlID} because Jackson doesn't support referential integrity. NOTE: this too may be supported
* in future if/when id references are handled
*
- {@link XmlIDREF} same as
XmlID
* - {@link javax.xml.bind.annotation.XmlInlineBinaryData} since the underlying concepts
* (like XOP) do not exist in JSON -- Jackson will always use inline base64 encoding as the method
*
- {@link javax.xml.bind.annotation.XmlList} because JSON does have (or necessarily need)
* method of serializing list of values as space-separated Strings
*
- {@link javax.xml.bind.annotation.XmlMimeType}
*
- {@link javax.xml.bind.annotation.XmlMixed} since JSON has no concept of mixed content
*
- {@link XmlRegistry}
*
- {@link XmlRootElement} is recognized and used (as of 1.7) for defining root wrapper name (if used)
*
- {@link XmlSchema} not used, unlikely to be used
*
- {@link XmlSchemaType} not used, unlikely to be used
*
- {@link XmlSchemaTypes} not used, unlikely to be used
*
- {@link XmlSeeAlso} not needed for anything currently (could theoretically be useful
* for locating subtypes for Polymorphic Type Handling)
*
*
* Note also the following limitations:
*
*
* - Any property annotated with {@link XmlValue} will have a property named 'value' on its JSON object.
*
*
* @author Ryan Heaton
* @author Tatu Saloranta
*/
public class JaxbAnnotationIntrospector
extends AnnotationIntrospector
implements Versioned
{
protected final static String MARKER_FOR_DEFAULT = "##default";
protected final String _jaxbPackageName;
protected final JsonSerializer> _dataHandlerSerializer;
protected final JsonDeserializer> _dataHandlerDeserializer;
public JaxbAnnotationIntrospector()
{
_jaxbPackageName = XmlElement.class.getPackage().getName();
JsonSerializer> dataHandlerSerializer = null;
JsonDeserializer> dataHandlerDeserializer = null;
/* Data handlers included dynamically, to try to prevent issues on platforms
* with less than complete support for JAXB API
*/
try {
dataHandlerSerializer = (JsonSerializer>) Class.forName("org.codehaus.jackson.xc.DataHandlerJsonSerializer").newInstance();
dataHandlerDeserializer = (JsonDeserializer>) Class.forName("org.codehaus.jackson.xc.DataHandlerJsonDeserializer").newInstance();
} catch (Throwable e) {
//dataHandlers not supported...
}
_dataHandlerSerializer = dataHandlerSerializer;
_dataHandlerDeserializer = dataHandlerDeserializer;
}
/**
* Method that will return version information stored in and read from jar
* that contains this class.
*
* @since 1.6
*/
@Override
public Version version() {
return VersionUtil.versionFor(getClass());
}
/*
/**********************************************************
/* General annotation properties
/**********************************************************
*/
/**
* An annotation is handled if it's in the same package as @XmlElement, including subpackages.
*
* @param ann The annotation.
* @return Whether the annotation is in the JAXB package.
*/
@Override
public boolean isHandled(Annotation ann)
{
/* note: class we want is the annotation class, not instance
* (since annotation instances, like enums, may be of different
* physical type!)
*/
Class> cls = ann.annotationType();
Package pkg = cls.getPackage();
String pkgName = (pkg != null) ? pkg.getName() : cls.getName();
if (pkgName.startsWith(_jaxbPackageName)) {
return true;
}
// as per [JACKSON-472], also need to recognize @JsonCachable
if (cls == JsonCachable.class) {
return true;
}
return false;
}
/*
/**********************************************************
/* General class annotations
/**********************************************************
*/
@Override
public Boolean findCachability(AnnotatedClass ac)
{
/* 30-Jan-2011, tatu: As per [JACKSON-472], we may want to also
* check Jackson annotation here, because sometimes JAXB
* introspector is used alone...
*/
JsonCachable ann = ac.getAnnotation(JsonCachable.class);
if (ann != null) {
return ann.value() ? Boolean.TRUE : Boolean.FALSE;
}
return null;
}
@Override
public String findRootName(AnnotatedClass ac)
{
XmlRootElement elem = findRootElementAnnotation(ac);
if (elem != null) {
String name = elem.name();
// default means "derive from class name"; so we'll return ""
return MARKER_FOR_DEFAULT.equals(name) ? "" : name;
}
return null;
}
@Override
public String[] findPropertiesToIgnore(AnnotatedClass ac) {
// nothing in JAXB for this?
return null;
}
@Override
public Boolean findIgnoreUnknownProperties(AnnotatedClass ac) {
/* 08-Nov-2009, tatus: This is bit trickier: by default JAXB
* does actually ignore all unknown properties.
* But since there is no annotation to
* specify or change this, it seems wrong to claim such setting
* is in effect. May need to revisit this issue in future
*/
return null;
}
@Override
public Boolean isIgnorableType(AnnotatedClass ac) {
// Does JAXB have any such indicators? No?
return null;
}
/*
/**********************************************************
/* General member (field, method/constructor) annotations
/**********************************************************
*/
@Override
public boolean hasIgnoreMarker(AnnotatedMember m) {
return m.getAnnotation(XmlTransient.class) != null;
}
/*
/**********************************************************
/* Property auto-detection
/**********************************************************
*/
@Override
public VisibilityChecker> findAutoDetectVisibility(AnnotatedClass ac,
VisibilityChecker> checker)
{
XmlAccessType at = findAccessType(ac);
if (at == null) {
/* JAXB default is "PUBLIC_MEMBER"; however, here we should not
* override settings if there is no annotation -- that would mess
* up global baseline. Fortunately Jackson defaults are very close
* to JAXB 'PUBLIC_MEMBER' settings (considering that setters and
* getters must come in pairs)
*/
return checker;
}
// Note: JAXB does not do creator auto-detection, can (and should) ignore
switch (at) {
case FIELD: // all fields, independent of visibility; no methods
return checker.withFieldVisibility(Visibility.ANY)
.withSetterVisibility(Visibility.NONE)
.withGetterVisibility(Visibility.NONE)
.withIsGetterVisibility(Visibility.NONE)
;
case NONE: // no auto-detection
return checker.withFieldVisibility(Visibility.NONE)
.withSetterVisibility(Visibility.NONE)
.withGetterVisibility(Visibility.NONE)
.withIsGetterVisibility(Visibility.NONE)
;
case PROPERTY:
return checker.withFieldVisibility(Visibility.NONE)
.withSetterVisibility(Visibility.PUBLIC_ONLY)
.withGetterVisibility(Visibility.PUBLIC_ONLY)
.withIsGetterVisibility(Visibility.PUBLIC_ONLY)
;
case PUBLIC_MEMBER:
return checker.withFieldVisibility(Visibility.PUBLIC_ONLY)
.withSetterVisibility(Visibility.PUBLIC_ONLY)
.withGetterVisibility(Visibility.PUBLIC_ONLY)
.withIsGetterVisibility(Visibility.PUBLIC_ONLY)
;
}
return checker;
}
/**
* Method for locating JAXB {@link XmlAccessType} annotation value
* for given annotated entity, if it has one, or inherits one from
* its ancestors (in JAXB sense, package etc). Returns null if
* nothing has been explicitly defined.
*/
protected XmlAccessType findAccessType(Annotated ac)
{
XmlAccessorType at = findAnnotation(XmlAccessorType.class, ac, true, true, true);
return (at == null) ? null : at.value();
}
/*
/**********************************************************
/* Class annotations for PM type handling (1.5+)
/**********************************************************
*/
@Override
public TypeResolverBuilder> findTypeResolver(MapperConfig> config,
AnnotatedClass ac, JavaType baseType)
{
// no per-class type resolvers, right?
return null;
}
@Override
public TypeResolverBuilder> findPropertyTypeResolver(MapperConfig> config,
AnnotatedMember am, JavaType baseType)
{
/* First: @XmlElements and @XmlElementRefs only applies type for immediate property, if it
* is NOT a structured type.
*/
if (baseType.isContainerType()) return null;
return _typeResolverFromXmlElements(am);
}
@Override
public TypeResolverBuilder> findPropertyContentTypeResolver(MapperConfig> config,
AnnotatedMember am, JavaType containerType)
{
/* First: let's ensure property is a container type: caller should have
* verified but just to be sure
*/
if (!containerType.isContainerType()) {
throw new IllegalArgumentException("Must call method with a container type (got "+containerType+")");
}
return _typeResolverFromXmlElements(am);
}
protected TypeResolverBuilder> _typeResolverFromXmlElements(AnnotatedMember am)
{
/* If simple type, @XmlElements and @XmlElementRefs are applicable.
* Note: @XmlElement and @XmlElementRef are NOT handled here, since they
* are handled specifically as non-polymorphic indication
* of the actual type
*/
XmlElements elems = findAnnotation(XmlElements.class, am, false, false, false);
XmlElementRefs elemRefs = findAnnotation(XmlElementRefs.class, am, false, false, false);
if (elems == null && elemRefs == null) {
return null;
}
TypeResolverBuilder> b = new StdTypeResolverBuilder();
// JAXB always uses type name as id
b = b.init(JsonTypeInfo.Id.NAME, null);
// and let's consider WRAPPER_OBJECT to be canonical inclusion method
b = b.inclusion(JsonTypeInfo.As.WRAPPER_OBJECT);
return b;
}
@Override
public List findSubtypes(Annotated a)
{
// No package/superclass defaulting (only used with fields, methods)
XmlElements elems = findAnnotation(XmlElements.class, a, false, false, false);
if (elems != null) {
ArrayList result = new ArrayList();
for (XmlElement elem : elems.value()) {
String name = elem.name();
if (MARKER_FOR_DEFAULT.equals(name)) name = null;
result.add(new NamedType(elem.type(), name));
}
return result;
}
else {
XmlElementRefs elemRefs = findAnnotation(XmlElementRefs.class, a, false, false, false);
if (elemRefs != null) {
ArrayList result = new ArrayList();
for (XmlElementRef elemRef : elemRefs.value()) {
Class> refType = elemRef.type();
// only good for types other than JAXBElement (which is XML based)
if (!JAXBElement.class.isAssignableFrom(refType)) {
// [JACKSON-253] first consider explicit name declaration
String name = elemRef.name();
if (name == null || MARKER_FOR_DEFAULT.equals(name)) {
XmlRootElement rootElement = (XmlRootElement) refType.getAnnotation(XmlRootElement.class);
if (rootElement != null) {
name = rootElement.name();
}
}
if (name == null || MARKER_FOR_DEFAULT.equals(name)) {
name = Introspector.decapitalize(refType.getSimpleName());
}
result.add(new NamedType(refType, name));
}
}
return result;
}
}
return null;
}
@Override
public String findTypeName(AnnotatedClass ac) {
XmlType type = findAnnotation(XmlType.class, ac, false, false, false);
if (type != null) {
String name = type.name();
if (!MARKER_FOR_DEFAULT.equals(name)) return name;
}
return null;
}
/*
/**********************************************************
/* General method annotations
/**********************************************************
*/
@Override
public boolean isIgnorableMethod(AnnotatedMethod m)
{
return m.getAnnotation(XmlTransient.class) != null;
}
@Override
public boolean isIgnorableConstructor(AnnotatedConstructor c)
{
/* @XmlTransient can not be attached to constructors...
* so there seems to be no way to do this. But then again,
* JAXB does not use non-default constructors anyway.
*/
return false;
}
/*
/**********************************************************
/* General field annotations
/**********************************************************
*/
@Override
public boolean isIgnorableField(AnnotatedField f)
{
return f.getAnnotation(XmlTransient.class) != null;
}
/*
/**********************************************************
/* Serialization: general annotations
/**********************************************************
*/
@Override
public JsonSerializer> findSerializer(Annotated am)
{
XmlAdapter