You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2019/12/30 17:49:25 UTC

[juneau] branch master updated: Remove extended metadata from ClassMeta/BeanMeta/BeanPropertyMeta.

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 060a287  Remove extended metadata from ClassMeta/BeanMeta/BeanPropertyMeta.
060a287 is described below

commit 060a287edf5adcb4112c4b2a7f3ffe91fcd9699f
Author: JamesBognar <ja...@apache.org>
AuthorDate: Mon Dec 30 12:49:11 2019 -0500

    Remove extended metadata from ClassMeta/BeanMeta/BeanPropertyMeta.
---
 .../java/org/apache/juneau/jena/RdfBeanMeta.java   |  7 +-
 .../apache/juneau/jena/RdfBeanPropertyMeta.java    |  5 +-
 .../java/org/apache/juneau/jena/RdfClassMeta.java  |  5 +-
 .../org/apache/juneau/jena/RdfMetaProvider.java}   | 40 ++++++-----
 .../java/org/apache/juneau/jena/RdfParser.java     | 75 ++++++++++++++++++-
 .../org/apache/juneau/jena/RdfParserSession.java   | 48 ++++++++++++-
 .../java/org/apache/juneau/jena/RdfSerializer.java | 75 ++++++++++++++++++-
 .../apache/juneau/jena/RdfSerializerSession.java   | 77 +++++++++++++-------
 .../src/main/java/org/apache/juneau/BeanMeta.java  | 28 ++++----
 .../java/org/apache/juneau/BeanPropertyMeta.java   | 30 +++++---
 .../src/main/java/org/apache/juneau/ClassMeta.java | 47 ++++--------
 ...BeanMetaExtended.java => ExtendedBeanMeta.java} |  8 +--
 ...Extended.java => ExtendedBeanPropertyMeta.java} |  8 +--
 ...assMetaExtended.java => ExtendedClassMeta.java} |  8 +--
 .../{BeanMetaExtended.java => ExtendedMeta.java}   | 30 +-------
 .../apache/juneau/html/HtmlBeanPropertyMeta.java   |  6 +-
 .../java/org/apache/juneau/html/HtmlClassMeta.java |  5 +-
 .../HtmlMetaProvider.java}                         | 33 ++++-----
 .../java/org/apache/juneau/html/HtmlParser.java    | 34 ++++++++-
 .../org/apache/juneau/html/HtmlParserSession.java  | 32 ++++++++-
 .../org/apache/juneau/html/HtmlSerializer.java     | 31 +++++++-
 .../apache/juneau/html/HtmlSerializerSession.java  | 68 +++++++++++-------
 .../java/org/apache/juneau/json/JsonClassMeta.java |  5 +-
 .../JsonMetaProvider.java}                         | 33 +++------
 .../java/org/apache/juneau/json/JsonParser.java    | 20 +++++-
 .../org/apache/juneau/json/JsonParserSession.java  | 18 ++++-
 .../apache/juneau/json/JsonSchemaSerializer.java   | 31 +++++++-
 .../juneau/json/JsonSchemaSerializerSession.java   | 26 +++++++
 .../org/apache/juneau/json/JsonSerializer.java     | 18 ++++-
 .../apache/juneau/json/JsonSerializerSession.java  | 16 ++++-
 .../jsonschema/JsonSchemaBeanPropertyMeta.java     |  5 +-
 .../juneau/jsonschema/JsonSchemaClassMeta.java     | 15 ++--
 .../juneau/jsonschema/JsonSchemaGenerator.java     | 29 +++++++-
 .../jsonschema/JsonSchemaGeneratorSession.java     | 30 +++++++-
 .../JsonSchemaMetaProvider.java}                   | 33 ++++-----
 .../juneau/urlencoding/UrlEncodingClassMeta.java   |  5 +-
 .../UrlEncodingMetaProvider.java}                  | 33 +++------
 .../juneau/urlencoding/UrlEncodingParser.java      | 20 +++++-
 .../urlencoding/UrlEncodingParserSession.java      | 20 +++++-
 .../juneau/urlencoding/UrlEncodingSerializer.java  | 20 +++++-
 .../urlencoding/UrlEncodingSerializerSession.java  | 16 ++++-
 .../java/org/apache/juneau/utils/MetadataMap.java  | 84 ----------------------
 .../java/org/apache/juneau/xml/XmlBeanMeta.java    | 13 ++--
 .../org/apache/juneau/xml/XmlBeanPropertyMeta.java | 14 ++--
 .../java/org/apache/juneau/xml/XmlClassMeta.java   |  5 +-
 .../XmlMetaProvider.java}                          | 39 +++++-----
 .../main/java/org/apache/juneau/xml/XmlParser.java | 43 ++++++++++-
 .../org/apache/juneau/xml/XmlParserSession.java    | 42 +++++++++--
 .../java/org/apache/juneau/xml/XmlSerializer.java  | 42 ++++++++++-
 .../apache/juneau/xml/XmlSerializerSession.java    | 70 +++++++++++-------
 .../xmlschema/XmlSchemaSerializerSession.java      | 24 +++----
 51 files changed, 1000 insertions(+), 469 deletions(-)

diff --git a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanMeta.java b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanMeta.java
index 4e035ec..4c2b721 100644
--- a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanMeta.java
+++ b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanMeta.java
@@ -19,7 +19,7 @@ import org.apache.juneau.jena.annotation.*;
  * Metadata on beans specific to the RDF serializers and parsers pulled from the {@link Rdf @Rdf} annotation on the
  * class.
  */
