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 jackson-xc Show documentation
Show all versions of jackson-xc Show documentation
Extensions that provide interoperability support for
Jackson JSON processor's data binding functionality.
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