-public class RdfBeanMeta extends BeanMetaExtended {
+public class RdfBeanMeta extends ExtendedBeanMeta {
 
 	// RDF related fields
 	private final BeanPropertyMeta beanUriProperty;    // Bean property that identifies the URI of the bean.
@@ -28,13 +28,14 @@ public class RdfBeanMeta extends BeanMetaExtended {
 	 * Constructor.
 	 *
 	 * @param beanMeta The metadata on the bean that this metadata applies to.
+	 * @param rdfMetaProvider RDF metadata provider (for finding information about other artifacts).
 	 */
-	public RdfBeanMeta(BeanMeta<?> beanMeta) {
+	public RdfBeanMeta(BeanMeta<?> beanMeta, RdfMetaProvider rdfMetaProvider) {
 		super(beanMeta);
 
 		BeanPropertyMeta t_beanUriProperty = null;
 		for (BeanPropertyMeta p : beanMeta.getPropertyMetas()) {
-			RdfBeanPropertyMeta bpm = p.getExtendedMeta(RdfBeanPropertyMeta.class);
+			RdfBeanPropertyMeta bpm = rdfMetaProvider.getRdfBeanPropertyMeta(p);
 			if (bpm.isBeanUri())
 				t_beanUriProperty = p;
 		}
diff --git a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanPropertyMeta.java b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanPropertyMeta.java
index 6fdfb63..ec1eaff 100644
--- a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanPropertyMeta.java
+++ b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanPropertyMeta.java
@@ -22,7 +22,7 @@ import org.apache.juneau.xml.*;
  * Metadata on bean properties specific to the RDF serializers and parsers pulled from the {@link Rdf @Rdf} annotation
  * on the bean property.
  */
-public class RdfBeanPropertyMeta extends BeanPropertyMetaExtended {
+public class RdfBeanPropertyMeta extends ExtendedBeanPropertyMeta {
 
 	/**
 	 * Default instance.
@@ -38,8 +38,9 @@ public class RdfBeanPropertyMeta extends BeanPropertyMetaExtended {
 	 * Constructor.
 	 *
 	 * @param bpm The metadata of the bean property of this additional metadata.
+	 * @param rdfMetaProvider RDF metadata provider (for finding information about other artifacts).
 	 */
-	public RdfBeanPropertyMeta(BeanPropertyMeta bpm) {
+	public RdfBeanPropertyMeta(BeanPropertyMeta bpm, RdfMetaProvider rdfMetaProvider) {
 		super(bpm);
 
 		List<Rdf> rdfs = bpm.findAnnotations(Rdf.class);
diff --git a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfClassMeta.java b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfClassMeta.java
index 9474a96..ac1fe64 100644
--- a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfClassMeta.java
+++ b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfClassMeta.java
@@ -23,7 +23,7 @@ import org.apache.juneau.xml.*;
  * Metadata on classes specific to the RDF serializers and parsers pulled from the {@link Rdf @Rdf} annotation on the
  * class.
  */
-public class RdfClassMeta extends ClassMetaExtended {
+public class RdfClassMeta extends ExtendedClassMeta {
 
 	private final Rdf rdf;
 	private final RdfCollectionFormat collectionFormat;
@@ -33,8 +33,9 @@ public class RdfClassMeta extends ClassMetaExtended {
 	 * Constructor.
 	 *
 	 * @param cm The class that this annotation is defined on.
+	 * @param rdfMetaProvider RDF metadata provider (for finding information about other artifacts).
 	 */
-	public RdfClassMeta(ClassMeta<?> cm) {
+	public RdfClassMeta(ClassMeta<?> cm, RdfMetaProvider rdfMetaProvider) {
 		super(cm);
 		ClassInfo ci = cm.getInfo();
 		this.rdf = ci.getAnnotation(Rdf.class);
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMetaExtended.java b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfMetaProvider.java
similarity index 63%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMetaExtended.java
copy to juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfMetaProvider.java
index 43362f2..7f598c7 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMetaExtended.java
+++ b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfMetaProvider.java
@@ -10,35 +10,37 @@
 // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
 // * specific language governing permissions and limitations under the License.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau;
+package org.apache.juneau.jena;
+
+import org.apache.juneau.*;
+import org.apache.juneau.xml.*;
 
 /**
- * Defines extended language-specific metadata associated with a bean property.
- *
- * <p>
- * Serializers and parsers can implement their own language-specific metadata on a bean property and retrieve
- * the metadata using the {@link BeanPropertyMeta#getExtendedMeta(Class)} method.
+ * Interface for providing access to {@link RdfClassMeta}, {@link RdfBeanMeta}, and {@link RdfBeanPropertyMeta} objects.
  */
-public class BeanPropertyMetaExtended {
+public interface RdfMetaProvider extends XmlMetaProvider {
 
-	private final BeanPropertyMeta bpm;
+	/**
+	 * Returns the language-specific metadata on the specified class.
+	 *
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
+	 */
+	RdfClassMeta getRdfClassMeta(ClassMeta<?> cm);
 
 	/**
-	 * Constructor.
+	 * Returns the language-specific metadata on the specified bean.
 	 *
-	 * @param bpm The metadata of the bean property we're extending.
-	 * @throws BeanRuntimeException If any error occurred trying to construct the metadata.
+	 * @param bm The bean to return the metadata on.
+	 * @return The metadata.
 	 */
-	public BeanPropertyMetaExtended(BeanPropertyMeta bpm) throws BeanRuntimeException {
-		this.bpm = bpm;
-	}
+	RdfBeanMeta getRdfBeanMeta(BeanMeta<?> bm);
 
 	/**
-	 * Returns the bean property metadata that was passed into the constructor.
+	 * Returns the language-specific metadata on the specified bean property.
 	 *
-	 * @return The bean property metadata that was passed into the constructor.
+	 * @param bpm The bean property to return the metadata on.
+	 * @return The metadata.
 	 */
-	protected BeanPropertyMeta getBeanPropertyMeta() {
-		return bpm;
-	}
+	RdfBeanPropertyMeta getRdfBeanPropertyMeta(BeanPropertyMeta bpm);
 }
diff --git a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java
index f647de4..a2dc6f3 100644
--- a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java
+++ b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java
@@ -15,6 +15,7 @@ package org.apache.juneau.jena;
 import static org.apache.juneau.internal.CollectionUtils.*;
 
 import java.util.*;
+import java.util.concurrent.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
@@ -43,7 +44,7 @@ import org.apache.juneau.xml.*;
  * </ul>
  */
 @ConfigurableContext(prefixes={RdfCommon.PREFIX,RdfParser.PREFIX})
-public class RdfParser extends ReaderParser implements RdfCommon {
+public class RdfParser extends ReaderParser implements RdfCommon, RdfMetaProvider {
 
 	private static final Namespace
 		DEFAULT_JUNEAU_NS = Namespace.create("j", "http://www.apache.org/juneau/"),
@@ -105,6 +106,14 @@ public class RdfParser extends ReaderParser implements RdfCommon {
 
 	final Map<String,Object> jenaProperties;
 
+	private final Map<ClassMeta<?>,RdfClassMeta> rdfClassMetas = new ConcurrentHashMap<>();
+	private final Map<BeanMeta<?>,RdfBeanMeta> rdfBeanMetas = new ConcurrentHashMap<>();
+	private final Map<BeanPropertyMeta,RdfBeanPropertyMeta> rdfBeanPropertyMetas = new ConcurrentHashMap<>();
+
+	private final Map<ClassMeta<?>,XmlClassMeta> xmlClassMetas = new ConcurrentHashMap<>();
+	private final Map<BeanMeta<?>,XmlBeanMeta> xmlBeanMetas = new ConcurrentHashMap<>();
+	private final Map<BeanPropertyMeta,XmlBeanPropertyMeta> xmlBeanPropertyMetas = new ConcurrentHashMap<>();
+
 	/**
 	 * Constructor.
 	 *
@@ -168,6 +177,70 @@ public class RdfParser extends ReaderParser implements RdfCommon {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Override /* RdfMetaProvider */
+	public RdfClassMeta getRdfClassMeta(ClassMeta<?> cm) {
+		RdfClassMeta m = rdfClassMetas.get(cm);
+		if (m == null) {
+			m = new RdfClassMeta(cm, this);
+			rdfClassMetas.put(cm, m);
+		}
+		return m;
+	}
+
+	@Override /* RdfMetaProvider */
+	public RdfBeanMeta getRdfBeanMeta(BeanMeta<?> bm) {
+		RdfBeanMeta m = rdfBeanMetas.get(bm);
+		if (m == null) {
+			m = new RdfBeanMeta(bm, this);
+			rdfBeanMetas.put(bm, m);
+		}
+		return m;
+	}
+
+	@Override /* RdfMetaProvider */
+	public RdfBeanPropertyMeta getRdfBeanPropertyMeta(BeanPropertyMeta bpm) {
+		RdfBeanPropertyMeta m = rdfBeanPropertyMetas.get(bpm);
+		if (m == null) {
+			m = new RdfBeanPropertyMeta(bpm.getDelegateFor(), this);
+			rdfBeanPropertyMetas.put(bpm, m);
+		}
+		return m;
+	}
+
+	@Override /* XmlMetaProvider */
+	public XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) {
+		XmlClassMeta m = xmlClassMetas.get(cm);
+		if (m == null) {
+			m = new XmlClassMeta(cm, this);
+			xmlClassMetas.put(cm, m);
+		}
+		return m;
+	}
+
+	@Override /* XmlMetaProvider */
+	public XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) {
+		XmlBeanMeta m = xmlBeanMetas.get(bm);
+		if (m == null) {
+			m = new XmlBeanMeta(bm, this);
+			xmlBeanMetas.put(bm, m);
+		}
+		return m;
+	}
+
+	@Override /* XmlMetaProvider */
+	public XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
+		XmlBeanPropertyMeta m = xmlBeanPropertyMetas.get(bpm);
+		if (m == null) {
+			m = new XmlBeanPropertyMeta(bpm.getDelegateFor(), this);
+			xmlBeanPropertyMetas.put(bpm, m);
+		}
+		return m;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Common properties
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
index 31b3eed..3d2a171 100644
--- a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
+++ b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
@@ -168,7 +168,7 @@ public class RdfParserSession extends ReaderParserSession {
 
 	private <T> BeanMap<T> parseIntoBeanMap(Resource r2, BeanMap<T> m) throws IOException, ParseException, ExecutableException {
 		BeanMeta<T> bm = m.getMeta();
-		RdfBeanMeta rbm = bm.getExtendedMeta(RdfBeanMeta.class);
+		RdfBeanMeta rbm = getRdfBeanMeta(bm);
 		if (rbm.hasBeanUri() && r2.getURI() != null)
 			rbm.getBeanUriProperty().set(m, null, r2.getURI());
 		for (StmtIterator i = r2.listProperties(); i.hasNext();) {
@@ -199,7 +199,7 @@ public class RdfParserSession extends ReaderParserSession {
 	}
 
 	private boolean isMultiValuedCollections(BeanPropertyMeta pMeta) {
-		RdfBeanPropertyMeta bpRdf = (pMeta == null ? RdfBeanPropertyMeta.DEFAULT : pMeta.getExtendedMeta(RdfBeanPropertyMeta.class));
+		RdfBeanPropertyMeta bpRdf = (pMeta == null ? RdfBeanPropertyMeta.DEFAULT : getRdfBeanPropertyMeta(pMeta));
 
 		if (bpRdf.getCollectionFormat() != RdfCollectionFormat.DEFAULT)
 			return bpRdf.getCollectionFormat() == RdfCollectionFormat.MULTI_VALUED;
@@ -516,6 +516,50 @@ public class RdfParserSession extends ReaderParserSession {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * Returns the language-specific metadata on the specified class.
+	 *
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected RdfClassMeta getRdfClassMeta(ClassMeta<?> cm) {
+		return ctx.getRdfClassMeta(cm);
+	}
+
+	/**
+	 * Returns the language-specific metadata on the specified bean.
+	 *
+	 * @param bm The bean to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected RdfBeanMeta getRdfBeanMeta(BeanMeta<?> bm) {
+		return ctx.getRdfBeanMeta(bm);
+	}
+
+	/**
+	 * Returns the language-specific metadata on the specified bean property.
+	 *
+	 * @param bpm The bean property to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected RdfBeanPropertyMeta getRdfBeanPropertyMeta(BeanPropertyMeta bpm) {
+		return ctx.getRdfBeanPropertyMeta(bpm);
+	}
+
+	/**
+	 * Returns the language-specific metadata on the specified bean property.
+	 *
+	 * @param bpm The bean property to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
+		return ctx.getXmlBeanPropertyMeta(bpm);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Other methods
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java
index 08aa592..86fe318 100644
--- a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java
+++ b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java
@@ -15,6 +15,7 @@ package org.apache.juneau.jena;
 import static org.apache.juneau.internal.CollectionUtils.*;
 
 import java.util.*;
+import java.util.concurrent.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
@@ -42,7 +43,7 @@ import org.apache.juneau.xml.annotation.*;
  * </ul>
  */
 @ConfigurableContext(prefixes={RdfCommon.PREFIX,RdfSerializer.PREFIX})
-public class RdfSerializer extends WriterSerializer implements RdfCommon {
+public class RdfSerializer extends WriterSerializer implements RdfCommon, RdfMetaProvider {
 
 	private static final Namespace
 		DEFAULT_JUNEAU_NS = Namespace.create("j", "http://www.apache.org/juneau/"),
@@ -215,6 +216,14 @@ public class RdfSerializer extends WriterSerializer implements RdfCommon {
 	final Map<String,Object> jenaProperties;
 	final Namespace[] namespaces;
 
+	private final Map<ClassMeta<?>,RdfClassMeta> rdfClassMetas = new ConcurrentHashMap<>();
+	private final Map<BeanMeta<?>,RdfBeanMeta> rdfBeanMetas = new ConcurrentHashMap<>();
+	private final Map<BeanPropertyMeta,RdfBeanPropertyMeta> rdfBeanPropertyMetas = new ConcurrentHashMap<>();
+
+	private final Map<ClassMeta<?>,XmlClassMeta> xmlClassMetas = new ConcurrentHashMap<>();
+	private final Map<BeanMeta<?>,XmlBeanMeta> xmlBeanMetas = new ConcurrentHashMap<>();
+	private final Map<BeanPropertyMeta,XmlBeanPropertyMeta> xmlBeanPropertyMetas = new ConcurrentHashMap<>();
+
 	/**
 	 * Constructor.
 	 *
@@ -305,6 +314,70 @@ public class RdfSerializer extends WriterSerializer implements RdfCommon {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Override /* RdfMetaProvider */
+	public RdfClassMeta getRdfClassMeta(ClassMeta<?> cm) {
+		RdfClassMeta m = rdfClassMetas.get(cm);
+		if (m == null) {
+			m = new RdfClassMeta(cm, this);
+			rdfClassMetas.put(cm, m);
+		}
+		return m;
+	}
+
+	@Override /* RdfMetaProvider */
+	public RdfBeanMeta getRdfBeanMeta(BeanMeta<?> bm) {
+		RdfBeanMeta m = rdfBeanMetas.get(bm);
+		if (m == null) {
+			m = new RdfBeanMeta(bm, this);
+			rdfBeanMetas.put(bm, m);
+		}
+		return m;
+	}
+
+	@Override /* RdfMetaProvider */
+	public RdfBeanPropertyMeta getRdfBeanPropertyMeta(BeanPropertyMeta bpm) {
+		RdfBeanPropertyMeta m = rdfBeanPropertyMetas.get(bpm);
+		if (m == null) {
+			m = new RdfBeanPropertyMeta(bpm.getDelegateFor(), this);
+			rdfBeanPropertyMetas.put(bpm, m);
+		}
+		return m;
+	}
+
+	@Override /* XmlMetaProvider */
+	public XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) {
+		XmlClassMeta m = xmlClassMetas.get(cm);
+		if (m == null) {
+			m = new XmlClassMeta(cm, this);
+			xmlClassMetas.put(cm, m);
+		}
+		return m;
+	}
+
+	@Override /* XmlMetaProvider */
+	public XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) {
+		XmlBeanMeta m = xmlBeanMetas.get(bm);
+		if (m == null) {
+			m = new XmlBeanMeta(bm, this);
+			xmlBeanMetas.put(bm, m);
+		}
+		return m;
+	}
+
+	@Override /* XmlMetaProvider */
+	public XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
+		XmlBeanPropertyMeta m = xmlBeanPropertyMetas.get(bpm);
+		if (m == null) {
+			m = new XmlBeanPropertyMeta(bpm.getDelegateFor(), this);
+			xmlBeanPropertyMetas.put(bpm, m);
+		}
+		return m;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Common properties
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
index 2036305..dcc665f 100644
--- a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
+++ b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
@@ -223,7 +223,7 @@ public final class RdfSerializerSession extends WriterSerializerSession {
 			if (o instanceof BeanMap) {
 				BeanMap bm = (BeanMap)o;
 				Object uri = null;
-				RdfBeanMeta rbm = bRdf(bm.getMeta());
+				RdfBeanMeta rbm = getRdfBeanMeta(bm.getMeta());
 				if (rbm.hasBeanUri())
 					uri = rbm.getBeanUriProperty().get(bm, null);
 				String uri2 = getUri(uri, null);
@@ -238,7 +238,7 @@ public final class RdfSerializerSession extends WriterSerializerSession {
 		} else if (sType.isBean()) {
 			BeanMap bm = toBeanMap(o);
 			Object uri = null;
-			RdfBeanMeta rbm = bRdf(bm.getMeta());
+			RdfBeanMeta rbm = getRdfBeanMeta(bm.getMeta());
 			if (rbm.hasBeanUri())
 				uri = rbm.getBeanUriProperty().get(bm, null);
 			String uri2 = getUri(uri, null);
@@ -249,8 +249,8 @@ public final class RdfSerializerSession extends WriterSerializerSession {
 
 			Collection c = sort(sType.isCollection() ? (Collection)o : toList(sType.getInnerClass(), o));
 			RdfCollectionFormat f = getCollectionFormat();
-			RdfClassMeta cRdf = cRdf(sType);
-			RdfBeanPropertyMeta bpRdf = bpRdf(bpm);
+			RdfClassMeta cRdf = getRdfClassMeta(sType);
+			RdfBeanPropertyMeta bpRdf = getRdfBeanPropertyMeta(bpm);
 
 			if (cRdf.getCollectionFormat() != RdfCollectionFormat.DEFAULT)
 				f = cRdf.getCollectionFormat();
@@ -315,8 +315,8 @@ public final class RdfSerializerSession extends WriterSerializerSession {
 
 			BeanPropertyMeta bpMeta = bpv.getMeta();
 			ClassMeta<?> cMeta = bpMeta.getClassMeta();
-			RdfBeanPropertyMeta bpRdf = bpRdf(bpMeta);
-			XmlBeanPropertyMeta bpXml = bpXml(bpMeta);
+			RdfBeanPropertyMeta bpRdf = getRdfBeanPropertyMeta(bpMeta);
+			XmlBeanPropertyMeta bpXml = getXmlBeanPropertyMeta(bpMeta);
 
 			if (bpRdf.isBeanUri())
 				continue;
@@ -369,8 +369,8 @@ public final class RdfSerializerSession extends WriterSerializerSession {
 			BeanPropertyMeta bpm, String attrName, Resource parentResource) throws IOException, SerializeException {
 
 		ClassMeta<?> elementType = sType.getElementType();
-		RdfBeanPropertyMeta bpRdf = bpRdf(bpm);
-		XmlBeanPropertyMeta bpXml = bpXml(bpm);
+		RdfBeanPropertyMeta bpRdf = getRdfBeanPropertyMeta(bpm);
+		XmlBeanPropertyMeta bpXml = getXmlBeanPropertyMeta(bpm);
 
 		for (Object e : c) {
 			Namespace ns = bpRdf.getNamespace();
@@ -386,22 +386,7 @@ public final class RdfSerializerSession extends WriterSerializerSession {
 		}
 	}
 
-	private static RdfClassMeta cRdf(ClassMeta<?> cm) {
-		return cm.getExtendedMeta(RdfClassMeta.class);
-	}
-
-	private static XmlBeanPropertyMeta bpXml(BeanPropertyMeta pMeta) {
-		return pMeta == null ? XmlBeanPropertyMeta.DEFAULT : pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
-	}
-
-	private static RdfBeanPropertyMeta bpRdf(BeanPropertyMeta pMeta) {
-		return pMeta == null ? RdfBeanPropertyMeta.DEFAULT : pMeta.getExtendedMeta(RdfBeanPropertyMeta.class);
-	}
-
-	private static RdfBeanMeta bRdf(BeanMeta bm) {
-		return (RdfBeanMeta)bm.getExtendedMeta(RdfBeanMeta.class);
-	}
-
+	
 	//-----------------------------------------------------------------------------------------------------------------
 	// Common properties
 	//-----------------------------------------------------------------------------------------------------------------
@@ -551,6 +536,50 @@ public final class RdfSerializerSession extends WriterSerializerSession {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * Returns the language-specific metadata on the specified class.
+	 *
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected RdfClassMeta getRdfClassMeta(ClassMeta<?> cm) {
+		return ctx.getRdfClassMeta(cm);
+	}
+
+	/**
+	 * Returns the language-specific metadata on the specified bean.
+	 *
+	 * @param bm The bean to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected RdfBeanMeta getRdfBeanMeta(BeanMeta<?> bm) {
+		return ctx.getRdfBeanMeta(bm);
+	}
+
+	/**
+	 * Returns the language-specific metadata on the specified bean property.
+	 *
+	 * @param bpm The bean property to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected RdfBeanPropertyMeta getRdfBeanPropertyMeta(BeanPropertyMeta bpm) {
+		return bpm == null ? RdfBeanPropertyMeta.DEFAULT : ctx.getRdfBeanPropertyMeta(bpm);
+	}
+
+	/**
+	 * Returns the language-specific metadata on the specified bean property.
+	 *
+	 * @param bpm The bean property to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
+		return bpm == null ? XmlBeanPropertyMeta.DEFAULT : ctx.getXmlBeanPropertyMeta(bpm);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Other methods
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
index 3e7fd31..3b83da0 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
@@ -27,7 +27,6 @@ import java.util.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.reflect.*;
 import org.apache.juneau.transform.*;
-import org.apache.juneau.utils.*;
 
 /**
  * Encapsulates all access to the properties of a bean class (like a souped-up {@link java.beans.BeanInfo}).
@@ -92,8 +91,6 @@ public class BeanMeta<T> {
 	/** For beans with constructors with Beanc annotation, this is the list of constructor arg properties. */
 	protected final String[] constructorArgs;
 
-	private final MetadataMap extMeta;  // Extended metadata
-
 	// Other fields
 	final String typePropertyName;                         // "_type" property actual name.
 	private final BeanPropertyMeta typeProperty;           // "_type" mock bean property.
@@ -129,7 +126,6 @@ public class BeanMeta<T> {
 		this.typeVarImpls = unmodifiableMap(b.typeVarImpls);
 		this.constructor = b.constructor;
 		this.constructorArgs = b.constructorArgs;
-		this.extMeta = b.extMeta;
 		this.beanRegistry = b.beanRegistry;
 		this.typePropertyName = b.typePropertyName;
 		this.typeProperty = BeanPropertyMeta.builder(this, typePropertyName).canRead().canWrite().rawMetaType(ctx.string()).beanRegistry(beanRegistry).build();
@@ -150,7 +146,6 @@ public class BeanMeta<T> {
 		Map<Class<?>,Class<?>[]> typeVarImpls;
 		ConstructorInfo constructor;
 		String[] constructorArgs = new String[0];
-		MetadataMap extMeta = new MetadataMap();
 		PropertyNamer propertyNamer;
 		BeanRegistry beanRegistry;
 		String dictionaryName, typePropertyName;
@@ -832,16 +827,6 @@ public class BeanMeta<T> {
 	}
 
 	/**
-	 * Returns the language-specified extended metadata on this bean class.
-	 *
-	 * @param metaDataClass The name of the metadata class to create.
-	 * @return Extended metadata on this bean class.  Never <jk>null</jk>.
-	 */
-	public <M extends BeanMetaExtended> M getExtendedMeta(Class<M> metaDataClass) {
-		return extMeta.get(metaDataClass, this);
-	}
-
-	/**
 	 * Returns metadata about the specified property.
 	 *
 	 * @param name The name of the property on this bean.
@@ -968,4 +953,17 @@ public class BeanMeta<T> {
 		sb.append('}');
 		return sb.toString();
 	}
+
+	@Override /* Object */
+	public int hashCode() {
+		return classMeta.hashCode();
+	}
+
+	@Override /* Object */
+	public boolean equals(Object o) {
+		if (o == null || ! (o instanceof BeanMeta))
+			return false;
+		BeanMeta<?> o2 = (BeanMeta<?>)o;
+		return o2.classMeta.equals(this.classMeta);
+	}
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
index bf4678b..ca2da56 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
@@ -30,7 +30,6 @@ import org.apache.juneau.reflect.*;
 import org.apache.juneau.serializer.*;
 import org.apache.juneau.transform.*;
 import org.apache.juneau.transforms.*;
-import org.apache.juneau.utils.*;
 
 /**
  * Contains metadata about a bean property.
@@ -64,12 +63,12 @@ public final class BeanPropertyMeta {
 	private final String[] properties;                        // The value of the @Beanp(properties) annotation.
 	private final PojoSwap swap;                              // PojoSwap defined only via @Beanp annotation.
 
-	private final MetadataMap extMeta;                        // Extended metadata
 	private final BeanRegistry beanRegistry;
 
 	private final Object overrideValue;                       // The bean property value (if it's an overridden delegate).
 	private final BeanPropertyMeta delegateFor;               // The bean property that this meta is a delegate for.
 	private final boolean canRead, canWrite, readOnly, writeOnly;
+	private final int hashCode;
 
 	/**
 	 * Creates a builder for {@link #BeanPropertyMeta} objects.
@@ -98,7 +97,6 @@ public final class BeanPropertyMeta {
 		BeanRegistry beanRegistry;
 		Object overrideValue;
 		BeanPropertyMeta delegateFor;
-		MetadataMap extMeta = new MetadataMap();
 		boolean canRead, canWrite, readOnly, writeOnly;
 
 		Builder(BeanMeta<?> beanMeta, String name) {
@@ -434,13 +432,13 @@ public final class BeanPropertyMeta {
 		this.beanRegistry = b.beanRegistry;
 		this.overrideValue = b.overrideValue;
 		this.delegateFor = b.delegateFor;
-		this.extMeta = b.extMeta;
 		this.isDyna = b.isDyna;
 		this.isDynaGetterMap = b.isDynaGetterMap;
 		this.canRead = b.canRead;
 		this.canWrite = b.canWrite;
 		this.readOnly = b.readOnly;
 		this.writeOnly = b.writeOnly;
+		this.hashCode = HashCode.create().add(beanMeta.hashCode()).add(name == null ? 0 : name.hashCode()).get();
 	}
 
 	/**
@@ -566,15 +564,12 @@ public final class BeanPropertyMeta {
 	}
 
 	/**
-	 * Returns the language-specified extended metadata on this bean property.
+	 * Returns the metadata on the property that this metadata is a delegate for.
 	 *
-	 * @param c The name of the metadata class to create.
-	 * @return Extended metadata on this bean property.  Never <jk>null</jk>.
+	 * @return the metadata on the property that this metadata is a delegate for, or this object if it's not a delegate.
 	 */
-	public <M extends BeanPropertyMetaExtended> M getExtendedMeta(Class<M> c) {
-		if (delegateFor != null)
-			return delegateFor.getExtendedMeta(c);
-		return extMeta.get(c, this);
+	public BeanPropertyMeta getDelegateFor() {
+		return delegateFor != null ? delegateFor : this;
 	}
 
 	/**
@@ -1294,4 +1289,17 @@ public final class BeanPropertyMeta {
 	protected boolean isWriteOnly() {
 		return writeOnly;
 	}
+
+	@Override /* Object */
+	public int hashCode() {
+		return hashCode;
+	}
+
+	@Override /* Object */
+	public boolean equals(Object o) {
+		if (o == null || ! (o instanceof BeanPropertyMeta))
+			return false;
+		BeanPropertyMeta o2 = (BeanPropertyMeta)o;
+		return o2.beanMeta.equals(this.beanMeta) && isEquals(o2.name, this.name);
+	}
 }
\ No newline at end of file
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
index fe532b0..c78437b 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
@@ -33,7 +33,6 @@ import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.reflect.*;
 import org.apache.juneau.transform.*;
-import org.apache.juneau.utils.*;
 
 /**
  * A wrapper class around the {@link Class} object that provides cached information about that class.
@@ -93,7 +92,6 @@ public final class ClassMeta<T> implements Type {
 	private final PojoSwap<T,?>[] pojoSwaps;                // The object POJO swaps associated with this bean (if it has any).
 	private final BeanFilter beanFilter;                    // The bean filter associated with this bean (if it has one).
 	private final BuilderSwap<T,?> builderSwap;             // The builder swap associated with this bean (if it has one).
-	private final MetadataMap extMeta;                      // Extended metadata
 	private final BeanContext beanContext;                  // The bean context that created this object.
 	private final ClassMeta<?>
 		elementType,                                         // If ARRAY or COLLECTION, the element class type.
@@ -143,7 +141,6 @@ public final class ClassMeta<T> implements Type {
 		this.innerClass = innerClass;
 		this.info = ClassInfo.of(innerClass);
 		this.beanContext = beanContext;
-		this.extMeta = new MetadataMap();
 		String notABeanReason = null;
 
 		wLock.lock();
@@ -253,7 +250,6 @@ public final class ClassMeta<T> implements Type {
 		this.pojoSwaps = mainType.pojoSwaps;
 		this.builderSwap = mainType.builderSwap;
 		this.beanFilter = mainType.beanFilter;
-		this.extMeta = mainType.extMeta;
 		this.initException = mainType.initException;
 		this.beanRegistry = mainType.beanRegistry;
 		this.exampleMethod = mainType.exampleMethod;
@@ -270,7 +266,6 @@ public final class ClassMeta<T> implements Type {
 	ClassMeta(ClassMeta<?>[] args) {
 		this.innerClass = (Class<T>) Object[].class;
 		this.info = ClassInfo.of(innerClass);
-		this.extMeta = new MetadataMap();
 		this.args = args;
 		this.implClass = null;
 		this.childPojoSwaps = null;
@@ -1524,16 +1519,6 @@ public final class ClassMeta<T> implements Type {
 	}
 
 	/**
-	 * Returns the language-specified extended metadata on this class.
-	 *
-	 * @param c The name of the metadata class to create.
-	 * @return Extended metadata on this class.  Never <jk>null</jk>.
-	 */
-	public <M extends ClassMetaExtended> M getExtendedMeta(Class<M> c) {
-		return extMeta.get(c, this);
-	}
-
-	/**
 	 * Returns the interface proxy invocation handler for this class.
 	 *
 	 * @return The interface proxy invocation handler, or <jk>null</jk> if it does not exist.
@@ -1778,20 +1763,6 @@ public final class ClassMeta<T> implements Type {
 	}
 
 	/**
-	 * Checks to see if the specified class type is the same as this one.
-	 *
-	 * @param t The specified class type.
-	 * @return <jk>true</jk> if the specified class type is the same as the class for this type.
-	 */
-	@Override /* Object */
-	public boolean equals(Object t) {
-		if (t == null || ! (t instanceof ClassMeta))
-			return false;
-		ClassMeta<?> t2 = (ClassMeta<?>)t;
-		return t2.getInnerClass() == this.getInnerClass();
-	}
-
-	/**
 	 * Similar to {@link #equals(Object)} except primitive and Object types that are similar are considered the same.
 	 * (e.g. <jk>boolean</jk> == <c>Boolean</c>).
 	 *
@@ -1885,11 +1856,6 @@ public final class ClassMeta<T> implements Type {
 		return innerClass.getSimpleName();
 	}
 
-	@Override /* Object */
-	public int hashCode() {
-		return super.hashCode();
-	}
-
 	/**
 	 * Returns <jk>true</jk> if this class has a transform associated with it that allows it to be created from a Reader.
 	 *
@@ -2079,4 +2045,17 @@ public final class ClassMeta<T> implements Type {
 	public <A extends Annotation> A getAnnotation(Class<A> a) {
 		return this.innerClass.getAnnotation(a);
 	}
+
+	@Override /* Object */
+	public int hashCode() {
+		return innerClass.hashCode();
+	}
+
+	@Override /* Object */
+	public boolean equals(Object o) {
+		if (o == null || ! (o instanceof ClassMeta))
+			return false;
+		ClassMeta<?> o2 = (ClassMeta<?>)o;
+		return o2.innerClass.equals(innerClass);
+	}
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ExtendedBeanMeta.java
similarity index 88%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ExtendedBeanMeta.java
index f4c2eab..f24d23f 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ExtendedBeanMeta.java
@@ -14,12 +14,8 @@ package org.apache.juneau;
 
 /**
  * Defines extended language-specific metadata associated with a bean.
- *
- * <p>
- * Serializers and parsers can implement their own language-specific metadata on a bean and retrieve the metadata using
- * the {@link BeanMeta#getExtendedMeta(Class)} method.
  */
-public class BeanMetaExtended {
+public class ExtendedBeanMeta extends ExtendedMeta {
 
 	private final BeanMeta<?> bm;
 
@@ -29,7 +25,7 @@ public class BeanMetaExtended {
 	 * @param bm The metadata of the bean we're extending.
 	 * @throws BeanRuntimeException If any error occurred trying to construct the metadata.
 	 */
-	public BeanMetaExtended(BeanMeta<?> bm) throws BeanRuntimeException {
+	public ExtendedBeanMeta(BeanMeta<?> bm) throws BeanRuntimeException {
 		this.bm = bm;
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMetaExtended.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ExtendedBeanPropertyMeta.java
similarity index 87%
rename from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMetaExtended.java
rename to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ExtendedBeanPropertyMeta.java
index 43362f2..5109637 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMetaExtended.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ExtendedBeanPropertyMeta.java
@@ -14,12 +14,8 @@ package org.apache.juneau;
 
 /**
  * Defines extended language-specific metadata associated with a bean property.
- *
- * <p>
- * Serializers and parsers can implement their own language-specific metadata on a bean property and retrieve
- * the metadata using the {@link BeanPropertyMeta#getExtendedMeta(Class)} method.
  */
-public class BeanPropertyMetaExtended {
+public class ExtendedBeanPropertyMeta extends ExtendedMeta {
 
 	private final BeanPropertyMeta bpm;
 
@@ -29,7 +25,7 @@ public class BeanPropertyMetaExtended {
 	 * @param bpm The metadata of the bean property we're extending.
 	 * @throws BeanRuntimeException If any error occurred trying to construct the metadata.
 	 */
-	public BeanPropertyMetaExtended(BeanPropertyMeta bpm) throws BeanRuntimeException {
+	public ExtendedBeanPropertyMeta(BeanPropertyMeta bpm) throws BeanRuntimeException {
 		this.bpm = bpm;
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMetaExtended.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ExtendedClassMeta.java
similarity index 89%
rename from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMetaExtended.java
rename to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ExtendedClassMeta.java
index b085de1..ae10c67 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMetaExtended.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ExtendedClassMeta.java
@@ -14,12 +14,8 @@ package org.apache.juneau;
 
 /**
  * Defines extended language-specific metadata associated with a class.
- *
- * <p>
- * Serializers and parsers can implement their own language-specific metadata on a class and retrieve the metadata
- * using the {@link ClassMeta#getExtendedMeta(Class)} method.
  */
-public class ClassMetaExtended {
+public class ExtendedClassMeta extends ExtendedMeta {
 
 	private final ClassMeta<?> cm;
 
@@ -29,7 +25,7 @@ public class ClassMetaExtended {
 	 * @param cm The metadata of the class we're extending.
 	 * @throws BeanRuntimeException If any error occurred trying to construct the metadata.
 	 */
-	public ClassMetaExtended(ClassMeta<?> cm) throws BeanRuntimeException {
+	public ExtendedClassMeta(ClassMeta<?> cm) throws BeanRuntimeException {
 		this.cm = cm;
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ExtendedMeta.java
similarity index 66%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ExtendedMeta.java
index f4c2eab..2ac483c 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ExtendedMeta.java
@@ -13,32 +13,6 @@
 package org.apache.juneau;
 
 /**
- * Defines extended language-specific metadata associated with a bean.
- *
- * <p>
- * Serializers and parsers can implement their own language-specific metadata on a bean and retrieve the metadata using
- * the {@link BeanMeta#getExtendedMeta(Class)} method.
+ * Superclass of all extended metadata classes.
  */
-public class BeanMetaExtended {
-
-	private final BeanMeta<?> bm;
-
-	/**
-	 * Constructor.
-	 *
-	 * @param bm The metadata of the bean we're extending.
-	 * @throws BeanRuntimeException If any error occurred trying to construct the metadata.
-	 */
-	public BeanMetaExtended(BeanMeta<?> bm) throws BeanRuntimeException {
-		this.bm = bm;
-	}
-
-	/**
-	 * Returns the bean metadata that was passed into the constructor.
-	 *
-	 * @return The bean metadata that was passed into the constructor.
-	 */
-	protected BeanMeta<?> getBeanMeta() {
-		return bm;
-	}
-}
+public class ExtendedMeta {}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java
index 3943455..5fea34b 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java
@@ -22,7 +22,7 @@ import org.apache.juneau.html.annotation.*;
  * annotation on the bean property.
  */
 @SuppressWarnings("rawtypes")
-public final class HtmlBeanPropertyMeta extends BeanPropertyMetaExtended {
+public final class HtmlBeanPropertyMeta extends ExtendedBeanPropertyMeta {
 
 	/**
 	 * Default instance.
@@ -38,9 +38,9 @@ public final class HtmlBeanPropertyMeta extends BeanPropertyMetaExtended {
 	 * Constructor.
 	 *
 	 * @param bpm The metadata of the bean property of this additional metadata.
-	 * @throws Exception If render class could not be instantiated.
+	 * @param htmlMetaProvider HTML metadata provider (for finding information about other artifacts).
 	 */
-	public HtmlBeanPropertyMeta(BeanPropertyMeta bpm) throws Exception {
+	public HtmlBeanPropertyMeta(BeanPropertyMeta bpm, HtmlMetaProvider htmlMetaProvider) {
 		super(bpm);
 		Builder b = new Builder();
 		if (bpm.getInnerField() != null)
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlClassMeta.java
index 1590c2e..86890b7 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlClassMeta.java
@@ -21,7 +21,7 @@ import org.apache.juneau.html.annotation.*;
  * Metadata on classes specific to the HTML serializers and parsers pulled from the {@link Html @Html} annotation on
  * the class.
  */
-public class HtmlClassMeta extends ClassMetaExtended {
+public class HtmlClassMeta extends ExtendedClassMeta {
 
 	private final Html html;
 	private final boolean noTables, noTableHeaders;
@@ -32,8 +32,9 @@ public class HtmlClassMeta extends ClassMetaExtended {
 	 * Constructor.
 	 *
 	 * @param cm The class that this annotation is defined on.
+	 * @param htmlMetaProvider HTML metadata provider (for finding information about other artifacts).
 	 */
-	public HtmlClassMeta(ClassMeta<?> cm) {
+	public HtmlClassMeta(ClassMeta<?> cm, HtmlMetaProvider htmlMetaProvider) {
 		super(cm);
 		this.html = cm.getInfo().getAnnotation(Html.class);
 		if (html != null) {
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlMetaProvider.java
similarity index 66%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlMetaProvider.java
index f4c2eab..74c7949 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlMetaProvider.java
@@ -10,35 +10,28 @@
 // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
 // * specific language governing permissions and limitations under the License.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau;
+package org.apache.juneau.html;
+
+import org.apache.juneau.*;
 
 /**
- * Defines extended language-specific metadata associated with a bean.
- *
- * <p>
- * Serializers and parsers can implement their own language-specific metadata on a bean and retrieve the metadata using
- * the {@link BeanMeta#getExtendedMeta(Class)} method.
+ * Interface for providing access to {@link HtmlClassMeta} and {@link HtmlBeanPropertyMeta} objects.
  */
-public class BeanMetaExtended {
-
-	private final BeanMeta<?> bm;
+public interface HtmlMetaProvider {
 
 	/**
-	 * Constructor.
+	 * Returns the language-specific metadata on the specified class.
 	 *
-	 * @param bm The metadata of the bean we're extending.
-	 * @throws BeanRuntimeException If any error occurred trying to construct the metadata.
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
 	 */
-	public BeanMetaExtended(BeanMeta<?> bm) throws BeanRuntimeException {
-		this.bm = bm;
-	}
+	HtmlClassMeta getHtmlClassMeta(ClassMeta<?> cm);
 
 	/**
-	 * Returns the bean metadata that was passed into the constructor.
+	 * Returns the language-specific metadata on the specified bean property.
 	 *
-	 * @return The bean metadata that was passed into the constructor.
+	 * @param bpm The bean property to return the metadata on.
+	 * @return The metadata.
 	 */
-	protected BeanMeta<?> getBeanMeta() {
-		return bm;
-	}
+	HtmlBeanPropertyMeta getHtmlBeanPropertyMeta(BeanPropertyMeta bpm);
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParser.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParser.java
index d8220b3..e6bd2c4 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParser.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParser.java
@@ -12,6 +12,9 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.html;
 
+import java.util.*;
+import java.util.concurrent.*;
+
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.parser.*;
@@ -31,7 +34,7 @@ import org.apache.juneau.xml.*;
  * This class is used primarily for automated testing of the {@link HtmlSerializer} class.
  */
 @ConfigurableContext
-public class HtmlParser extends XmlParser {
+public class HtmlParser extends XmlParser implements HtmlMetaProvider {
 
 	//-------------------------------------------------------------------------------------------------------------------
 	// Configurable properties
@@ -51,6 +54,9 @@ public class HtmlParser extends XmlParser {
 	// Instance
 	//-------------------------------------------------------------------------------------------------------------------
 
+	private final Map<ClassMeta<?>,HtmlClassMeta> htmlClassMetas = new ConcurrentHashMap<>();
+	private final Map<BeanPropertyMeta,HtmlBeanPropertyMeta> htmlBeanPropertyMetas = new ConcurrentHashMap<>();
+
 	/**
 	 * Constructor.
 	 *
@@ -92,6 +98,32 @@ public class HtmlParser extends XmlParser {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Override /* HtmlMetaProvider */
+	public HtmlClassMeta getHtmlClassMeta(ClassMeta<?> cm) {
+		HtmlClassMeta m = htmlClassMetas.get(cm);
+		if (m == null) {
+			m = new HtmlClassMeta(cm, this);
+			htmlClassMetas.put(cm, m);
+		}
+		return m;
+	}
+
+	@Override /* HtmlMetaProvider */
+	public HtmlBeanPropertyMeta getHtmlBeanPropertyMeta(BeanPropertyMeta bpm) {
+		if (bpm == null)
+			return HtmlBeanPropertyMeta.DEFAULT;
+		HtmlBeanPropertyMeta m = htmlBeanPropertyMetas.get(bpm);
+		if (m == null) {
+			m = new HtmlBeanPropertyMeta(bpm.getDelegateFor(), this);
+			htmlBeanPropertyMetas.put(bpm, m);
+		}
+		return m;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Other methods
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java
index 8e66874..6ad4d2b 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java
@@ -44,6 +44,8 @@ public final class HtmlParserSession extends XmlParserSession {
 		)
 	);
 
+	private final HtmlParser ctx;
+
 	/**
 	 * Create a new session using properties specified in the context.
 	 *
@@ -55,6 +57,7 @@ public final class HtmlParserSession extends XmlParserSession {
 	 */
 	protected HtmlParserSession(HtmlParser ctx, ParserSessionArgs args) {
 		super(ctx, args);
+		this.ctx = ctx;
 	}
 
 	@Override /* ParserSession */
@@ -120,7 +123,7 @@ public final class HtmlParserSession extends XmlParserSession {
 			throw new ParseException(this, "Unexpected end of stream in parseAnything for type ''{0}''", eType);
 
 		// Handle @Html(asXml=true) beans.
-		HtmlClassMeta hcm = sType.getExtendedMeta(HtmlClassMeta.class);
+		HtmlClassMeta hcm = getHtmlClassMeta(sType);
 		if (hcm.getFormat() == HtmlFormat.XML)
 			return super.parseAnything(eType, null, r, outer, false, pMeta);
 
@@ -154,8 +157,7 @@ public final class HtmlParserSession extends XmlParserSession {
 			else
 				isValid = false;
 
-		} else if (tag == STRING || (tag == A && pMeta != null
-				&& pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).getLink() != null)) {
+		} else if (tag == STRING || (tag == A && pMeta != null && getHtmlBeanPropertyMeta(pMeta).getLink() != null)) {
 			String text = getElementText(r);
 			if (sType.isObject() || sType.isCharSequence())
 				o = text;
@@ -738,6 +740,30 @@ public final class HtmlParserSession extends XmlParserSession {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * Returns the language-specific metadata on the specified class.
+	 *
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected HtmlClassMeta getHtmlClassMeta(ClassMeta<?> cm) {
+		return ctx.getHtmlClassMeta(cm);
+	}
+
+	/**
+	 * Returns the language-specific metadata on the specified bean property.
+	 *
+	 * @param bpm The bean property to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected HtmlBeanPropertyMeta getHtmlBeanPropertyMeta(BeanPropertyMeta bpm) {
+		return ctx.getHtmlBeanPropertyMeta(bpm);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Other methods
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializer.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializer.java
index b41435d..1ba0e2f 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializer.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializer.java
@@ -13,6 +13,7 @@
 package org.apache.juneau.html;
 
 import java.util.*;
+import java.util.concurrent.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
@@ -124,7 +125,7 @@ import org.apache.juneau.xml.*;
  * </p>
  */
 @ConfigurableContext
-public class HtmlSerializer extends XmlSerializer {
+public class HtmlSerializer extends XmlSerializer implements HtmlMetaProvider {
 
 	//-------------------------------------------------------------------------------------------------------------------
 	// Configurable properties
@@ -635,6 +636,8 @@ public class HtmlSerializer extends XmlSerializer {
 		addKeyValueTableHeaders,
 		addBeanTypes;
 	private final String labelParameter;
+	private final Map<ClassMeta<?>,HtmlClassMeta> htmlClassMetas = new ConcurrentHashMap<>();
+	private final Map<BeanPropertyMeta,HtmlBeanPropertyMeta> htmlBeanPropertyMetas = new ConcurrentHashMap<>();
 
 	private volatile HtmlSchemaSerializer schemaSerializer;
 
@@ -724,6 +727,32 @@ public class HtmlSerializer extends XmlSerializer {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Override /* HtmlMetaProvider */
+	public HtmlClassMeta getHtmlClassMeta(ClassMeta<?> cm) {
+		HtmlClassMeta m = htmlClassMetas.get(cm);
+		if (m == null) {
+			m = new HtmlClassMeta(cm, this);
+			htmlClassMetas.put(cm, m);
+		}
+		return m;
+	}
+
+	@Override /* HtmlMetaProvider */
+	public HtmlBeanPropertyMeta getHtmlBeanPropertyMeta(BeanPropertyMeta bpm) {
+		if (bpm == null)
+			return HtmlBeanPropertyMeta.DEFAULT;
+		HtmlBeanPropertyMeta m = htmlBeanPropertyMetas.get(bpm);
+		if (m == null) {
+			m = new HtmlBeanPropertyMeta(bpm.getDelegateFor(), this);
+			htmlBeanPropertyMetas.put(bpm, m);
+		}
+		return m;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Properties
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
index 7213563..fff755e 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
@@ -198,7 +198,7 @@ public class HtmlSerializerSession extends XmlSerializerSession {
 				type = getClassMetaForObject(o);
 		}
 
-		HtmlClassMeta cHtml = cHtml(type);
+		HtmlClassMeta cHtml = getHtmlClassMeta(type);
 
 		if (type.isMapOrBean() && ! cHtml.isXml())
 			return serializeAnything(out, o, eType, elementName, pMeta, 0, false, false);
@@ -290,8 +290,8 @@ public class HtmlSerializerSession extends XmlSerializerSession {
 				return ContentResult.CR_MIXED;
 			}
 
-			HtmlClassMeta cHtml = cHtml(sType);
-			HtmlBeanPropertyMeta bpHtml = bpHtml(pMeta);
+			HtmlClassMeta cHtml = getHtmlClassMeta(sType);
+			HtmlBeanPropertyMeta bpHtml = getHtmlBeanPropertyMeta(pMeta);
 
 			HtmlRender render = firstNonNull(bpHtml.getRender(), cHtml.getRender());
 
@@ -388,8 +388,8 @@ public class HtmlSerializerSession extends XmlSerializerSession {
 		ClassMeta<?> keyType = eKeyType == null ? string() : eKeyType;
 		ClassMeta<?> valueType = eValueType == null ? object() : eValueType;
 		ClassMeta<?> aType = getClassMetaForObject(m);       // The actual type
-		HtmlClassMeta cHtml = cHtml(aType);
-		HtmlBeanPropertyMeta bpHtml = bpHtml(ppMeta);
+		HtmlClassMeta cHtml = getHtmlClassMeta(aType);
+		HtmlBeanPropertyMeta bpHtml = getHtmlBeanPropertyMeta(ppMeta);
 
 		int i = indent;
 
@@ -445,8 +445,8 @@ public class HtmlSerializerSession extends XmlSerializerSession {
 
 	private void serializeBeanMap(XmlWriter out, BeanMap<?> m, ClassMeta<?> eType, BeanPropertyMeta ppMeta) throws IOException, SerializeException {
 
-		HtmlClassMeta cHtml = cHtml(m.getClassMeta());
-		HtmlBeanPropertyMeta bpHtml = bpHtml(ppMeta);
+		HtmlClassMeta cHtml = getHtmlClassMeta(m.getClassMeta());
+		HtmlBeanPropertyMeta bpHtml = getHtmlBeanPropertyMeta(ppMeta);
 
 		int i = indent;
 
@@ -519,8 +519,8 @@ public class HtmlSerializerSession extends XmlSerializerSession {
 	@SuppressWarnings({ "rawtypes", "unchecked" })
 	private void serializeCollection(XmlWriter out, Object in, ClassMeta<?> sType, ClassMeta<?> eType, String name, BeanPropertyMeta ppMeta) throws IOException, SerializeException {
 
-		HtmlClassMeta cHtml = cHtml(sType);
-		HtmlBeanPropertyMeta bpHtml = bpHtml(ppMeta);
+		HtmlClassMeta cHtml = getHtmlClassMeta(sType);
+		HtmlBeanPropertyMeta bpHtml = getHtmlBeanPropertyMeta(ppMeta);
 
 		Collection c = (sType.isCollection() ? (Collection)in : toList(sType.getInnerClass(), in));
 
@@ -667,37 +667,29 @@ public class HtmlSerializerSession extends XmlSerializerSession {
 		}
 	}
 
-	private static HtmlRender<?> getRender(HtmlSerializerSession session, BeanPropertyMeta pMeta, Object value) {
+	private HtmlRender<?> getRender(HtmlSerializerSession session, BeanPropertyMeta pMeta, Object value) {
 		if (pMeta == null)
 			return null;
-		HtmlRender<?> render = bpHtml(pMeta).getRender();
+		HtmlRender<?> render = getHtmlBeanPropertyMeta(pMeta).getRender();
 		if (render != null)
 			return render;
 		ClassMeta<?> cMeta = session.getClassMetaForObject(value);
-		render = cMeta == null ? null : cHtml(cMeta).getRender();
+		render = cMeta == null ? null : getHtmlClassMeta(cMeta).getRender();
 		return render;
 	}
 
 	@SuppressWarnings({"rawtypes","unchecked"})
-	private static String getStyle(HtmlSerializerSession session, BeanPropertyMeta pMeta, Object value) {
+	private String getStyle(HtmlSerializerSession session, BeanPropertyMeta pMeta, Object value) {
 		HtmlRender render = getRender(session, pMeta, value);
 		return render == null ? null : render.getStyle(session, value);
 	}
 
-	private static String getLink(BeanPropertyMeta pMeta) {
-		return pMeta == null ? null : pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).getLink();
+	private String getLink(BeanPropertyMeta pMeta) {
+		return pMeta == null ? null : getHtmlBeanPropertyMeta(pMeta).getLink();
 	}
 
-	private static String getAnchorText(BeanPropertyMeta pMeta) {
-		return pMeta == null ? null : pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).getAnchorText();
-	}
-
-	private static HtmlClassMeta cHtml(ClassMeta<?> cm) {
-		return cm.getExtendedMeta(HtmlClassMeta.class);
-	}
-
-	private static HtmlBeanPropertyMeta bpHtml(BeanPropertyMeta pMeta) {
-		return pMeta == null ? HtmlBeanPropertyMeta.DEFAULT : pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class);
+	private String getAnchorText(BeanPropertyMeta pMeta) {
+		return pMeta == null ? null : getHtmlBeanPropertyMeta(pMeta).getAnchorText();
 	}
 
 	/*
@@ -733,7 +725,7 @@ public class HtmlSerializerSession extends XmlSerializerSession {
 		if (cm.getInnerClass().isAnnotationPresent(HtmlLink.class))
 			return null;
 
-		HtmlClassMeta cHtml = cHtml(cm);
+		HtmlClassMeta cHtml = getHtmlClassMeta(cm);
 
 		if (cHtml.isNoTables() || bpHtml.isNoTables())
 			return null;
@@ -897,6 +889,30 @@ public class HtmlSerializerSession extends XmlSerializerSession {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * Returns the language-specific metadata on the specified class.
+	 *
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected HtmlClassMeta getHtmlClassMeta(ClassMeta<?> cm) {
+		return ctx.getHtmlClassMeta(cm);
+	}
+
+	/**
+	 * Returns the language-specific metadata on the specified bean property.
+	 *
+	 * @param bpm The bean property to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected HtmlBeanPropertyMeta getHtmlBeanPropertyMeta(BeanPropertyMeta bpm) {
+		return ctx.getHtmlBeanPropertyMeta(bpm);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Other methods
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonClassMeta.java
index 34fa895..11d6aa2 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonClassMeta.java
@@ -21,7 +21,7 @@ import org.apache.juneau.json.annotation.*;
  * Metadata on classes specific to the JSON serializers and parsers pulled from the {@link Json @Json} annotation on
  * the class.
  */
-public class JsonClassMeta extends ClassMetaExtended {
+public class JsonClassMeta extends ExtendedClassMeta {
 
 	private final Json json;
 	private final String wrapperAttr;
@@ -30,8 +30,9 @@ public class JsonClassMeta extends ClassMetaExtended {
 	 * Constructor.
 	 *
 	 * @param cm The class that this annotation is defined on.
+	 * @param jsonMetaProvider JSON metadata provider (for finding information about other artifacts).
 	 */
-	public JsonClassMeta(ClassMeta<?> cm) {
+	public JsonClassMeta(ClassMeta<?> cm, JsonMetaProvider jsonMetaProvider) {
 		super(cm);
 		this.json = cm.getInfo().getAnnotation(Json.class);
 		if (json != null) {
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonMetaProvider.java
similarity index 65%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonMetaProvider.java
index f4c2eab..9d28c35 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonMetaProvider.java
@@ -10,35 +10,20 @@
 // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
 // * specific language governing permissions and limitations under the License.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau;
+package org.apache.juneau.json;
+
+import org.apache.juneau.*;
 
 /**
- * Defines extended language-specific metadata associated with a bean.
- *
- * <p>
- * Serializers and parsers can implement their own language-specific metadata on a bean and retrieve the metadata using
- * the {@link BeanMeta#getExtendedMeta(Class)} method.
+ * Interface for providing access to {@link JsonClassMeta} objects.
  */
-public class BeanMetaExtended {
-
-	private final BeanMeta<?> bm;
-
-	/**
-	 * Constructor.
-	 *
-	 * @param bm The metadata of the bean we're extending.
-	 * @throws BeanRuntimeException If any error occurred trying to construct the metadata.
-	 */
-	public BeanMetaExtended(BeanMeta<?> bm) throws BeanRuntimeException {
-		this.bm = bm;
-	}
+public interface JsonMetaProvider {
 
 	/**
-	 * Returns the bean metadata that was passed into the constructor.
+	 * Returns the language-specific metadata on the specified class.
 	 *
-	 * @return The bean metadata that was passed into the constructor.
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
 	 */
-	protected BeanMeta<?> getBeanMeta() {
-		return bm;
-	}
+	JsonClassMeta getJsonClassMeta(ClassMeta<?> cm);
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParser.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParser.java
index 2aa051d..4272382 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParser.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParser.java
@@ -12,6 +12,9 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.json;
 
+import java.util.*;
+import java.util.concurrent.*;
+
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.parser.*;
@@ -100,7 +103,7 @@ import org.apache.juneau.parser.*;
  * The end result should be the same.
  */
 @ConfigurableContext
-public class JsonParser extends ReaderParser {
+public class JsonParser extends ReaderParser implements JsonMetaProvider {
 
 	//-------------------------------------------------------------------------------------------------------------------
 	// Configurable properties
@@ -196,6 +199,7 @@ public class JsonParser extends ReaderParser {
 	//-------------------------------------------------------------------------------------------------------------------
 
 	private final boolean validateEnd;
+	private final Map<ClassMeta<?>,JsonClassMeta> jsonClassMetas = new ConcurrentHashMap<>();
 
 	/**
 	 * Constructor.
@@ -249,6 +253,20 @@ public class JsonParser extends ReaderParser {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Override /* JsonMetaProvider */
+	public JsonClassMeta getJsonClassMeta(ClassMeta<?> cm) {
+		JsonClassMeta m = jsonClassMetas.get(cm);
+		if (m == null) {
+			m = new JsonClassMeta(cm, this);
+			jsonClassMetas.put(cm, m);
+		}
+		return m;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Properties
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
index 27faae0..a3e38cf 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
@@ -125,11 +125,11 @@ public final class JsonParserSession extends ReaderParserSession {
 		else
 			sType = eType;
 
-		if (sType.isOptional()) 
+		if (sType.isOptional())
 			return (T)Optional.ofNullable(parseAnything(eType.getElementType(), r, outer, pMeta));
 
 		setCurrentClass(sType);
-		String wrapperAttr = sType.getExtendedMeta(JsonClassMeta.class).getWrapperAttr();
+		String wrapperAttr = getJsonClassMeta(sType).getWrapperAttr();
 
 		Object o = null;
 
@@ -801,6 +801,20 @@ public final class JsonParserSession extends ReaderParserSession {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * Returns the language-specific metadata on the specified class.
+	 *
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected JsonClassMeta getJsonClassMeta(ClassMeta<?> cm) {
+		return ctx.getJsonClassMeta(cm);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Other methods
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java
index 5203ae9..1ec212a 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java
@@ -12,6 +12,9 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.json;
 
+import java.util.*;
+import java.util.concurrent.*;
+
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.jsonschema.*;
@@ -31,7 +34,7 @@ import org.apache.juneau.serializer.*;
  * Produces the JSON-schema for the JSON produced by the {@link JsonSerializer} class with the same properties.
  */
 @ConfigurableContext
-public class JsonSchemaSerializer extends JsonSerializer {
+public class JsonSchemaSerializer extends JsonSerializer implements JsonSchemaMetaProvider {
 
 	//-------------------------------------------------------------------------------------------------------------------
 	// Configurable properties
@@ -118,6 +121,8 @@ public class JsonSchemaSerializer extends JsonSerializer {
 	//-------------------------------------------------------------------------------------------------------------------
 
 	private final JsonSchemaGenerator generator;
+	private final Map<ClassMeta<?>,JsonSchemaClassMeta> jsonSchemaClassMetas = new ConcurrentHashMap<>();
+	private final Map<BeanPropertyMeta,JsonSchemaBeanPropertyMeta> jsonSchemaBeanPropertyMetas = new ConcurrentHashMap<>();
 
 	/**
 	 * Constructor.
@@ -168,6 +173,30 @@ public class JsonSchemaSerializer extends JsonSerializer {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Override /* JsonSchemaMetaProvider */
+	public JsonSchemaClassMeta getJsonSchemaClassMeta(ClassMeta<?> cm) {
+		JsonSchemaClassMeta m = jsonSchemaClassMetas.get(cm);
+		if (m == null) {
+			m = new JsonSchemaClassMeta(cm, this);
+			jsonSchemaClassMetas.put(cm, m);
+		}
+		return m;
+	}
+
+	@Override /* JsonSchemaMetaProvider */
+	public JsonSchemaBeanPropertyMeta getJsonSchemaBeanPropertyMeta(BeanPropertyMeta bpm) {
+		JsonSchemaBeanPropertyMeta m = jsonSchemaBeanPropertyMetas.get(bpm);
+		if (m == null) {
+			m = new JsonSchemaBeanPropertyMeta(bpm.getDelegateFor(), this);
+			jsonSchemaBeanPropertyMetas.put(bpm, m);
+		}
+		return m;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Other methods
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java
index ff17767..1e24f6e 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java
@@ -28,6 +28,7 @@ import org.apache.juneau.serializer.*;
 public class JsonSchemaSerializerSession extends JsonSerializerSession {
 
 	private final JsonSchemaGeneratorSession genSession;
+	private final JsonSchemaSerializer ctx;
 
 	/**
 	 * Create a new session using properties specified in the context.
@@ -44,6 +45,7 @@ public class JsonSchemaSerializerSession extends JsonSerializerSession {
 	protected JsonSchemaSerializerSession(JsonSchemaSerializer ctx, SerializerSessionArgs args) {
 		super(ctx, args);
 		genSession = ctx.getGenerator().createSession(args);
+		this.ctx = ctx;
 	}
 
 	@Override /* SerializerSession */
@@ -56,6 +58,30 @@ public class JsonSchemaSerializerSession extends JsonSerializerSession {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * Returns the language-specific metadata on the specified class.
+	 *
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected JsonSchemaClassMeta getJsonSchemaClassMeta(ClassMeta<?> cm) {
+		return ctx.getJsonSchemaClassMeta(cm);
+	}
+
+	/**
+	 * Returns the language-specific metadata on the specified bean property.
+	 *
+	 * @param bpm The bean property to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected JsonSchemaBeanPropertyMeta getJsonSchemaBeanPropertyMeta(BeanPropertyMeta bpm) {
+		return ctx.getJsonSchemaBeanPropertyMeta(bpm);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Other methods
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializer.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializer.java
index ebcebb8..da53881 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializer.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializer.java
@@ -13,6 +13,7 @@
 package org.apache.juneau.json;
 
 import java.util.*;
+import java.util.concurrent.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
@@ -88,7 +89,7 @@ import org.apache.juneau.serializer.*;
  * </p>
  */
 @ConfigurableContext
-public class JsonSerializer extends WriterSerializer {
+public class JsonSerializer extends WriterSerializer implements JsonMetaProvider {
 
 	//-------------------------------------------------------------------------------------------------------------------
 	// Configurable properties
@@ -312,6 +313,7 @@ public class JsonSerializer extends WriterSerializer {
 		simpleMode,
 		escapeSolidus,
 		addBeanTypes;
+	private final Map<ClassMeta<?>,JsonClassMeta> jsonClassMetas = new ConcurrentHashMap<>();
 
 	private volatile JsonSchemaSerializer schemaSerializer;
 
@@ -406,6 +408,20 @@ public class JsonSerializer extends WriterSerializer {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Override /* JsonMetaProvider */
+	public JsonClassMeta getJsonClassMeta(ClassMeta<?> cm) {
+		JsonClassMeta m = jsonClassMetas.get(cm);
+		if (m == null) {
+			m = new JsonClassMeta(cm, this);
+			jsonClassMetas.put(cm, m);
+		}
+		return m;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Properties
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
index 3ed2bc2..f8ce995 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
@@ -127,7 +127,7 @@ public class JsonSerializerSession extends WriterSerializerSession {
 				sType = getClassMetaForObject(o);
 		}
 
-		String wrapperAttr = sType.getExtendedMeta(JsonClassMeta.class).getWrapperAttr();
+		String wrapperAttr = getJsonClassMeta(sType).getWrapperAttr();
 		if (wrapperAttr != null) {
 			out.append('{').cr(indent).attr(wrapperAttr).append(':').s(indent);
 			indent++;
@@ -308,6 +308,20 @@ public class JsonSerializerSession extends WriterSerializerSession {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * Returns the language-specific metadata on the specified class.
+	 *
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected JsonClassMeta getJsonClassMeta(ClassMeta<?> cm) {
+		return ctx.getJsonClassMeta(cm);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Other methods
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaBeanPropertyMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaBeanPropertyMeta.java
index 6d6f724..b3ebb91 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaBeanPropertyMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaBeanPropertyMeta.java
@@ -22,7 +22,7 @@ import org.apache.juneau.parser.*;
  * Metadata on bean properties specific to the JSON-Schema pulled from the {@link Schema @Schema} annotation
  * on the bean property.
  */
-public class JsonSchemaBeanPropertyMeta extends BeanPropertyMetaExtended {
+public class JsonSchemaBeanPropertyMeta extends ExtendedBeanPropertyMeta {
 
 	/**
 	 * Default instance.
@@ -35,8 +35,9 @@ public class JsonSchemaBeanPropertyMeta extends BeanPropertyMetaExtended {
 	 * Constructor.
 	 *
 	 * @param bpm The metadata of the bean property of this additional metadata.
+	 * @param jsonSchemaMetaProvider JSON-schema metadata provider (for finding information about other artifacts).
 	 */
-	public JsonSchemaBeanPropertyMeta(BeanPropertyMeta bpm) {
+	public JsonSchemaBeanPropertyMeta(BeanPropertyMeta bpm, JsonSchemaMetaProvider jsonSchemaMetaProvider) {
 		super(bpm);
 
 		this.schema = new ObjectMap();
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaClassMeta.java
index 2bc6446..2f7b9c8 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaClassMeta.java
@@ -14,12 +14,13 @@ package org.apache.juneau.jsonschema;
 
 import org.apache.juneau.*;
 import org.apache.juneau.jsonschema.annotation.*;
+import org.apache.juneau.parser.*;
 
 /**
  * Metadata on classes specific to the JSON-Schema serializer and pulled from the {@link Schema @Schema} annotation on
  * the class.
  */
-public class JsonSchemaClassMeta extends ClassMetaExtended {
+public class JsonSchemaClassMeta extends ExtendedClassMeta {
 
 	private final ObjectMap schema;
 
@@ -27,12 +28,16 @@ public class JsonSchemaClassMeta extends ClassMetaExtended {
 	 * Constructor.
 	 *
 	 * @param cm The class that this annotation is defined on.
-	 * @throws Exception If invalid <ja>@Schema</ja> definition was encountered.
+	 * @param jsonSchemaMetaProvider JSON-schema metadata provider (for finding information about other artifacts).
 	 */
-	public JsonSchemaClassMeta(ClassMeta<?> cm) throws Exception {
+	public JsonSchemaClassMeta(ClassMeta<?> cm, JsonSchemaMetaProvider jsonSchemaMetaProvider) {
 		super(cm);
-		Schema s = cm.getInfo().getAnnotation(Schema.class);
-		schema = s == null ? ObjectMap.EMPTY_MAP : SchemaUtils.asMap(s);
+		try {
+			Schema s = cm.getInfo().getAnnotation(Schema.class);
+			schema = s == null ? ObjectMap.EMPTY_MAP : SchemaUtils.asMap(s);
+		} catch (ParseException e) {
+			throw new RuntimeException(e);
+		}
 	}
 
 	/**
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaGenerator.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaGenerator.java
index 3e34f7a..a1d8b2b 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaGenerator.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaGenerator.java
@@ -15,6 +15,7 @@ package org.apache.juneau.jsonschema;
 import static org.apache.juneau.internal.StringUtils.*;
 
 import java.util.*;
+import java.util.concurrent.*;
 import java.util.regex.*;
 
 import org.apache.juneau.*;
@@ -25,7 +26,7 @@ import org.apache.juneau.json.*;
  * Generates JSON-schema metadata about POJOs.
  */
 @ConfigurableContext
-public class JsonSchemaGenerator extends BeanTraverseContext {
+public class JsonSchemaGenerator extends BeanTraverseContext implements JsonSchemaMetaProvider {
 
 	//-------------------------------------------------------------------------------------------------------------------
 	// Configurable properties
@@ -282,6 +283,8 @@ public class JsonSchemaGenerator extends BeanTraverseContext {
 	private final Map<String,ObjectMap> defaultSchemas;
 	private final JsonSerializer jsonSerializer;
 	private final Set<Pattern> ignoreTypes;
+	private final Map<ClassMeta<?>,JsonSchemaClassMeta> jsonSchemaClassMetas = new ConcurrentHashMap<>();
+	private final Map<BeanPropertyMeta,JsonSchemaBeanPropertyMeta> jsonSchemaBeanPropertyMetas = new ConcurrentHashMap<>();
 
 	/**
 	 * Constructor.
@@ -431,6 +434,30 @@ public class JsonSchemaGenerator extends BeanTraverseContext {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Override
+	public JsonSchemaClassMeta getJsonSchemaClassMeta(ClassMeta<?> cm) {
+		JsonSchemaClassMeta m = jsonSchemaClassMetas.get(cm);
+		if (m == null) {
+			m = new JsonSchemaClassMeta(cm, this);
+			jsonSchemaClassMetas.put(cm, m);
+		}
+		return m;
+	}
+
+	@Override
+	public JsonSchemaBeanPropertyMeta getJsonSchemaBeanPropertyMeta(BeanPropertyMeta bpm) {
+		JsonSchemaBeanPropertyMeta m = jsonSchemaBeanPropertyMetas.get(bpm);
+		if (m == null) {
+			m = new JsonSchemaBeanPropertyMeta(bpm, this);
+			jsonSchemaBeanPropertyMetas.put(bpm, m);
+		}
+		return m;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Other methods
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaGeneratorSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaGeneratorSession.java
index b4ea353..87780e7 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaGeneratorSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaGeneratorSession.java
@@ -140,9 +140,9 @@ public class JsonSchemaGeneratorSession extends BeanTraverseSession {
 
 		JsonSchemaClassMeta jscm = null;
 		if (pojoSwap != null && pojoSwap.getClass().getAnnotation(Schema.class) != null)
-			jscm = getClassMeta(pojoSwap.getClass()).getExtendedMeta(JsonSchemaClassMeta.class);
+			jscm = getJsonSchemaClassMeta(getClassMeta(pojoSwap.getClass()));
 		if (jscm == null)
-			jscm = sType.getExtendedMeta(JsonSchemaClassMeta.class);
+			jscm = getJsonSchemaClassMeta(sType);
 
 		TypeCategory tc = null;
 
@@ -220,7 +220,7 @@ public class JsonSchemaGeneratorSession extends BeanTraverseSession {
 				for (Iterator<BeanPropertyMeta> i = bm.getPropertyMetas().iterator(); i.hasNext();) {
 					BeanPropertyMeta p = i.next();
 					if (p.canRead())
-						properties.put(p.getName(), getSchema(p.getClassMeta(), p.getName(), p.getProperties(), exampleAdded, descriptionAdded, p.getExtendedMeta(JsonSchemaBeanPropertyMeta.class)));
+						properties.put(p.getName(), getSchema(p.getClassMeta(), p.getName(), p.getProperties(), exampleAdded, descriptionAdded, getJsonSchemaBeanPropertyMeta(p)));
 				}
 				out.put("properties", properties);
 
@@ -449,6 +449,30 @@ public class JsonSchemaGeneratorSession extends BeanTraverseSession {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * Returns the language-specific metadata on the specified class.
+	 *
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
+	 */
+	public JsonSchemaClassMeta getJsonSchemaClassMeta(ClassMeta<?> cm) {
+		return ctx.getJsonSchemaClassMeta(cm);
+	}
+
+	/**
+	 * Returns the language-specific metadata on the specified bean property.
+	 *
+	 * @param bpm The bean property to return the metadata on.
+	 * @return The metadata.
+	 */
+	public JsonSchemaBeanPropertyMeta getJsonSchemaBeanPropertyMeta(BeanPropertyMeta bpm) {
+		return ctx.getJsonSchemaBeanPropertyMeta(bpm);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Utility methods
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaMetaProvider.java
similarity index 66%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaMetaProvider.java
index f4c2eab..9f34b9e 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaMetaProvider.java
@@ -10,35 +10,28 @@
 // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
 // * specific language governing permissions and limitations under the License.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau;
+package org.apache.juneau.jsonschema;
+
+import org.apache.juneau.*;
 
 /**
- * Defines extended language-specific metadata associated with a bean.
- *
- * <p>
- * Serializers and parsers can implement their own language-specific metadata on a bean and retrieve the metadata using
- * the {@link BeanMeta#getExtendedMeta(Class)} method.
+ * Interface for providing access to {@link JsonSchemaClassMeta} and {@link JsonSchemaBeanPropertyMeta} objects.
  */
-public class BeanMetaExtended {
-
-	private final BeanMeta<?> bm;
+public interface JsonSchemaMetaProvider {
 
 	/**
-	 * Constructor.
+	 * Returns the language-specific metadata on the specified class.
 	 *
-	 * @param bm The metadata of the bean we're extending.
-	 * @throws BeanRuntimeException If any error occurred trying to construct the metadata.
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
 	 */
-	public BeanMetaExtended(BeanMeta<?> bm) throws BeanRuntimeException {
-		this.bm = bm;
-	}
+	JsonSchemaClassMeta getJsonSchemaClassMeta(ClassMeta<?> cm);
 
 	/**
-	 * Returns the bean metadata that was passed into the constructor.
+	 * Returns the language-specific metadata on the specified bean property.
 	 *
-	 * @return The bean metadata that was passed into the constructor.
+	 * @param bpm The bean property to return the metadata on.
+	 * @return The metadata.
 	 */
-	protected BeanMeta<?> getBeanMeta() {
-		return bm;
-	}
+	JsonSchemaBeanPropertyMeta getJsonSchemaBeanPropertyMeta(BeanPropertyMeta bpm);
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingClassMeta.java
index 6f3e1bb..0b4de2d 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingClassMeta.java
@@ -18,7 +18,7 @@ import org.apache.juneau.urlencoding.annotation.*;
 /**
  * Metadata on classes specific to the URL-Encoding serializers and parsers pulled from the {@link UrlEncoding @UrlEncoding} annotation on the class.
  */
-public class UrlEncodingClassMeta extends ClassMetaExtended {
+public class UrlEncodingClassMeta extends ExtendedClassMeta {
 
 	private final UrlEncoding urlEncoding;
 	private final boolean expandedParams;
@@ -27,8 +27,9 @@ public class UrlEncodingClassMeta extends ClassMetaExtended {
 	 * Constructor.
 	 *
 	 * @param cm The class that this annotation is defined on.
+	 * @param urlEncodingMetaProvider URL-encoding metadata provider (for finding information about other artifacts).
 	 */
-	public UrlEncodingClassMeta(ClassMeta<?> cm) {
+	public UrlEncodingClassMeta(ClassMeta<?> cm, UrlEncodingMetaProvider urlEncodingMetaProvider) {
 		super(cm);
 		this.urlEncoding = cm.getInfo().getAnnotation(UrlEncoding.class);
 		if (urlEncoding != null) {
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingMetaProvider.java
similarity index 65%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingMetaProvider.java
index f4c2eab..e7d5e2c 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingMetaProvider.java
@@ -10,35 +10,20 @@
 // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
 // * specific language governing permissions and limitations under the License.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau;
+package org.apache.juneau.urlencoding;
+
+import org.apache.juneau.*;
 
 /**
- * Defines extended language-specific metadata associated with a bean.
- *
- * <p>
- * Serializers and parsers can implement their own language-specific metadata on a bean and retrieve the metadata using
- * the {@link BeanMeta#getExtendedMeta(Class)} method.
+ * Interface for providing access to {@link UrlEncodingClassMeta} objects.
  */
-public class BeanMetaExtended {
-
-	private final BeanMeta<?> bm;
-
-	/**
-	 * Constructor.
-	 *
-	 * @param bm The metadata of the bean we're extending.
-	 * @throws BeanRuntimeException If any error occurred trying to construct the metadata.
-	 */
-	public BeanMetaExtended(BeanMeta<?> bm) throws BeanRuntimeException {
-		this.bm = bm;
-	}
+public interface UrlEncodingMetaProvider {
 
 	/**
-	 * Returns the bean metadata that was passed into the constructor.
+	 * Returns the language-specific metadata on the specified class.
 	 *
-	 * @return The bean metadata that was passed into the constructor.
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
 	 */
-	protected BeanMeta<?> getBeanMeta() {
-		return bm;
-	}
+	UrlEncodingClassMeta getUrlEncodingClassMeta(ClassMeta<?> cm);
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java
index df5e6c3..0efa945 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java
@@ -12,6 +12,9 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.urlencoding;
 
+import java.util.*;
+import java.util.concurrent.*;
+
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.parser.*;
@@ -36,7 +39,7 @@ import org.apache.juneau.urlencoding.annotation.*;
  * This parser uses a state machine, which makes it very fast and efficient.
  */
 @ConfigurableContext
-public class UrlEncodingParser extends UonParser {
+public class UrlEncodingParser extends UonParser implements UrlEncodingMetaProvider {
 
 	//-------------------------------------------------------------------------------------------------------------------
 	// Configurable properties
@@ -110,6 +113,7 @@ public class UrlEncodingParser extends UonParser {
 	//-------------------------------------------------------------------------------------------------------------------
 
 	private final boolean expandedParams;
+	private final Map<ClassMeta<?>,UrlEncodingClassMeta> urlEncodingClassMetas = new ConcurrentHashMap<>();
 
 	/**
 	 * Constructor.
@@ -163,6 +167,20 @@ public class UrlEncodingParser extends UonParser {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Override /* UrlEncodingMetaProvider */
+	public UrlEncodingClassMeta getUrlEncodingClassMeta(ClassMeta<?> cm) {
+		UrlEncodingClassMeta m = urlEncodingClassMetas.get(cm);
+		if (m == null) {
+			m = new UrlEncodingClassMeta(cm, this);
+			urlEncodingClassMetas.put(cm, m);
+		}
+		return m;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Properties
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java
index 5bbc18d..5e63330 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java
@@ -59,7 +59,7 @@ public class UrlEncodingParserSession extends UonParserSession {
 		if (cm.isCollectionOrArray()) {
 			if (isExpandedParams())
 				return true;
-			if (pMeta.getBeanMeta().getClassMeta().getExtendedMeta(UrlEncodingClassMeta.class).isExpandedParams())
+			if (getUrlEncodingClassMeta(pMeta.getBeanMeta().getClassMeta()).isExpandedParams())
 				return true;
 		}
 		return false;
@@ -95,8 +95,8 @@ public class UrlEncodingParserSession extends UonParserSession {
 			sType = swap.getSwapClassMeta(this);
 		else
 			sType = eType;
-		
-		if (sType.isOptional()) 
+
+		if (sType.isOptional())
 			return (T)Optional.ofNullable(parseAnything(eType.getElementType(), r, outer));
 
 		int c = r.peekSkipWs();
@@ -372,6 +372,20 @@ public class UrlEncodingParserSession extends UonParserSession {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * Returns the language-specific metadata on the specified class.
+	 *
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected UrlEncodingClassMeta getUrlEncodingClassMeta(ClassMeta<?> cm) {
+		return ctx.getUrlEncodingClassMeta(cm);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Other methods
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
index 1741ccb..61bfd92 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
@@ -12,6 +12,9 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.urlencoding;
 
+import java.util.*;
+import java.util.concurrent.*;
+
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.serializer.*;
@@ -112,7 +115,7 @@ import org.apache.juneau.uon.*;
  * </p>
  */
 @ConfigurableContext
-public class UrlEncodingSerializer extends UonSerializer {
+public class UrlEncodingSerializer extends UonSerializer implements UrlEncodingMetaProvider {
 
 	//-------------------------------------------------------------------------------------------------------------------
 	// Configurable properties
@@ -249,6 +252,7 @@ public class UrlEncodingSerializer extends UonSerializer {
 
 	private final boolean
 		expandedParams;
+	private final Map<ClassMeta<?>,UrlEncodingClassMeta> urlEncodingClassMetas = new ConcurrentHashMap<>();
 
 	/**
 	 * Constructor.
@@ -334,6 +338,20 @@ public class UrlEncodingSerializer extends UonSerializer {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Override /* UrlEncodingMetaProvider */
+	public UrlEncodingClassMeta getUrlEncodingClassMeta(ClassMeta<?> cm) {
+		UrlEncodingClassMeta m = urlEncodingClassMetas.get(cm);
+		if (m == null) {
+			m = new UrlEncodingClassMeta(cm, this);
+			urlEncodingClassMetas.put(cm, m);
+		}
+		return m;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Properties
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java
index 10dd525..0d7d786 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java
@@ -62,7 +62,7 @@ public class UrlEncodingSerializerSession extends UonSerializerSession {
 		if (cm.isCollectionOrArray()) {
 			if (isExpandedParams())
 				return true;
-			if (pMeta.getBeanMeta().getClassMeta().getExtendedMeta(UrlEncodingClassMeta.class).isExpandedParams())
+			if (getUrlEncodingClassMeta(pMeta.getBeanMeta().getClassMeta()).isExpandedParams())
 				return true;
 		}
 		return false;
@@ -276,6 +276,20 @@ public class UrlEncodingSerializerSession extends UonSerializerSession {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * Returns the language-specific metadata on the specified class.
+	 *
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected UrlEncodingClassMeta getUrlEncodingClassMeta(ClassMeta<?> cm) {
+		return ctx.getUrlEncodingClassMeta(cm);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Other methods
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MetadataMap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MetadataMap.java
deleted file mode 100644
index 207a36e..0000000
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MetadataMap.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// ***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
-// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
-// * with the License.  You may obtain a copy of the License at                                                              *
-// *                                                                                                                         *
-// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
-// *                                                                                                                         *
-// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
-// * specific language governing permissions and limitations under the License.                                              *
-// ***************************************************************************************************************************
-package org.apache.juneau.utils;
-
-import java.lang.reflect.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.reflect.*;
-
-/**
- * Utility class for quick lookup of class metadata instances.
- *
- * <p>
- * Class instances are created once and then cached.
- *
- * <p>
- * Classes must have a constructor that takes in a single argument.
- */
-public class MetadataMap {
-
-	private Class<?>[] classes = new Class<?>[0];
-	private Object[] metadata = new Object[0];
-
-
-	/**
-	 * Constructor.
-	 *
-	 * @param c The metadata class to create.
-	 * @param constructorArg The argument needed to construct the metadata.
-	 * @return The cached metadata object.
-	 */
-	@SuppressWarnings("unchecked")
-	public <T> T get(Class<T> c, Object constructorArg) {
-		for (int i = 0; i < classes.length; i++)
-			if (classes[i] == c)
-				return (T)metadata[i];
-		synchronized(this) {
-			for (int i = 0; i < classes.length; i++)
-				if (classes[i] == c)
-					return (T)metadata[i];
-			Class<?>[] classes2 = new Class<?>[classes.length + 1];
-			Object[] metadata2 = new Object[classes.length + 1];
-			for (int i = 0; i < classes.length; i++) {
-				classes2[i] = classes[i];
-				metadata2[i] = metadata[i];
-			}
-			Object o = null;
-			try {
-				for (Constructor<?> con : c.getConstructors()) {
-					Class<?>[] pt = con.getParameterTypes();
-					if (pt.length == 1 && ClassInfo.of(pt[0]).isParentOf(constructorArg.getClass())) {
-						o = con.newInstance(constructorArg);
-						break;
-					}
-				}
-			} catch (InvocationTargetException e) {
-				Throwable t = e.getTargetException();
-				if (t instanceof RuntimeException)
-					throw (RuntimeException)t;
-				throw new RuntimeException(t);
-			} catch (Exception e) {
-				throw new RuntimeException(e);
-			}
-			if (o == null)
-				throw new BeanRuntimeException(c,
-					"Could not find a constructor on class with a parameter to handle type {0}", constructorArg.getClass());
-			classes2[classes.length] = c;
-			metadata2[classes.length] = o;
-			classes = classes2;
-			metadata = metadata2;
-			return (T)o;
-		}
-	}
-}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanMeta.java
index 9fbb27d..9924b2e 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanMeta.java
@@ -24,7 +24,7 @@ import org.apache.juneau.xml.annotation.*;
  * Metadata on beans specific to the XML serializers and parsers pulled from the {@link Xml @Xml} annotation on the
  * class.
  */
-public class XmlBeanMeta extends BeanMetaExtended {
+public class XmlBeanMeta extends ExtendedBeanMeta {
 
 	// XML related fields
 	private final Map<String,BeanPropertyMeta> attrs;                        // Map of bean properties that are represented as XML attributes.
@@ -38,12 +38,13 @@ public class XmlBeanMeta extends BeanMetaExtended {
 	 * Constructor.
 	 *
 	 * @param beanMeta The metadata on the bean that this metadata applies to.
+	 * @param xmlMetaProvider XML metadata provider (for finding information about other artifacts).
 	 */
-	public XmlBeanMeta(BeanMeta<?> beanMeta) {
+	public XmlBeanMeta(BeanMeta<?> beanMeta, XmlMetaProvider xmlMetaProvider) {
 		super(beanMeta);
 
 		Class<?> c = beanMeta.getClassMeta().getInnerClass();
-		XmlBeanMetaBuilder b = new XmlBeanMetaBuilder(beanMeta);
+		XmlBeanMetaBuilder b = new XmlBeanMetaBuilder(beanMeta, xmlMetaProvider);
 
 		attrs = unmodifiableMap(b.attrs);
 		elements = unmodifiableMap(b.elements);
@@ -76,7 +77,7 @@ public class XmlBeanMeta extends BeanMetaExtended {
 			contentProperty;
 		XmlFormat contentFormat = DEFAULT;
 
-		XmlBeanMetaBuilder(BeanMeta<?> beanMeta) {
+		XmlBeanMetaBuilder(BeanMeta<?> beanMeta, XmlMetaProvider xmlMetaProvider) {
 			Class<?> c = beanMeta.getClassMeta().getInnerClass();
 			Xml xml = c.getAnnotation(Xml.class);
 			XmlFormat defaultFormat = null;
@@ -96,7 +97,7 @@ public class XmlBeanMeta extends BeanMetaExtended {
 			}
 
 			for (BeanPropertyMeta p : beanMeta.getPropertyMetas()) {
-				XmlFormat xf = p.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat();
+				XmlFormat xf = xmlMetaProvider.getXmlBeanPropertyMeta(p).getXmlFormat();
 				ClassMeta<?> pcm = p.getClassMeta();
 				if (xf == ATTR) {
 					attrs.put(p.getName(), p);
@@ -127,7 +128,7 @@ public class XmlBeanMeta extends BeanMetaExtended {
 					contentFormat = xf;
 				}
 				// Look for any properties that are collections with @Xml.childName specified.
-				String n = p.getExtendedMeta(XmlBeanPropertyMeta.class).getChildName();
+				String n = xmlMetaProvider.getXmlBeanPropertyMeta(p).getChildName();
 				if (n != null) {
 					if (collapsedProperties.containsKey(n) && collapsedProperties.get(n) != p)
 						throw new BeanRuntimeException(c, "Multiple properties found with the child name ''{0}''.", n);
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java
index a98013f..1a979c4 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java
@@ -21,7 +21,7 @@ import org.apache.juneau.xml.annotation.*;
  * Metadata on bean properties specific to the XML serializers and parsers pulled from the {@link Xml @Xml} annotation
  * on the bean property.
  */
-public class XmlBeanPropertyMeta extends BeanPropertyMetaExtended {
+public class XmlBeanPropertyMeta extends ExtendedBeanPropertyMeta {
 
 	/**
 	 * Default instance.
@@ -31,14 +31,17 @@ public class XmlBeanPropertyMeta extends BeanPropertyMetaExtended {
 	private Namespace namespace = null;
 	private XmlFormat xmlFormat = XmlFormat.DEFAULT;
 	private String childName;
+	private final XmlMetaProvider xmlMetaProvider;
 
 	/**
 	 * Constructor.
 	 *
 	 * @param bpm The metadata of the bean property of this additional metadata.
+	 * @param xmlMetaProvider XML metadata provider (for finding information about other artifacts).
 	 */
-	public XmlBeanPropertyMeta(BeanPropertyMeta bpm) {
+	public XmlBeanPropertyMeta(BeanPropertyMeta bpm, XmlMetaProvider xmlMetaProvider) {
 		super(bpm);
+		this.xmlMetaProvider = xmlMetaProvider;
 
 		if (bpm.getInnerField() != null)
 			findXmlInfo(bpm.getInnerField().getAnnotation(Xml.class));
@@ -48,11 +51,12 @@ public class XmlBeanPropertyMeta extends BeanPropertyMetaExtended {
 			findXmlInfo(bpm.getSetter().getAnnotation(Xml.class));
 
 		if (namespace == null)
-			namespace = bpm.getBeanMeta().getClassMeta().getExtendedMeta(XmlClassMeta.class).getNamespace();
+			namespace = xmlMetaProvider.getXmlClassMeta(bpm.getBeanMeta().getClassMeta()).getNamespace();
 	}
 
 	private XmlBeanPropertyMeta() {
 		super(null);
+		this.xmlMetaProvider = null;
 	}
 
 	/**
@@ -120,8 +124,8 @@ public class XmlBeanPropertyMeta extends BeanPropertyMetaExtended {
 
 		if (xmlFormat == XmlFormat.COLLAPSED) {
 			if (isCollection) {
-				if (cen.isEmpty())
-					cen = cmProperty.getExtendedMeta(XmlClassMeta.class).getChildName();
+				if (cen.isEmpty() && xmlMetaProvider != null)
+					cen = xmlMetaProvider.getXmlClassMeta(cmProperty).getChildName();
 				if (cen == null || cen.isEmpty())
 					cen = cmProperty.getElementType().getDictionaryName();
 				if (cen == null || cen.isEmpty())
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlClassMeta.java
index c6d902f..3d111ee 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlClassMeta.java
@@ -24,7 +24,7 @@ import org.apache.juneau.xml.annotation.*;
  * Metadata on classes specific to the XML serializers and parsers pulled from the {@link Xml @Xml} annotation on the
  * class.
  */
-public class XmlClassMeta extends ClassMetaExtended {
+public class XmlClassMeta extends ExtendedClassMeta {
 
 	private final Namespace namespace;
 	private final Xml xml;
@@ -35,8 +35,9 @@ public class XmlClassMeta extends ClassMetaExtended {
 	 * Constructor.
 	 *
 	 * @param cm The class that this annotation is defined on.
+	 * @param xmlMetaProvider XML metadata provider (for finding information about other artifacts).
 	 */
-	public XmlClassMeta(ClassMeta<?> cm) {
+	public XmlClassMeta(ClassMeta<?> cm, XmlMetaProvider xmlMetaProvider) {
 		super(cm);
 		this.namespace = findNamespace(cm);
 		this.xml = cm.getInfo().getAnnotation(Xml.class);
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlMetaProvider.java
similarity index 65%
rename from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java
rename to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlMetaProvider.java
index f4c2eab..668763a 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMetaExtended.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlMetaProvider.java
@@ -10,35 +10,36 @@
 // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
 // * specific language governing permissions and limitations under the License.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau;
+package org.apache.juneau.xml;
+
+import org.apache.juneau.*;
 
 /**
- * Defines extended language-specific metadata associated with a bean.
- *
- * <p>
- * Serializers and parsers can implement their own language-specific metadata on a bean and retrieve the metadata using
- * the {@link BeanMeta#getExtendedMeta(Class)} method.
+ * Interface for providing access to {@link XmlClassMeta}, {@link XmlBeanMeta}, and {@link XmlBeanPropertyMeta} objects.
  */
-public class BeanMetaExtended {
+public interface XmlMetaProvider {
 
-	private final BeanMeta<?> bm;
+	/**
+	 * Returns the language-specific metadata on the specified class.
+	 *
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
+	 */
+	XmlClassMeta getXmlClassMeta(ClassMeta<?> cm);
 
 	/**
-	 * Constructor.
+	 * Returns the language-specific metadata on the specified bean.
 	 *
-	 * @param bm The metadata of the bean we're extending.
-	 * @throws BeanRuntimeException If any error occurred trying to construct the metadata.
+	 * @param bm The bean to return the metadata on.
+	 * @return The metadata.
 	 */
-	public BeanMetaExtended(BeanMeta<?> bm) throws BeanRuntimeException {
-		this.bm = bm;
-	}
+	XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm);
 
 	/**
-	 * Returns the bean metadata that was passed into the constructor.
+	 * Returns the language-specific metadata on the specified bean property.
 	 *
-	 * @return The bean metadata that was passed into the constructor.
+	 * @param bpm The bean property to return the metadata on.
+	 * @return The metadata.
 	 */
-	protected BeanMeta<?> getBeanMeta() {
-		return bm;
-	}
+	XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm);
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParser.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParser.java
index 642b569..508bdaa 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParser.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParser.java
@@ -12,6 +12,9 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.xml;
 
+import java.util.*;
+import java.util.concurrent.*;
+
 import javax.xml.stream.*;
 import javax.xml.stream.util.*;
 
@@ -31,7 +34,7 @@ import org.apache.juneau.parser.*;
  * See the {@link XmlSerializer} class for a description of Juneau-generated XML.
  */
 @ConfigurableContext
-public class XmlParser extends ReaderParser {
+public class XmlParser extends ReaderParser implements XmlMetaProvider {
 
 	//-------------------------------------------------------------------------------------------------------------------
 	// Configurable properties
@@ -197,6 +200,9 @@ public class XmlParser extends ReaderParser {
 	private final XMLReporter reporter;
 	private final XMLResolver resolver;
 	private final XMLEventAllocator eventAllocator;
+	private final Map<ClassMeta<?>,XmlClassMeta> xmlClassMetas = new ConcurrentHashMap<>();
+	private final Map<BeanMeta<?>,XmlBeanMeta> xmlBeanMetas = new ConcurrentHashMap<>();
+	private final Map<BeanPropertyMeta,XmlBeanPropertyMeta> xmlBeanPropertyMetas = new ConcurrentHashMap<>();
 
 	/**
 	 * Constructor.
@@ -257,6 +263,41 @@ public class XmlParser extends ReaderParser {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Override /* XmlMetaProvider */
+	public XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) {
+		XmlClassMeta m = xmlClassMetas.get(cm);
+		if (m == null) {
+			m = new XmlClassMeta(cm, this);
+			xmlClassMetas.put(cm, m);
+		}
+		return m;
+	}
+
+	@Override /* XmlMetaProvider */
+	public XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) {
+		XmlBeanMeta m = xmlBeanMetas.get(bm);
+		if (m == null) {
+			m = new XmlBeanMeta(bm, this);
+			xmlBeanMetas.put(bm, m);
+		}
+		return m;
+	}
+
+	@Override /* XmlMetaProvider */
+	public XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
+		XmlBeanPropertyMeta m = xmlBeanPropertyMetas.get(bpm);
+		if (m == null) {
+			BeanPropertyMeta dbpm = bpm.getDelegateFor();
+			m = new XmlBeanPropertyMeta(dbpm, this);
+			xmlBeanPropertyMetas.put(bpm, m);
+		}
+		return m;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Properties
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParserSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParserSession.java
index 872a1e8..0723260 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParserSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParserSession.java
@@ -357,10 +357,10 @@ public class XmlParserSession extends ReaderParserSession {
 		} else if (sType.isNumber()) {
 			o = parseNumber(getElementText(r), (Class<? extends Number>)sType.getInnerClass());
 		} else if (builder != null || sType.canCreateNewBean(outer)) {
-			if (sType.getExtendedMeta(XmlClassMeta.class).getFormat() == COLLAPSED) {
+			if (getXmlClassMeta(sType).getFormat() == COLLAPSED) {
 				String fieldName = r.getLocalName();
 				BeanMap<?> m = builder != null ? toBeanMap(builder.create(this, eType)) : newBeanMap(outer, sType.getInnerClass());
-				BeanPropertyMeta bpm = m.getMeta().getExtendedMeta(XmlBeanMeta.class).getPropertyMeta(fieldName);
+				BeanPropertyMeta bpm = getXmlBeanMeta(m.getMeta()).getPropertyMeta(fieldName);
 				ClassMeta<?> cm = m.getMeta().getClassMeta();
 				Object value = parseAnything(cm, currAttr, r, m.getBean(false), false, null);
 				setName(cm, value, currAttr);
@@ -472,7 +472,7 @@ public class XmlParserSession extends ReaderParserSession {
 
 	private <T> BeanMap<T> parseIntoBean(XmlReader r, BeanMap<T> m, boolean isNil) throws IOException, ParseException, ExecutableException, XMLStreamException {
 		BeanMeta<?> bMeta = m.getMeta();
-		XmlBeanMeta xmlMeta = bMeta.getExtendedMeta(XmlBeanMeta.class);
+		XmlBeanMeta xmlMeta = getXmlBeanMeta(bMeta);
 
 		for (int i = 0; i < r.getAttributeCount(); i++) {
 			String key = getAttributeName(r, i);
@@ -567,7 +567,7 @@ public class XmlParserSession extends ReaderParserSession {
 						skipCurrentTag(r);
 					} else {
 						setCurrentProperty(pMeta);
-						XmlFormat xf = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat();
+						XmlFormat xf = getXmlBeanPropertyMeta(pMeta).getXmlFormat();
 						if (xf == COLLAPSED) {
 							ClassMeta<?> et = pMeta.getClassMeta().getElementType();
 							Object value = parseAnything(et, currAttr, r, m.getBean(false), false, pMeta);
@@ -763,6 +763,40 @@ public class XmlParserSession extends ReaderParserSession {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * Returns the language-specific metadata on the specified class.
+	 *
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) {
+		return ctx.getXmlClassMeta(cm);
+	}
+
+	/**
+	 * Returns the language-specific metadata on the specified bean.
+	 *
+	 * @param bm The bean to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) {
+		return ctx.getXmlBeanMeta(bm);
+	}
+
+	/**
+	 * Returns the language-specific metadata on the specified bean property.
+	 *
+	 * @param bpm The bean property to return the metadata on.
+	 * @return The metadata.
+	 */
+	protected XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
+		return ctx.getXmlBeanPropertyMeta(bpm);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Other methods
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializer.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializer.java
index 4bc8c27..095b967 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializer.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializer.java
@@ -12,6 +12,9 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.xml;
 
+import java.util.*;
+import java.util.concurrent.*;
+
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.json.*;
@@ -111,7 +114,7 @@ import org.apache.juneau.xmlschema.XmlSchemaSerializer;
  * </ul>
  */
 @ConfigurableContext
-public class XmlSerializer extends WriterSerializer {
+public class XmlSerializer extends WriterSerializer implements XmlMetaProvider {
 
 	//-------------------------------------------------------------------------------------------------------------------
 	// Configurable properties
@@ -455,6 +458,9 @@ public class XmlSerializer extends WriterSerializer {
 	private final Namespace
 		xsNamespace;
 	private final Namespace[] namespaces;
+	private final Map<ClassMeta<?>,XmlClassMeta> xmlClassMetas = new ConcurrentHashMap<>();
+	private final Map<BeanMeta<?>,XmlBeanMeta> xmlBeanMetas = new ConcurrentHashMap<>();
+	private final Map<BeanPropertyMeta,XmlBeanPropertyMeta> xmlBeanPropertyMetas = new ConcurrentHashMap<>();
 
 	private volatile XmlSchemaSerializer schemaSerializer;
 
@@ -547,6 +553,40 @@ public class XmlSerializer extends WriterSerializer {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Override /* XmlMetaProvider */
+	public XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) {
+		XmlClassMeta m = xmlClassMetas.get(cm);
+		if (m == null) {
+			m = new XmlClassMeta(cm, this);
+			xmlClassMetas.put(cm, m);
+		}
+		return m;
+	}
+
+	@Override /* XmlMetaProvider */
+	public XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) {
+		XmlBeanMeta m = xmlBeanMetas.get(bm);
+		if (m == null) {
+			m = new XmlBeanMeta(bm, this);
+			xmlBeanMetas.put(bm, m);
+		}
+		return m;
+	}
+
+	@Override /* XmlMetaProvider */
+	public XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
+		XmlBeanPropertyMeta m = xmlBeanPropertyMetas.get(bpm);
+		if (m == null) {
+			m = new XmlBeanPropertyMeta(bpm.getDelegateFor(), this);
+			xmlBeanPropertyMetas.put(bpm, m);
+		}
+		return m;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Properties
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
index fdec1e6..c515083 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
@@ -151,7 +151,7 @@ public class XmlSerializerSession extends WriterSerializerSession {
 		}
 
 		if (aType != null) {
-			Namespace ns = cXml(aType).getNamespace();
+			Namespace ns = getXmlClassMeta(aType).getNamespace();
 			if (ns != null) {
 				if (ns.uri != null)
 					addNamespace(ns);
@@ -170,7 +170,7 @@ public class XmlSerializerSession extends WriterSerializerSession {
 				bm = toBeanMap(o);
 			} else if (aType.isDelegate()) {
 				ClassMeta<?> innerType = ((Delegate<?>)o).getClassMeta();
-				Namespace ns = cXml(innerType).getNamespace();
+				Namespace ns = getXmlClassMeta(innerType).getNamespace();
 				if (ns != null) {
 					if (ns.uri != null)
 						addNamespace(ns);
@@ -181,7 +181,7 @@ public class XmlSerializerSession extends WriterSerializerSession {
 				if (innerType.isBean()) {
 					for (BeanPropertyMeta bpm : innerType.getBeanMeta().getPropertyMetas()) {
 						if (bpm.canRead()) {
-							ns = bpXml(bpm).getNamespace();
+							ns = getXmlBeanPropertyMeta(bpm).getNamespace();
 							if (ns != null && ns.uri != null)
 								addNamespace(ns);
 						}
@@ -208,7 +208,7 @@ public class XmlSerializerSession extends WriterSerializerSession {
 			if (bm != null) {
 				for (BeanPropertyValue p : bm.getValues(isTrimNullProperties())) {
 
-					Namespace ns = bpXml(p.getMeta()).getNamespace();
+					Namespace ns = getXmlBeanPropertyMeta(p.getMeta()).getNamespace();
 					if (ns != null && ns.uri != null)
 						addNamespace(ns);
 
@@ -340,7 +340,7 @@ public class XmlSerializerSession extends WriterSerializerSession {
 		} else if (sType.isBoolean()) {
 			type = BOOLEAN;
 		} else if (sType.isMapOrBean()) {
-			isCollapsed = cXml(sType).getFormat() == COLLAPSED;
+			isCollapsed = getXmlClassMeta(sType).getFormat() == COLLAPSED;
 			type = OBJECT;
 		} else if (sType.isCollectionOrArray()) {
 			isCollapsed = (format == COLLAPSED && ! addNamespaceUris);
@@ -360,9 +360,9 @@ public class XmlSerializerSession extends WriterSerializerSession {
 
 		if (isEnableNamespaces()) {
 			if (elementNamespace == null)
-				elementNamespace = cXml(sType).getNamespace();
+				elementNamespace = getXmlClassMeta(sType).getNamespace();
 			if (elementNamespace == null)
-				elementNamespace = cXml(aType).getNamespace();
+				elementNamespace = getXmlClassMeta(aType).getNamespace();
 			if (elementNamespace != null && elementNamespace.uri == null)
 				elementNamespace = null;
 			if (elementNamespace == null)
@@ -488,7 +488,7 @@ public class XmlSerializerSession extends WriterSerializerSession {
 	private boolean isXmlText(XmlFormat format, ClassMeta<?> sType) {
 		if (format == XMLTEXT)
 			return true;
-		XmlClassMeta xcm = sType.getExtendedMeta(XmlClassMeta.class);
+		XmlClassMeta xcm = getXmlClassMeta(sType);
 		if (xcm == null)
 			return false;
 		return xcm.getFormat() == XMLTEXT;
@@ -533,7 +533,7 @@ public class XmlSerializerSession extends WriterSerializerSession {
 
 		List<BeanPropertyValue> lp = m.getValues(isTrimNullProperties());
 
-		XmlBeanMeta xbm = bXml(bm);
+		XmlBeanMeta xbm = getXmlBeanMeta(bm);
 
 		Set<String>
 			attrs = xbm.getAttrPropertyNames(),
@@ -563,7 +563,7 @@ public class XmlSerializerSession extends WriterSerializerSession {
 					if (canIgnoreValue(cMeta, key, value))
 						continue;
 
-					XmlBeanPropertyMeta bpXml = bpXml(pMeta);
+					XmlBeanPropertyMeta bpXml = getXmlBeanPropertyMeta(pMeta);
 					Namespace ns = (isEnableNamespaces() && bpXml.getNamespace() != elementNs ? bpXml.getNamespace() : null);
 
 					if (pMeta.isUri()  ) {
@@ -631,7 +631,7 @@ public class XmlSerializerSession extends WriterSerializerSession {
 						out.appendIf(! isCollapsed, '>').nlIf(! isMixedOrText, indent);
 					}
 
-					XmlBeanPropertyMeta bpXml = bpXml(pMeta);
+					XmlBeanPropertyMeta bpXml = getXmlBeanPropertyMeta(pMeta);
 					serializeAnything(out, value, cMeta, key, bpXml.getNamespace(), false, bpXml.getXmlFormat(), isMixedOrText, false, pMeta);
 				}
 			}
@@ -679,7 +679,7 @@ public class XmlSerializerSession extends WriterSerializerSession {
 		Namespace eNs = null;
 
 		if (ppMeta != null) {
-			XmlBeanPropertyMeta bpXml = bpXml(ppMeta);
+			XmlBeanPropertyMeta bpXml = getXmlBeanPropertyMeta(ppMeta);
 			eName = bpXml.getChildName();
 			eNs = bpXml.getNamespace();
 		}
@@ -691,18 +691,6 @@ public class XmlSerializerSession extends WriterSerializerSession {
 		return out;
 	}
 
-	private static XmlClassMeta cXml(ClassMeta<?> cm) {
-		return cm.getExtendedMeta(XmlClassMeta.class);
-	}
-
-	private static XmlBeanPropertyMeta bpXml(BeanPropertyMeta pMeta) {
-		return pMeta == null ? XmlBeanPropertyMeta.DEFAULT : pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
-	}
-
-	private static XmlBeanMeta bXml(BeanMeta bm) {
-		return (XmlBeanMeta)bm.getExtendedMeta(XmlBeanMeta.class);
-	}
-
 	static enum JsonType {
 		STRING("string"),BOOLEAN("boolean"),NUMBER("number"),ARRAY("array"),OBJECT("object"),NULL("null");
 
@@ -820,6 +808,40 @@ public class XmlSerializerSession extends WriterSerializerSession {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
+	// Extended metadata
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * Returns the language-specific metadata on the specified class.
+	 *
+	 * @param cm The class to return the metadata on.
+	 * @return The metadata.
+	 */
+	public XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) {
+		return ctx.getXmlClassMeta(cm);
+	}
+
+	/**
+	 * Returns the language-specific metadata on the specified bean.
+	 *
+	 * @param bm The bean to return the metadata on.
+	 * @return The metadata.
+	 */
+	public XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) {
+		return ctx.getXmlBeanMeta(bm);
+	}
+
+	/**
+	 * Returns the language-specific metadata on the specified bean property.
+	 *
+	 * @param bpm The bean property to return the metadata on.
+	 * @return The metadata.
+	 */
+	public XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
+		return bpm == null ? XmlBeanPropertyMeta.DEFAULT : ctx.getXmlBeanPropertyMeta(bpm);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
 	// Other methods
 	//-----------------------------------------------------------------------------------------------------------------
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xmlschema/XmlSchemaSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xmlschema/XmlSchemaSerializerSession.java
index 4ac369a..e451ea4 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xmlschema/XmlSchemaSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xmlschema/XmlSchemaSerializerSession.java
@@ -175,7 +175,7 @@ public class XmlSchemaSerializerSession extends XmlSerializerSession {
 			if (cm == null)
 				queueElement(ns, "null", object());
 			else {
-				XmlClassMeta xmlMeta = cm.getExtendedMeta(XmlClassMeta.class);
+				XmlClassMeta xmlMeta = getXmlClassMeta(cm);
 				if (cm.getDictionaryName() != null && xmlMeta.getNamespace() != null)
 					ns = xmlMeta.getNamespace();
 				queueElement(ns, cm.getDictionaryName(), cm);
@@ -277,7 +277,7 @@ public class XmlSchemaSerializerSession extends XmlSerializerSession {
 			ClassMeta<?> ft = cm.getSerializedClassMeta(schemas.session);
 			if (name == null)
 				name = getElementName(ft);
-			Namespace ns = first(ft.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
+			Namespace ns = first(getXmlClassMeta(ft).getNamespace(), defaultNs);
 			String type = getXmlType(ns, ft);
 
 			w.oTag(indent+1, "element")
@@ -316,7 +316,7 @@ public class XmlSchemaSerializerSession extends XmlSerializerSession {
 			while (cm.isOptional())
 				cm = cm.getElementType();
 
-			XmlBeanMeta xbm = cm.isBean() ? cm.getBeanMeta().getExtendedMeta(XmlBeanMeta.class) : null;
+			XmlBeanMeta xbm = cm.isBean() ? getXmlBeanMeta(cm.getBeanMeta()) : null;
 
 			w.oTag(i, "complexType")
 				.attr("name", name);
@@ -344,7 +344,7 @@ public class XmlSchemaSerializerSession extends XmlSerializerSession {
 
 					for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) {
 						if (pMeta.canRead()) {
-							XmlFormat bpXml = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat();
+							XmlFormat bpXml = getXmlBeanPropertyMeta(pMeta).getXmlFormat();
 							if (bpXml == ATTRS)
 								hasAnyAttrs = true;
 							else if (bpXml != XmlFormat.ATTR)
@@ -352,7 +352,7 @@ public class XmlSchemaSerializerSession extends XmlSerializerSession {
 						}
 					}
 
-					XmlBeanMeta xbm2 = bm.getExtendedMeta(XmlBeanMeta.class);
+					XmlBeanMeta xbm2 = getXmlBeanMeta(bm);
 					if (xbm2.getContentProperty() != null && xbm2.getContentFormat() == ELEMENTS) {
 						w.sTag(i+1, "sequence").nl(i+1);
 						w.oTag(i+2, "any")
@@ -368,11 +368,11 @@ public class XmlSchemaSerializerSession extends XmlSerializerSession {
 
 						for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) {
 							if (pMeta.canRead()) {
-								XmlBeanPropertyMeta xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
+								XmlBeanPropertyMeta xmlMeta = getXmlBeanPropertyMeta(pMeta);
 								if (xmlMeta.getXmlFormat() != ATTR) {
 									if (xmlMeta.getNamespace() != null) {
 										ClassMeta<?> ct2 = pMeta.getClassMeta();
-										Namespace cNs = first(xmlMeta.getNamespace(), ct2.getExtendedMeta(XmlClassMeta.class).getNamespace(), cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
+										Namespace cNs = first(xmlMeta.getNamespace(), getXmlClassMeta(ct2).getNamespace(), getXmlClassMeta(cm).getNamespace(), defaultNs);
 										// Child element is in another namespace.
 										schemas.queueElement(cNs, pMeta.getName(), ct2);
 										hasOtherNsElement = true;
@@ -399,7 +399,7 @@ public class XmlSchemaSerializerSession extends XmlSerializerSession {
 							w.sTag(i+1, "all").nl(i+1);
 							for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) {
 								if (pMeta.canRead()) {
-									XmlBeanPropertyMeta xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
+									XmlBeanPropertyMeta xmlMeta = getXmlBeanPropertyMeta(pMeta);
 									if (xmlMeta.getXmlFormat() != ATTR) {
 										boolean isCollapsed = xmlMeta.getXmlFormat() == COLLAPSED;
 										ClassMeta<?> ct2 = pMeta.getClassMeta();
@@ -409,7 +409,7 @@ public class XmlSchemaSerializerSession extends XmlSerializerSession {
 												childName = xmlMeta.getChildName();
 											ct2 = pMeta.getClassMeta().getElementType();
 										}
-										Namespace cNs = first(xmlMeta.getNamespace(), ct2.getExtendedMeta(XmlClassMeta.class).getNamespace(), cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
+										Namespace cNs = first(xmlMeta.getNamespace(), getXmlClassMeta(ct2).getNamespace(), getXmlClassMeta(cm).getNamespace(), defaultNs);
 										if (xmlMeta.getNamespace() == null) {
 											w.oTag(i+2, "element")
 												.attr("name", XmlUtils.encodeElementName(childName), false)
@@ -430,9 +430,9 @@ public class XmlSchemaSerializerSession extends XmlSerializerSession {
 
 					}
 
-					for (BeanPropertyMeta pMeta : bm.getExtendedMeta(XmlBeanMeta.class).getAttrProperties().values()) {
+					for (BeanPropertyMeta pMeta : getXmlBeanMeta(bm).getAttrProperties().values()) {
 						if (pMeta.canRead()) {
-							Namespace pNs = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
+							Namespace pNs = getXmlBeanPropertyMeta(pMeta).getNamespace();
 							if (pNs == null)
 								pNs = defaultNs;
 
@@ -470,7 +470,7 @@ public class XmlSchemaSerializerSession extends XmlSerializerSession {
 							.ceTag().nl(i+2);
 						w.eTag(i+1, "sequence").nl(i+1);
 					} else {
-						Namespace cNs = first(elementType.getExtendedMeta(XmlClassMeta.class).getNamespace(), cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
+						Namespace cNs = first(getXmlClassMeta(elementType).getNamespace(), getXmlClassMeta(cm).getNamespace(), defaultNs);
 						schemas.queueType(cNs, null, elementType);
 						w.sTag(i+1, "sequence").nl(i+1);
 						w.oTag(i+2, "any")