[java-identity-provider] branch master updated: IDP-1434 - Redesign Attribute encoding/decoding as a service

Scott Cantor cantor.2 at osu.edu
Mon May 13 13:39:56 EDT 2019


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch master
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=0353832c821a21a870bba5845ec0ead1b61aedf1

The following commit(s) were added to refs/heads/master by this push:
       new  0353832   IDP-1434 - Redesign Attribute encoding/decoding as a service
0353832 is described below

commit 0353832c821a21a870bba5845ec0ead1b61aedf1
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon May 13 13:39:51 2019 -0400

    IDP-1434 - Redesign Attribute encoding/decoding as a service
    
    https://issues.shibboleth.net/jira/browse/IDP-1434
    
    Squashed commit from feature/IDP-1434.
---
 idp-admin-api/.project                             |   5 -
 idp-admin-impl/.project                            |   5 -
 idp-attribute-api/.project                         |   5 -
 .../idp/attribute/AttributeDecodingException.java  |  62 +++
 .../shibboleth/idp/attribute/AttributeEncoder.java |  71 ----
 .../net/shibboleth/idp/attribute/IdPAttribute.java |  33 +-
 .../transcoding/AbstractAttributeTranscoder.java   | 156 ++++++++
 .../attribute/transcoding/AttributeTranscoder.java |  98 +++++
 .../transcoding/AttributeTranscoderRegistry.java   |  85 ++++
 .../attribute/transcoding/TranscoderSupport.java   |  55 +++
 .../idp/attribute/transcoding/TranscodingRule.java | 160 ++++++++
 .../idp/attribute/transcoding}/package-info.java   |   5 +-
 .../shibboleth/idp/attribute/AttributeTest.java    |  63 ---
 .../net/shibboleth/idp/attribute/MockEncoder.java  |  70 ----
 idp-attribute-filter-api/.project                  |   5 -
 idp-attribute-filter-impl/.project                 |   5 -
 .../saml/impl/AttributeInMetadataMatcher.java      | 165 +++++---
 .../impl/MappedAttributeInMetadataMatcher.java     | 261 -------------
 .../saml/impl/AttributeInMetadataMatcherTest.java  |  26 +-
 .../impl/MappedAttributeInMetadataMatcherTest.java |  12 +-
 idp-attribute-filter-spring/.project               |   5 -
 .../impl/MappedAttributeInMetadataRuleParser.java  |  14 +-
 .../MappedAttributeInMetadataRuleParserTest.java   |   6 +-
 idp-attribute-impl/.project                        |   5 -
 idp-attribute-impl/pom.xml                         |   6 +
 .../impl/AttributeTranscoderRegistryImpl.java      | 357 +++++++++++++++++
 .../transcoding/impl/TranscodingRuleLoader.java    |  95 +++++
 .../attribute/transcoding/impl}/package-info.java  |   5 +-
 .../impl/AttributeTranscoderRegistryImplTest.java  | 338 ++++++++++++++++
 .../attribute/transcoding/impl/PairTranscoder.java | 102 +++++
 idp-attribute-resolver-api/.project                |   5 -
 .../resolver/AbstractAttributeDefinition.java      |  38 --
 .../attribute/resolver/AttributeDefinition.java    |  10 -
 .../idp/attribute/resolver/AttributeResolver.java  |  12 -
 .../resolver/ResolvedAttributeDefinition.java      |   6 -
 .../resolver/AbstractAttributeDefinitionTest.java  |  72 ----
 .../attribute/resolver/MockAttributeEncoder.java   |  66 ----
 idp-attribute-resolver-impl/.project               |   5 -
 .../resolver/impl/AttributeResolverImpl.java       |  13 -
 idp-attribute-resolver-spring/.project             |   5 -
 .../spring/ad/BaseAttributeDefinitionParser.java   |  11 -
 .../spring/enc/BaseAttributeEncoderParser.java     | 104 ++---
 .../enc/BaseSAML1AttributeEncoderParser.java       |  61 +++
 .../enc/BaseSAML2AttributeEncoderParser.java       |  65 ++++
 .../enc/BaseScopedAttributeEncoderParser.java      |  59 ---
 .../impl/SAML1Base64AttributeEncoderParser.java    |  39 +-
 .../SAML1ScopedStringAttributeEncoderParser.java   |  56 +--
 .../impl/SAML1StringAttributeEncoderParser.java    |  45 +--
 .../impl/SAML1XMLObjectAttributeEncoderParser.java |  39 +-
 .../impl/SAML2Base64AttributeEncoderParser.java    |  44 +--
 .../SAML2ScopedStringAttributeEncoderParser.java   |  59 ++-
 .../impl/SAML2StringAttributeEncoderParser.java    |  45 +--
 .../impl/SAML2XMLObjectAttributeEncoderParser.java |  45 +--
 .../spring/impl/AttributeResolverParser.java       |  22 +-
 .../impl/AttributeRegistryServiceStrategy.java     | 100 +++++
 .../transcoding/spring/impl}/package-info.java     |   6 +-
 .../resolver/spring/AttributeMapperTest.java       |  56 ++-
 .../spring/AttributeMappingNodeProcessorTest.java  |  21 +-
 .../spring/BaseAttributeDefinitionParserTest.java  |  22 +-
 .../spring/BaseEncoderDefinitionParserTest.java    | 104 +++++
 .../spring/ad/SimpleAttributeParserTest.java       |  37 +-
 .../attribute/resolver/spring/enc/Regressions.java |  20 +-
 .../enc/SAML1Base64AttributeEncoderParserTest.java |  42 +-
 ...AML1ScopedStringAttributeEncoderParserTest.java |  61 +--
 .../enc/SAML1StringAttributeEncoderParserTest.java |  45 ++-
 .../SAML1XMLObjectAttributeEncoderParserTest.java  |  44 ++-
 .../enc/SAML2Base64AttributeEncoderParserTest.java |  45 ++-
 ...AML2ScopedStringAttributeEncoderParserTest.java |  63 +--
 .../enc/SAML2StringAttributeEncoderParserTest.java |  76 ++--
 .../SAML2XMLObjectAttributeEncoderParserTest.java  |  48 ++-
 .../src/test/resources/logback-test.xml            |   3 +
 .../idp/attribute/resolver/filter/service.xml      |  52 ++-
 .../idp/attribute/resolver/spring/customBean.xml   |  28 +-
 .../attribute/resolver/spring/enc/predicates.xml   |  10 -
 .../resolver/spring/enc/resolver/idp-571.xml       |   3 -
 .../resolver/spring/enc/resolver/saml1Base64.xml   |  11 +-
 .../spring/enc/resolver/saml1Base64Default.xml     |   4 +-
 .../resolver/spring/enc/resolver/saml1Scoped.xml   |   9 +-
 .../spring/enc/resolver/saml1ScopedDefault.xml     |   3 +-
 .../resolver/spring/enc/resolver/saml1String.xml   |  11 +-
 .../spring/enc/resolver/saml1StringDefault.xml     |   3 +-
 .../spring/enc/resolver/saml1XmlObject.xml         |  11 +-
 .../spring/enc/resolver/saml1XmlObjectDefault.xml  |   1 +
 .../resolver/spring/enc/resolver/saml2Base64.xml   |  10 +-
 .../spring/enc/resolver/saml2Base64Default.xml     |   1 +
 .../resolver/spring/enc/resolver/saml2Scoped.xml   |  10 +-
 .../spring/enc/resolver/saml2ScopedDefault.xml     |   1 +
 .../spring/enc/resolver/saml2ScopedNoName.xml      |  13 +-
 .../resolver/spring/enc/resolver/saml2String.xml   |   8 +-
 .../spring/enc/resolver/saml2StringConditional.xml |   2 +-
 .../spring/enc/resolver/saml2StringDefault.xml     |   1 +
 .../spring/enc/resolver/saml2XmlObject.xml         |  11 +-
 .../spring/enc/resolver/saml2XmlObjectDefault.xml  |   1 +
 .../resolver/spring/mapperTest-attributes.xml      |  42 +-
 .../idp/attribute/resolver/spring/mapperTest.xml   |  26 +-
 idp-authn-api/.project                             |   5 -
 idp-authn-impl/.project                            |   5 -
 idp-bom/.project                                   |   5 -
 idp-cas-api/.project                               |   5 -
 idp-cas-impl/.project                              |   5 -
 idp-conf/.project                                  |   5 -
 .../attributes/saml1/eduPersonPrincipalName.txt    |   4 +
 .../saml1/eduPersonScopedAffiliation.txt           |   4 +
 .../src/main/resources/attributes/saml1/mail.txt   |   4 +
 .../src/main/resources/attributes/saml1/uid.txt    |   4 +
 .../attributes/saml2/eduPersonPrincipalName.txt    |   4 +
 .../saml2/eduPersonScopedAffiliation.txt           |   4 +
 .../src/main/resources/attributes/saml2/mail.txt   |   4 +
 .../src/main/resources/attributes/saml2/uid.txt    |   4 +
 .../src/main/resources/conf/attribute-registry.xml |  16 +-
 .../src/main/resources/conf/attribute-resolver.xml |  11 +-
 .../src/main/resources/conf/services.properties    |   5 +
 idp-conf/src/main/resources/conf/services.xml      |  12 +
 .../system/conf/attribute-registry-system.xml      |  48 +++
 .../main/resources/system/conf/services-system.xml |  32 +-
 .../resources/system/flows/admin/status-beans.xml  |   1 +
 .../system/flows/saml/saml-abstract-beans.xml      |  10 +-
 .../flows/saml/saml1/attribute-query-beans.xml     |   4 +
 .../system/flows/saml/saml1/common-beans.xml       |   3 +-
 .../flows/saml/saml2/attribute-query-beans.xml     |   4 +
 .../system/flows/saml/saml2/common-beans.xml       |   3 +-
 .../idp/test/flows/mapper/MappingTest.java         |   2 +-
 .../idp/test/flows/mapper/TestAfterFilter.java     |   8 +-
 .../flows/saml1/SAML1AttributeQueryFlowTest.java   |   2 +-
 .../flows/saml2/SAML2AttributeQueryFlowTest.java   |   2 +-
 .../resources/mapping/configs/attribute-filter.xml |   2 +-
 .../mapping/configs/attribute-resolver.xml         |   2 -
 .../test/resources/mapping/configs/override.xml    |   5 +
 idp-consent-api/.project                           |   5 -
 idp-consent-impl/.project                          |   5 -
 .../logic/impl/IsAttributeRequiredPredicate.java   |   2 +-
 idp-core/.project                                  |   5 -
 idp-installer/.project                             |   5 -
 idp-profile-api/.project                           |   5 -
 idp-profile-impl/.project                          |   5 -
 idp-profile-spring/.project                        |   5 -
 .../NodeProcessingAttachingBeanPostProcessor.java  |  14 +-
 idp-saml-api/.project                              |   5 -
 idp-saml-api/pom.xml                               |   1 -
 .../encoding/AbstractSAML1AttributeEncoder.java    | 158 --------
 .../encoding/AbstractSAML2AttributeEncoder.java    | 166 --------
 .../encoding/AbstractSAMLAttributeEncoder.java     | 258 -------------
 .../AttributeDesignatorMapperProcessor.java        |  44 ---
 .../encoding/AttributeMapperProcessor.java         |  47 ---
 .../attribute/encoding/SAML1AttributeEncoder.java  |  48 ---
 .../AbstractSAMLAttributeDesignatorMapper.java     | 258 -------------
 .../AbstractSAMLAttributeDesignatorsMapper.java    | 187 ---------
 .../mapping/AbstractSAMLAttributeMapper.java       | 309 ---------------
 .../mapping/AbstractSAMLAttributeValueMapper.java  | 204 ----------
 .../mapping/AbstractSAMLAttributesMapper.java      | 191 ---------
 .../saml/attribute/mapping/AttributeMapper.java    |  49 ---
 .../saml/attribute/mapping/AttributesMapper.java   |  49 ---
 .../AbstractSAML1AttributeTranscoder.java          | 187 +++++++++
 .../AbstractSAML2AttributeTranscoder.java          | 188 +++++++++
 .../AbstractSAMLAttributeTranscoder.java           | 285 ++++++++++++++
 .../AttributesMapContainer.java                    |  16 +-
 .../transcoding/SAML1AttributeTranscoder.java      |  27 +-
 .../SAML2AttributeTranscoder.java}                 |  37 +-
 .../transcoding/SAMLAttributeTranscoder.java       |  31 +-
 .../SAMLEncoderSupport.java                        |   2 +-
 .../{encoding => transcoding}/package-info.java    |   4 +-
 .../AbstractSAML1AttributeEncoderTest.java         |  94 -----
 .../AbstractSAML2AttributeEncoderTest.java         | 143 -------
 .../encoding/AbstractSAMLAttributeEncoderTest.java | 229 -----------
 .../SAMLEncoderSupportTest.java                    |   2 +-
 idp-saml-impl/.project                             |   5 -
 .../encoding/impl/SAML1ByteAttributeEncoder.java   |  52 ---
 .../impl/SAML1ScopedStringAttributeEncoder.java    | 176 ---------
 .../impl/SAML1XMLObjectAttributeEncoder.java       |  52 ---
 .../encoding/impl/SAML2ByteAttributeEncoder.java   |  67 ----
 .../impl/SAML2ScopedStringAttributeEncoder.java    | 192 ---------
 .../impl/SAML2XMLObjectAttributeEncoder.java       |  69 ----
 .../mapping/impl/ByteAttributeValueMapper.java     |  52 ---
 .../mapping/impl/RequestedAttributeMapper.java     |  40 --
 .../mapping/impl/RequestedAttributesMapper.java    |  60 ---
 .../impl/SAML1AttributeDesignatorsMapper.java      |  58 ---
 .../SAML1AttributeDesignatorsMapperService.java    | 140 -------
 .../mapping/impl/SAML2AttributesMapper.java        |  60 ---
 .../mapping/impl/SAML2AttributesMapperService.java | 138 -------
 .../impl/ScopedStringAttributeValueMapper.java     | 110 ------
 .../mapping/impl/StringAttributeValueMapper.java   |  49 ---
 .../impl/XMLObjectAttributeValueMapper.java        | 112 ------
 .../impl/SAML1ByteAttributeTranscoder.java         |  84 ++++
 .../impl/SAML1ScopedStringAttributeTranscoder.java | 134 +++++++
 .../impl/SAML1StringAttributeTranscoder.java}      |  44 ++-
 .../impl/SAML1XMLObjectAttributeTranscoder.java    |  97 +++++
 .../impl/SAML2ByteAttributeTranscoder.java         |  84 ++++
 .../impl/SAML2ScopedStringAttributeTranscoder.java | 135 +++++++
 .../impl/SAML2StringAttributeTranscoder.java}      |  48 +--
 .../impl/SAML2XMLObjectAttributeTranscoder.java    |  97 +++++
 .../impl/package-info.java                         |   6 +-
 .../impl/AttributeMappingNodeProcessor.java        | 232 +++++------
 .../impl/BaseAddAttributeStatementToAssertion.java |  97 ++++-
 .../impl/AddAttributeStatementToAssertion.java     |  63 ++-
 .../impl/FilterByQueriedAttributeDesignators.java  | 118 +++++-
 .../impl/AddAttributeStatementToAssertion.java     |  61 ++-
 .../profile/impl/FilterByQueriedAttributes.java    | 109 +++++-
 ...estedAttributesInAttributeConsumingService.java |  33 +-
 .../impl/SAML1ByteAttributeEncoderTest.java        | 168 --------
 .../SAML1ScopedStringAttributeEncoderTest.java     | 288 --------------
 .../impl/SAML1StringAttributeEncoderTest.java      | 143 -------
 .../impl/SAML1XMLObjectAttributeEncoderTest.java   | 188 ---------
 .../impl/SAML2ByteAttributeEncoderTest.java        | 161 --------
 .../SAML2ScopedStringAttributeEncoderTest.java     | 290 --------------
 .../impl/SAML2StringAttributeEncoderTest.java      | 140 -------
 .../impl/SAML2XMLObjectAttributeEncoderTest.java   | 189 ---------
 .../mapping/impl/AttributeValueMapperTest.java     | 141 -------
 .../saml/attribute/mapping/impl/MappingTests.java  |  49 ---
 .../attribute/mapping/impl/NativeSpringTest.java   |  63 ---
 .../mapping/impl/RequestedAttributeMapperTest.java | 163 --------
 .../impl/RequestedAttributesMapperTest.java        |  85 ----
 .../impl/SAML1ByteAttributeTranscoderTest.java     | 368 ++++++++++++++++++
 .../SAML1ScopedStringAttributeTranscoderTest.java  | 370 ++++++++++++++++++
 .../impl/SAML1StringAttributeTranscoderTest.java   | 327 ++++++++++++++++
 .../impl/SAML2ByteAttributeTranscoderTest.java     | 405 +++++++++++++++++++
 .../SAML2ScopedStringAttributeTranscoderTest.java  | 394 +++++++++++++++++++
 .../impl/SAML2StringAttributeTranscoderTest.java   | 364 +++++++++++++++++
 .../SAML2XMLObjectAttributeTranscoderTest.java     | 429 +++++++++++++++++++++
 .../impl/AddAttributeStatementToAssertionTest.java | 152 ++++++--
 .../FilterByQueriedAttributeDesignatorsTest.java   |  71 +++-
 .../impl/AddAttributeStatementToAssertionTest.java | 152 ++++++--
 .../impl/FilterByQueriedAttributesTest.java        |  59 ++-
 .../impl/attribute/mapping/attributesMapper.xml    | 107 -----
 .../attribute/mapping/requestedAttributeValues.xml |  50 ---
 .../idp/saml/impl/profile/AttributeQuery.xml       |  11 +-
 .../idp/saml/impl/profile/AttributeQuerySaml1.xml  |  25 +-
 .../idp/saml/impl/profile/saml1Mapper.xml          | 156 ++++----
 .../idp/saml/impl/profile/saml2Mapper.xml          | 173 ++++-----
 idp-schema/.project                                |   5 -
 .../src/main/resources/schema/shibboleth-afp.xsd   |  44 +--
 idp-session-api/.project                           |   5 -
 idp-session-impl/.project                          |   5 -
 idp-ui/.project                                    |   5 -
 idp-war/.project                                   |   5 -
 idp-war/src/main/webapp/WEB-INF/jsp/status.jsp     |   1 +
 235 files changed, 7864 insertions(+), 8536 deletions(-)

diff --git a/idp-admin-api/.project b/idp-admin-api/.project
index 4efaa10..9dbebd9 100644
--- a/idp-admin-api/.project
+++ b/idp-admin-api/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-admin-impl/.project b/idp-admin-impl/.project
index 90b3f69..e76252e 100644
--- a/idp-admin-impl/.project
+++ b/idp-admin-impl/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-attribute-api/.project b/idp-attribute-api/.project
index ed92a0d..733bbbb 100644
--- a/idp-attribute-api/.project
+++ b/idp-attribute-api/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/AttributeDecodingException.java b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/AttributeDecodingException.java
new file mode 100644
index 0000000..ea56273
--- /dev/null
+++ b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/AttributeDecodingException.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+/** Indicates a problem during decoding into an attribute. */
+ at ThreadSafe
+public class AttributeDecodingException extends AttributeException {
+
+    /** Serialization ID. */
+    private static final long serialVersionUID = -7129079905857690466L;
+
+    /** Constructor. */
+    public AttributeDecodingException() {
+        super();
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param message exception message
+     */
+    public AttributeDecodingException(@Nullable final String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param wrappedException exception to be wrapped by this one
+     */
+    public AttributeDecodingException(@Nullable final Exception wrappedException) {
+        super(wrappedException);
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param message exception message
+     * @param wrappedException exception to be wrapped by this one
+     */
+    public AttributeDecodingException(@Nullable final String message, @Nullable final Exception wrappedException) {
+        super(message, wrappedException);
+    }
+}
\ No newline at end of file
diff --git a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/AttributeEncoder.java b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/AttributeEncoder.java
deleted file mode 100644
index f72b56b..0000000
--- a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/AttributeEncoder.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute;
-
-import java.util.function.Predicate;
-
-import javax.annotation.Nonnull;
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-
-/**
- * Attribute encoders convert an {@link IdPAttribute} into a protocol specific representation. Implementations must take
- * into account that an {@link IdPAttribute} may contain values of multiple types. An implementation encountering
- * a value type it does not understand may either decide to ignore it or throw an {@link AttributeEncodingException}.
- * 
- * <p>Encoders implement a {@link Predicate} interface to determine their applicability to a request.</p>
- * 
- * <p>Encoders <strong>MUST</strong> be thread-safe and stateless and <strong>MUST</strong> implement appropriate
- * {@link Object#equals(Object)} and {@link Object#hashCode()} methods.</p>
- * 
- * @param <EncodedType> the type of object created by encoding the attribute
- */
- at ThreadSafe
-public interface AttributeEncoder<EncodedType> {
-
-    /**
-     * Get the identifier of the protocol targeted by this encoder. Note, some protocols may have different types of
-     * encoders that are used to encode attributes in to different parts of the protocol message. This identifier should
-     * not be used to distinguish between the different message structure, it should only identify the protocol itself.
-     * 
-     * @return identifier of the protocol targeted by this encounter
-     */
-    @Nonnull @NotEmpty String getProtocol();
-    
-    /**
-     * Get an activation condition for this encoder.
-     * 
-     * @return  a predicate indicating whether the encoder should be applied
-     */
-    @Nonnull Predicate<ProfileRequestContext> getActivationCondition();
-
-    /**
-     * Encode the supplied attribute into a protocol specific representation.
-     * 
-     * @param attribute the attribute to encode
-     * 
-     * @return the Object the attribute was encoded into
-     * 
-     * @throws AttributeEncodingException if unable to successfully encode attribute
-     */
-    @Nonnull EncodedType encode(@Nonnull final IdPAttribute attribute) throws AttributeEncodingException;
-
-}
\ No newline at end of file
diff --git a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/IdPAttribute.java b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/IdPAttribute.java
index 3825d4f..56194f5 100644
--- a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/IdPAttribute.java
+++ b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/IdPAttribute.java
@@ -23,7 +23,6 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Set;
 import java.util.stream.Collectors;
 
 import javax.annotation.Nonnull;
@@ -32,10 +31,7 @@ import javax.annotation.concurrent.NotThreadSafe;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
-import com.google.common.base.Predicates;
-import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
 
 import net.shibboleth.idp.attribute.EmptyAttributeValue.EmptyType;
 import net.shibboleth.utilities.java.support.annotation.ParameterName;
@@ -69,9 +65,6 @@ public class IdPAttribute implements Comparable<IdPAttribute>, Cloneable {
     /** Values for this attribute. */
     @Nonnull private List<IdPAttributeValue<?>> values;
 
-    /** Encoders that may be used to encode this attribute. */
-    @Nonnull private Set<AttributeEncoder<?>> encoders;
-
     /**
      * Constructor.
      * 
@@ -86,7 +79,6 @@ public class IdPAttribute implements Comparable<IdPAttribute>, Cloneable {
         displayDescriptions = Collections.emptyMap();
 
         values = Collections.emptyList();
-        encoders = Collections.emptySet();
     }
 
     /**
@@ -183,28 +175,6 @@ public class IdPAttribute implements Comparable<IdPAttribute>, Cloneable {
         }
     }
 
-    /**
-     * Gets the list of attribute encoders usable with this attribute.
-     * 
-     * @return attribute encoders usable with this attribute
-     */
-    @Nonnull @NonnullElements @Unmodifiable @NotLive public Set<AttributeEncoder<?>> getEncoders() {
-        return encoders;
-    }
-
-    /**
-     * Replaces the existing encoders for this attribute with the given encoders.
-     * 
-     * @param newEncoders the new encoders for this attribute
-     */
-    public void setEncoders(@Nullable @NullableElements final Collection<AttributeEncoder<?>> newEncoders) {
-        if (newEncoders != null) {
-            encoders = ImmutableSet.copyOf(Collections2.filter(newEncoders, Predicates.notNull()));
-        } else {
-            encoders = ImmutableSet.of();
-        }
-    }
-
     /** {@inheritDoc} */
     @Override
     public int compareTo(final IdPAttribute other) {
@@ -222,7 +192,6 @@ public class IdPAttribute implements Comparable<IdPAttribute>, Cloneable {
         final IdPAttribute clone = (IdPAttribute) super.clone();
         clone.setDisplayDescriptions(getDisplayDescriptions());
         clone.setDisplayNames(getDisplayNames());
-        clone.setEncoders(getEncoders());
         clone.setValues(getValues());
         return clone;
     }
@@ -256,7 +225,7 @@ public class IdPAttribute implements Comparable<IdPAttribute>, Cloneable {
     @Override
     @Nonnull public String toString() {
         return MoreObjects.toStringHelper(this).add("id", getId()).add("displayNames", displayNames)
-                .add("displayDescriptions", displayDescriptions).add("encoders", encoders).add("values", values)
+                .add("displayDescriptions", displayDescriptions).add("values", values)
                 .toString();
     }
     
diff --git a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AbstractAttributeTranscoder.java b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AbstractAttributeTranscoder.java
new file mode 100644
index 0000000..14a2500
--- /dev/null
+++ b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AbstractAttributeTranscoder.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.transcoding;
+
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.attribute.AttributeDecodingException;
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Predicates;
+
+/**
+ * Base class for transcoders.
+ * 
+ * @param <T> type of object supported
+ */
+public abstract class AbstractAttributeTranscoder<T> extends AbstractInitializableComponent
+        implements AttributeTranscoder<T> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractAttributeTranscoder.class);
+
+    /** Condition for use of this transcoder. */
+    @Nonnull private Predicate<ProfileRequestContext> activationCondition;
+    
+    /** Constructor. */
+    public AbstractAttributeTranscoder() {
+        activationCondition = Predicates.alwaysTrue();
+    }
+    
+    /**
+     * Set an activation condition for this transcoder.
+     * 
+     * @param condition condition to set
+     */
+    public void setActivationCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        activationCondition = Constraint.isNotNull(condition, "Activation condition cannot be null");
+    }
+
+    /** {@inheritDoc} */
+    @Nullable public T encode(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final IdPAttribute attribute, @Nonnull final Class<? extends T> to,
+            @Nonnull final TranscodingRule rule) throws AttributeEncodingException {
+        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+        Constraint.isNotNull(attribute, "Attribute to encode cannot be null");
+
+        if (!checkActivation(profileRequestContext, rule)) {
+            return null;
+        }
+
+        return doEncode(profileRequestContext, attribute, to, rule);
+    }
+    
+    /** {@inheritDoc} */
+    @Nullable public IdPAttribute decode(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final T input, @Nonnull final TranscodingRule rule) throws AttributeDecodingException {
+        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+        Constraint.isNotNull(input, "Attribute to decode cannot be null");
+
+        if (!checkActivation(profileRequestContext, rule)) {
+            return null;
+        }
+        
+        return doDecode(profileRequestContext, input, rule);
+    }
+    
+    
+    /**
+     * Encode the supplied attribute into a protocol specific representation.
+     * 
+     * @param profileRequestContext current profile request context
+     * @param attribute the attribute to encode
+     * @param to specific type of object to encode
+     * @param rule properties governing the encoding process, principally the resulting object's naming
+     * 
+     * @return the Object the attribute was encoded into
+     * 
+     * @throws AttributeEncodingException if unable to successfully encode attribute
+     */
+    @Nullable protected abstract T doEncode(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final IdPAttribute attribute, @Nonnull final Class<? extends T> to,
+            @Nonnull final TranscodingRule rule) throws AttributeEncodingException;
+    
+
+    /**
+     * Decode the supplied object into a protocol-neutral representation.
+     * 
+     * @param profileRequestContext current profile request context
+     * @param input the object to decode
+     * @param rule properties governing the decoding process, principally the resulting attribute's naming
+     * 
+     * @return the attribute the object was decoded into
+     * 
+     * @throws AttributeDecodingException if unable to successfully decode object
+     */
+    @Nullable protected abstract IdPAttribute doDecode(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final T input, @Nonnull final TranscodingRule rule) throws AttributeDecodingException;
+
+
+    /**
+     * 
+     * Apply any activation rules to the request.
+     * 
+     * @param profileRequestContext current profile request context
+     * @param rule properties governing the transoding process
+     * 
+     * @return true iff the process should continue
+     */
+    private boolean checkActivation(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final TranscodingRule rule) {
+        
+        if (!activationCondition.test(profileRequestContext)) {
+            log.debug("Transcoder inactive");
+            return false;
+        }
+
+        final Predicate condition = rule.get(AttributeTranscoderRegistry.PROP_CONDITION, Predicate.class);
+        if (condition != null) {
+            if (!condition.test(profileRequestContext)) {
+                log.debug("Transcoder inactive");
+                return false;
+            }
+        }
+        
+        return true;
+    }
+
+}
\ No newline at end of file
diff --git a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AttributeTranscoder.java b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AttributeTranscoder.java
new file mode 100644
index 0000000..a8a0a2f
--- /dev/null
+++ b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AttributeTranscoder.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.transcoding;
+
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.attribute.AttributeDecodingException;
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.component.InitializableComponent;
+
+/**
+ * Transcoders are objects that support both attribute encoding and decoding for bidirectional
+ * translation between {@link IdPAttribute} format and technology-specific formats.
+ * 
+ * <p>Implementations must take into account values of multiple types. An implementation encountering
+ * a value type it does not understand may either decide to ignore it or throw exceptions.</p>
+ * 
+ * <p>Transcoders implement a {@link Predicate} interface to determine their applicability to a request.</p>
+ * 
+ * <p>Transcoders <strong>MUST</strong> be thread-safe and stateless.</p>
+ * 
+ * @param <T> the type of object supported
+ */
+ at ThreadSafe
+public interface AttributeTranscoder<T> extends InitializableComponent {
+
+    /**
+     * Get the class representing the type of object supported by this transcoder.
+     * 
+     * @return object type supported
+     */
+    @Nonnull Class<T> getEncodedType();
+    
+    /**
+     * Get the name of the encoded object that would be created by a given set of
+     * instructions.
+     * 
+     * @param rule properties governing the encoding process
+     * 
+     * @return a canonical name for objects produced by this transcoder for the
+     *  given instructions
+     */
+    @Nullable @NotEmpty String getEncodedName(@Nonnull final TranscodingRule rule);
+    
+    /**
+     * Encode the supplied attribute into a protocol specific representation.
+     * 
+     * @param profileRequestContext current profile request context
+     * @param attribute the attribute to encode
+     * @param to specific type of object to encode
+     * @param rule properties governing the encoding process, principally the resulting object's naming
+     * 
+     * @return the Object the attribute was encoded into
+     * 
+     * @throws AttributeEncodingException if unable to successfully encode attribute
+     */
+    @Nullable T encode(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final IdPAttribute attribute, @Nonnull final Class<? extends T> to,
+            @Nonnull final TranscodingRule rule) throws AttributeEncodingException;
+    
+    /**
+     * Decode the supplied object into a protocol-neutral representation.
+     * 
+     * @param profileRequestContext current profile request context
+     * @param input the object to decode
+     * @param rule properties governing the decoding process, principally the resulting attribute's naming
+     * 
+     * @return the attribute the object was decoded into
+     * 
+     * @throws AttributeDecodingException if unable to successfully decode object
+     */
+    @Nullable IdPAttribute decode(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final T input, @Nonnull final TranscodingRule rule) throws AttributeDecodingException;
+    
+}
\ No newline at end of file
diff --git a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AttributeTranscoderRegistry.java b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AttributeTranscoderRegistry.java
new file mode 100644
index 0000000..2594818
--- /dev/null
+++ b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/AttributeTranscoderRegistry.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.transcoding;
+
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
+import net.shibboleth.utilities.java.support.component.IdentifiedComponent;
+
+
+/**
+ * The transcoder registry provides access to "instructions" for converting between
+ * the {@link IdPAttribute} "neutral" representation within the IdP and protocol-specific
+ * forms such as SAML Attributes or OIDC claims.
+ */
+ at ThreadSafe
+public interface AttributeTranscoderRegistry extends IdentifiedComponent {
+    
+    /** Property name for accessing the name of the {@link IdPAttribute} to decode into. */
+    @Nonnull @NotEmpty static final String PROP_ID = "id";
+
+    /** Property name for accessing {@link AttributeTranscoder} object to use. */
+    @Nonnull @NotEmpty static final String PROP_TRANSCODER = "transcoder";
+
+    /** Property name for identifying an {@link AttributeTranscoder} class to build. */
+    @Nonnull @NotEmpty static final String PROP_TRANSCODER_CLASS = "transcoder_class";
+    
+    /** Property name for accessing an activation condition object to apply. */
+    @Nonnull @NotEmpty static final String PROP_CONDITION = "activationCondition";
+
+    /** Property name for accessing relying parties to wrap an activation condition around. */
+    @Nonnull @NotEmpty static final String PROP_RELYINGPARTIES = "relyingParties";
+    
+    /**
+     * Obtains a set of instructions for encoding an input {@link IdPAttribute} into a target type.
+     * 
+     * <p>The principal property useful to a caller is {@link #PROP_TRANSCODER} to obtain an instance
+     * of the appropriate {@link AttributeTranscoder} to call, passing in the properties to drive that
+     * call.</p>
+     * 
+     * @param from the input object to encode
+     * @param to class of object being encoded
+     * 
+     * @return a collection of {@link TranscodingRule} objects, possibly empty
+     */
+    @Nonnull @NonnullElements @Unmodifiable
+    Collection<TranscodingRule> getTranscodingRules(@Nonnull final IdPAttribute from, @Nonnull final Class<?> to);
+
+    /**
+     * Obtains a set of instructions for decoding an input object into an {@link IdPAttribute}.
+     * 
+     * <p>The principal property useful to a caller is {@link #PROP_TRANSCODER} to obtain an instance
+     * of the appropriate {@link AttributeTranscoder} to call, passing in the properties to drive that
+     * call.</p>
+     * 
+     * @param <T> the type of object to decode
+     * @param from object to decode
+     * 
+     * @return a collection of {@link TranscodingRule} objects, possibly empty
+     */
+    @Nonnull @NonnullElements @Unmodifiable <T> Collection<TranscodingRule> getTranscodingRules(
+            @Nonnull final T from);
+        
+}
\ No newline at end of file
diff --git a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/TranscoderSupport.java b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/TranscoderSupport.java
new file mode 100644
index 0000000..e9f8ac6
--- /dev/null
+++ b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/TranscoderSupport.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.transcoding;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.logic.ConstraintViolationException;
+
+/**
+ * Support functions for working with {@link AttributeTranscoder} framework.
+ */
+public final class TranscoderSupport {
+
+    /** Constructor. */
+    private TranscoderSupport() {
+        
+    }
+
+    /**
+     * Pull an {@link AttributeTranscoder} object out of the rule provided.
+     * 
+     * @param <T> type of supported target object
+     * @param rule transcoding rule
+     * 
+     * @return an {@link AttributeTranscoder}
+     * 
+     * @throws ConstraintViolationException if a transcoder cannot be obtained
+     */
+    @Nonnull public static <T> AttributeTranscoder<T> getTranscoder(@Nonnull final TranscodingRule rule)
+            throws ConstraintViolationException {
+        Constraint.isNotNull(rule, "Transcoding rule cannot be null");
+        
+        final AttributeTranscoder<T> transcoder =
+                rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER, AttributeTranscoder.class);
+        Constraint.isNotNull(transcoder, "AttributeTranscoder not found in properties");
+        return transcoder;
+    }
+
+}
\ No newline at end of file
diff --git a/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/TranscodingRule.java b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/TranscodingRule.java
new file mode 100644
index 0000000..89bcf88
--- /dev/null
+++ b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/TranscodingRule.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.transcoding;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.springframework.core.io.Resource;
+
+import net.shibboleth.utilities.java.support.annotation.ParameterName;
+import net.shibboleth.utilities.java.support.annotation.constraint.Live;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+
+/**
+ * Wrapper around a {@link Map} representing a rule for transcoding, used to
+ * detect and load the rules at runtime from a Spring context.
+ */
+public class TranscodingRule {
+
+    /** Underlying map containing the rule. */
+    @Nonnull @NonnullElements private final Map<String,Object> rule;
+    
+    /**
+     * Constructor.
+     * 
+     * @param map a map containing the rule
+     * 
+     * <p>The rule MUST contain at least:</p>
+     * <ul>
+     *  <li>
+     *  {@link AttributeTranscoderRegistry#PROP_ID} - internal attribute ID to map to/from
+     *  </li>
+     *  <li>
+     *  {@link AttributeTranscoderRegistry#PROP_TRANSCODER} - {@link AttributeTranscoder} instance supporting the type
+     *  </li>
+     * </ul>
+     */
+    public TranscodingRule(@Nonnull @NonnullElements @ParameterName(name="map") final Map<String,Object> map) {
+        rule = new HashMap<>(map);
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param properties a property set to initialize the map
+     * 
+     * <p>The rule MUST contain at least:</p>
+     * <ul>
+     *  <li>
+     *  {@link AttributeTranscoderRegistry#PROP_ID} - internal attribute ID to map to/from
+     *  </li>
+     *  <li>
+     *  {@link AttributeTranscoderRegistry#PROP_TRANSCODER} - {@link AttributeTranscoder} class name
+     *  </li>
+     * </ul>
+     */
+    public TranscodingRule(@Nonnull @NonnullElements @ParameterName(name="properties") final Properties properties) {
+        rule = new HashMap<>(properties.size());
+        properties.forEach(
+                (k,v) -> {
+                    if (k instanceof String && v != null) {
+                        rule.put((String) k, v);
+                    }
+                });
+    }
+
+    /**
+     * Access the underlying mapping rule.
+     * 
+     * @return the map representing the rule
+     */
+    @Nonnull @NonnullElements @Live public Map<String,Object> getMap() {
+        return rule;
+    }
+    
+    /**
+     * Get the value of a property key in the rule.
+     * 
+     * @param <T> type of property value
+     * @param key key of property
+     * @param type class type of property
+     * 
+     * @return the mapped value, or null
+     */
+    @Nullable public <T> T get(@Nonnull @NotEmpty final String key, @Nonnull final Class<T> type) {
+        final Object value = rule.get(key);
+        if (type.isInstance(value)) {
+            return (T) value;
+        } else if (type == Boolean.class && value instanceof String) {
+            return (T) Boolean.valueOf((String) value);
+        } else {
+            return null;
+        }
+    }
+    
+    /**
+     * Get the value of a property key in the rule or a default value if no property exists.
+     * 
+     * @param <T> type of property value
+     * @param key key of property
+     * @param type class type of property
+     * @param defValue default value
+     * 
+     * @return the mapped value, or the default
+     */
+    @Nullable public <T> T getOrDefault(@Nonnull @NotEmpty final String key, @Nonnull final Class<T> type,
+            @Nullable final T defValue) {
+        
+        final T value = get(key, type);
+        if (value != null) {
+            return value;
+        } else {
+            return defValue;
+        }
+    }
+
+    /**
+     * Build a new rule from a property set resource.
+     * 
+     * @param resource a property set to initialize the map
+     * 
+     * @return the new rule 
+     * 
+     * @throws IOException if an error occurs
+     */
+    @Nonnull public static TranscodingRule fromResource(
+            @Nonnull @ParameterName(name="resource") final Resource resource) throws IOException {
+        
+        final Properties props = new Properties();
+    
+        try (final InputStream is = resource.getInputStream()) {
+            props.load(is);
+        }
+        
+        return new TranscodingRule(props);
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/package-info.java b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/package-info.java
similarity index 85%
rename from idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/package-info.java
rename to idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/package-info.java
index 748f560..ca23131 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/package-info.java
+++ b/idp-attribute-api/src/main/java/net/shibboleth/idp/attribute/transcoding/package-info.java
@@ -16,6 +16,7 @@
  */
 
 /**
- * Classes to do with the reverse mapping of SAML2 attributes into IdP Attributes. 
+ * APIs for transcoding of attributes.
  */
-package net.shibboleth.idp.saml.attribute.mapping.impl;
\ No newline at end of file
+
+package net.shibboleth.idp.attribute.transcoding;
\ No newline at end of file
diff --git a/idp-attribute-api/src/test/java/net/shibboleth/idp/attribute/AttributeTest.java b/idp-attribute-api/src/test/java/net/shibboleth/idp/attribute/AttributeTest.java
index 8d82637..bf3187e 100644
--- a/idp-attribute-api/src/test/java/net/shibboleth/idp/attribute/AttributeTest.java
+++ b/idp-attribute-api/src/test/java/net/shibboleth/idp/attribute/AttributeTest.java
@@ -18,15 +18,11 @@
 package net.shibboleth.idp.attribute;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Set;
 
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -48,9 +44,6 @@ public class AttributeTest {
         Assert.assertNotNull(attrib.getDisplayNames());
         Assert.assertTrue(attrib.getDisplayNames().isEmpty());
 
-        Assert.assertNotNull(attrib.getEncoders());
-        Assert.assertTrue(attrib.getEncoders().isEmpty());
-
         Assert.assertNotNull(attrib.getValues());
         Assert.assertTrue(attrib.getValues().isEmpty());
 
@@ -316,62 +309,6 @@ public class AttributeTest {
         Assert.assertTrue(attrib.getValues().contains(value2));
     }
 
-    /** Tests that values are properly added and modified. */
-    @Test public void encoders() {
-        AttributeEncoder<String> enc1 = new MockEncoder<>();
-        AttributeEncoder<String> enc2 = new MockEncoder<>();
-
-        IdPAttribute attrib = new IdPAttribute("foo");
-        Assert.assertTrue(attrib.getEncoders().isEmpty());
-        attrib.setEncoders(null);
-        Assert.assertTrue(attrib.getEncoders().isEmpty());
-        Collection collection = Arrays.asList((AttributeEncoder)null); 
-        attrib.setEncoders(collection);
-        Assert.assertTrue(attrib.getEncoders().isEmpty());
-
-        Set<AttributeEncoder<?>> attribEncoders = new HashSet<>();
-
-        // test adding one entry
-        Assert.assertTrue(attribEncoders.add(enc1));
-        attrib.setEncoders(attribEncoders);
-        Assert.assertFalse(attrib.getEncoders().isEmpty());
-        Assert.assertEquals(attrib.getEncoders().size(), 1);
-        Assert.assertTrue(attrib.getEncoders().contains(enc1));
-
-        // test adding another entry
-        Assert.assertTrue(attribEncoders.add(enc2));
-        attrib.setEncoders(attribEncoders);
-        Assert.assertFalse(attrib.getEncoders().isEmpty());
-        Assert.assertEquals(attrib.getEncoders().size(), 2);
-        Assert.assertTrue(attrib.getEncoders().contains(enc1));
-        Assert.assertTrue(attrib.getEncoders().contains(enc2));
-
-        // test adding null
-        
-        attribEncoders.add(null);
-        attrib.setEncoders(attribEncoders);
-        Assert.assertFalse(attrib.getEncoders().isEmpty());
-        Assert.assertEquals(attrib.getEncoders().size(), 2);
-        Assert.assertTrue(attrib.getEncoders().contains(enc1));
-        Assert.assertTrue(attrib.getEncoders().contains(enc2));
-
-        // test adding an existing Encoder
-        Assert.assertFalse(attribEncoders.add(enc2));
-        attrib.setEncoders(attribEncoders);
-        Assert.assertFalse(attrib.getEncoders().isEmpty());
-        Assert.assertEquals(attrib.getEncoders().size(), 2);
-        Assert.assertTrue(attrib.getEncoders().contains(enc1));
-        Assert.assertTrue(attrib.getEncoders().contains(enc2));
-
-        try {
-            attrib.getEncoders().add(null);
-            Assert.fail();
-        } catch (UnsupportedOperationException e) {
-            // OK
-        }
-        
-    }
-    
     @SuppressWarnings("unlikely-arg-type")
     @Test public void cloneToString() {
         IdPAttribute attrib = new IdPAttribute("foo");
diff --git a/idp-attribute-api/src/test/java/net/shibboleth/idp/attribute/MockEncoder.java b/idp-attribute-api/src/test/java/net/shibboleth/idp/attribute/MockEncoder.java
deleted file mode 100644
index 69d089f..0000000
--- a/idp-attribute-api/src/test/java/net/shibboleth/idp/attribute/MockEncoder.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute;
-
-import java.util.function.Predicate;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-
-import com.google.common.base.Predicates;
-
-/**
- * Mock implementation of {@link AttributeEncoder}.
- * 
- * @param <ValueType> type of value produced by this encoder
- */
-public class MockEncoder<ValueType> implements AttributeEncoder<ValueType> {
-
-    /** Static protocol string for this encoder. */
-    private String proto;
-
-    /** Static encoded attribute value for this encoder. */
-    private ValueType encodedValue;
-
-    /** Constructor. */
-    public MockEncoder() {
-
-    }
-
-    /**
-     * Constructor.
-     * 
-     * @param protocol static value to bereturned from {@link #getProtocol()}
-     * @param encodedAttributeValue static value to be returned from {@link #encode(IdPAttribute)}
-     */
-    public MockEncoder(String protocol, ValueType encodedAttributeValue) {
-        proto = protocol;
-        encodedValue = encodedAttributeValue;
-    }
-
-    /** {@inheritDoc} */
-    public String getProtocol() {
-        return proto;
-    }
-
-    /** {@inheritDoc} */
-    public ValueType encode(IdPAttribute attribute) throws AttributeEncodingException {
-        return encodedValue;
-    }
-    
-    /** {@inheritDoc} */
-    public Predicate<ProfileRequestContext> getActivationCondition() {
-        return Predicates.alwaysTrue();
-    }
-    
-}
\ No newline at end of file
diff --git a/idp-attribute-filter-api/.project b/idp-attribute-filter-api/.project
index a33e638..4b2ce71 100644
--- a/idp-attribute-filter-api/.project
+++ b/idp-attribute-filter-api/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-attribute-filter-impl/.project b/idp-attribute-filter-impl/.project
index 0815e7a..70a90af 100644
--- a/idp-attribute-filter-impl/.project
+++ b/idp-attribute-filter-impl/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/matcher/saml/impl/AttributeInMetadataMatcher.java b/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/matcher/saml/impl/AttributeInMetadataMatcher.java
index 584974f..eccc8cc 100644
--- a/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/matcher/saml/impl/AttributeInMetadataMatcher.java
+++ b/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/matcher/saml/impl/AttributeInMetadataMatcher.java
@@ -18,6 +18,7 @@
 package net.shibboleth.idp.attribute.filter.matcher.saml.impl;
 
 import java.time.Instant;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -26,15 +27,15 @@ import java.util.Set;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import net.shibboleth.idp.attribute.AttributeEncoder;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.IdPRequestedAttribute;
 import net.shibboleth.idp.attribute.StringAttributeValue;
 import net.shibboleth.idp.attribute.filter.Matcher;
 import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
-import net.shibboleth.idp.saml.attribute.encoding.SAML1AttributeEncoder;
-import net.shibboleth.idp.saml.attribute.encoding.SAML2AttributeEncoder;
+import net.shibboleth.idp.saml.attribute.transcoding.AttributesMapContainer;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.primitive.StringSupport;
@@ -56,7 +57,10 @@ import org.opensaml.saml.saml2.metadata.RequestedAttribute;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Predicates;
+import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
 
 
 /**
@@ -75,13 +79,13 @@ public class AttributeInMetadataMatcher extends AbstractIdentifiableInitializabl
     private boolean matchIfMetadataSilent;
     
     /** The SAML Attribute Name to look for in the metadata. */
-    private String attributeName;
+    @Nullable @NotEmpty private String attributeName;
 
     /** The SAML Attribute NameFormat to look for in the metadata. */
-    private String attributeNameFormat;
+    @Nullable @NotEmpty private String attributeNameFormat;
     
     /** The String used to prefix log message. */
-    private String logPrefix;
+    @Nullable @NotEmpty private String logPrefix;
 
     /**
      * Gets whether optionally requested attributes should be matched.
@@ -125,7 +129,7 @@ public class AttributeInMetadataMatcher extends AbstractIdentifiableInitializabl
      * 
      * @return the Name to look for
      */
-    @Nullable public String getAttributeName() {
+    @Nullable @NotEmpty public String getAttributeName() {
         return attributeName;
     }
     
@@ -137,7 +141,7 @@ public class AttributeInMetadataMatcher extends AbstractIdentifiableInitializabl
      * 
      * @param name the Name to look for
      */
-    public void setAttributeName(@Nullable final String name) {
+    public void setAttributeName(@Nullable @NotEmpty final String name) {
         attributeName = StringSupport.trimOrNull(name);
     }
 
@@ -147,7 +151,7 @@ public class AttributeInMetadataMatcher extends AbstractIdentifiableInitializabl
      * 
      * @return the Name to look for
      */
-    @Nullable public String getAttributeNameFormat() {
+    @Nullable @NotEmpty public String getAttributeNameFormat() {
         return attributeNameFormat;
     }
 
@@ -159,14 +163,14 @@ public class AttributeInMetadataMatcher extends AbstractIdentifiableInitializabl
      * 
      * @param format the NameFormat to look for
      */
-    public void setAttributeNameFormat(@Nullable final String format) {
+    public void setAttributeNameFormat(@Nullable @NotEmpty final String format) {
         attributeNameFormat = StringSupport.trimOrNull(format);
         if (attributeNameFormat != null && Attribute.UNSPECIFIED.equals(attributeNameFormat)) {
             attributeNameFormat = null;
         }
     }
 
-// Checkstyle: CyclomaticComplexity OFF
+// Checkstyle: CyclomaticComplexity|ReturnCount|MethodLength OFF
     /** {@inheritDoc} */
     @Override @Nonnull public Set<IdPAttributeValue<?>> getMatchingValues(@Nonnull final IdPAttribute attribute,
             @Nonnull final AttributeFilterContext filterContext) {
@@ -186,42 +190,79 @@ public class AttributeInMetadataMatcher extends AbstractIdentifiableInitializabl
             }
         }
     
-        RequestedAttribute requestedAttribute = null;
         if (attributeName != null) {
             // Look for a RequestedAttribute explicitly identified by config.
-            log.debug("Looking for RequestedAttribute {} (NameFormat {}) in descriptor", attributeName,
+            log.debug("Looking for RequestedAttribute {} (NameFormat {}) in metadata", attributeName,
                     attributeNameFormat);
-            requestedAttribute = findInMetadata(service, attributeName, attributeNameFormat);
-        } else {
-            final Set<AttributeEncoder<?>> encoders = attribute.getEncoders();
-            for (final AttributeEncoder<?> encoder : encoders) {
-                requestedAttribute = findInMetadata(service, encoder);
-                if (requestedAttribute != null) {
-                    break;
-                }
-            }
-        }
-    
-        final String attributeToLog = attributeName != null ? attributeName : attribute.getId();
+            final RequestedAttribute requestedAttribute = findInMetadata(service, attributeName, attributeNameFormat);
+            final String attributeToLog = attributeName != null ? attributeName : attribute.getId();
 
-        if (null == requestedAttribute) {
-            log.debug("{} Attribute {} not found in metadata", getLogPrefix(), attributeToLog);
-            return Collections.emptySet();
-        }
-    
-        if (requestedAttribute != null) {
+            if (null == requestedAttribute) {
+                log.debug("{} Attribute {} not found in metadata", getLogPrefix(), attributeToLog);
+                return Collections.emptySet();
+            }
+        
             if (onlyIfRequired && !requestedAttribute.isRequired()) {
                 log.debug("{} Attribute {} found in metadata, but was not required, values not matched",
                         getLogPrefix(), attributeToLog);
                 return Collections.emptySet();
             }
+            
+            final Set<IdPAttributeValue<?>> values = new HashSet<>();
+            values.addAll(filterValues(attributeToLog, attribute, requestedAttribute.getAttributeValues()));
+            return values;
+        } else {
+            // Utilize pre-mapped approach.
+            final List<AttributesMapContainer> containerList =
+                    service.getObjectMetadata().get(AttributesMapContainer.class);
+            if (null == containerList || containerList.isEmpty() || containerList.get(0).get() == null ||
+                    containerList.get(0).get().isEmpty()) {
+                log.debug("{} No decoded attributes found when filtering", getLogPrefix());
+                if (matchIfMetadataSilent) {
+                    // TODO: not sure what the right answer is here
+                    log.debug("{} The peer's metadata did not contain requested attribute information"
+                            + ", returning all the input values", getLogPrefix());
+                    return ImmutableSet.copyOf(attribute.getValues());
+                } else {
+                    log.debug("{} The peer's metadata did not contain requested attribute information"
+                            + ", returning no values", getLogPrefix());
+                    return Collections.emptySet();
+                }
+            }
+            if (containerList.size() > 1) {
+                log.error("{} More than one set of mapped attributes found when filtering, this shouldn't ever happen",
+                        getLogPrefix());
+            }
+            
+            final Multimap<String,? extends IdPAttribute> requestedAttributes = containerList.get(0).get();
+
+            final Collection<? extends IdPAttribute> requestedAttributeList =
+                    requestedAttributes.get(attribute.getId());
+            if (null == requestedAttributeList) {
+                log.debug("{} Decoded attribute {} not found in metadata", getLogPrefix(), attribute.getId());
+                return Collections.emptySet();
+            }
+
+            final Set<IdPAttributeValue<?>> values = new HashSet<>();
+
+            for (final IdPAttribute requestedAttribute
+                    : Collections2.filter(requestedAttributeList, Predicates.notNull())) {
+
+                if (requestedAttribute instanceof IdPRequestedAttribute
+                        && !((IdPRequestedAttribute) requestedAttribute).getIsRequired() && onlyIfRequired) {
+                    log.debug("{} Decoded attribute {} found in metadata, but not required, values not matched",
+                            getLogPrefix(), attribute.getId());
+                    continue;
+                }
+
+                values.addAll(filterValues(attribute, requestedAttribute.getValues()));
+            }
+            
+            return values;
         }
-        
-        final Set<IdPAttributeValue<?>> values = new HashSet<>();
-        values.addAll(filterValues(attributeToLog, attribute, requestedAttribute.getAttributeValues()));
-        return values;
+    
     }
-// Checkstyle: CyclomaticComplexity ON
+// Checkstyle: CyclomaticComplexity|ReturnCount|MethodLength ON
     
     /**
      * Get the appropriate {@link AttributeConsumingService}.
@@ -245,30 +286,6 @@ public class AttributeInMetadataMatcher extends AbstractIdentifiableInitializabl
     }
     
     /**
-     * Locates a {@link RequestedAttribute} object in metadata that matches the eventual "to be encoded" name
-     * represented by the encoder.
-     * 
-     * @param service the metadata descriptor to search
-     * @param encoder the attribute encoder to base the search on
-     * 
-     * @return a matching RequestedAttribute, or null
-     */
-    @Nullable private RequestedAttribute findInMetadata(@Nonnull final AttributeConsumingService service,
-            @Nonnull final AttributeEncoder encoder) {
-        String name = null;
-        String qualifier = null;
-        if (encoder instanceof SAML2AttributeEncoder) {
-            name = ((SAML2AttributeEncoder) encoder).getName();
-            qualifier = ((SAML2AttributeEncoder) encoder).getNameFormat();
-        } else if (encoder instanceof SAML1AttributeEncoder) {
-            name = ((SAML1AttributeEncoder) encoder).getName();
-            qualifier = ((SAML1AttributeEncoder) encoder).getNamespace();
-        }
-        
-        return findInMetadata(service, name, qualifier);
-    }
-    
-    /**
      * Locates a RequestedAttribute object in metadata that matches a specific Attribute Name
      * and NameFormat.
      * 
@@ -296,6 +313,34 @@ public class AttributeInMetadataMatcher extends AbstractIdentifiableInitializabl
     /**
      * Given an attribute and the requested values do the filtering.
      * 
+     * @param attribute the attribute
+     * @param requestedValues the values
+     * @return the result of the filter
+     */
+    @Nonnull private Set<IdPAttributeValue<?>> filterValues(@Nullable final IdPAttribute attribute,
+            @Nonnull @NonnullElements final List<IdPAttributeValue<?>> requestedValues) {
+
+        if (null == requestedValues || requestedValues.isEmpty()) {
+            log.debug("{} Attribute {} found in metadata and no values specified", getLogPrefix(), attribute.getId());
+            return ImmutableSet.copyOf(attribute.getValues());
+        }
+
+        final ImmutableSet.Builder<IdPAttributeValue<?>> builder = ImmutableSet.builder();
+
+        for (final IdPAttributeValue attributeValue : attribute.getValues()) {
+            if (requestedValues.contains(attributeValue)) {
+                builder.add(attributeValue);
+            }
+        }
+        
+        final ImmutableSet<IdPAttributeValue<?>> result = builder.build();
+        log.debug("{} Values matched with metadata for Attribute {} : {}", getLogPrefix(), attribute.getId(), result);
+        return result;
+    }
+    
+    /**
+     * Given an attribute and the requested values do the filtering.
+     * 
      * @param attributeToLog name of attribute to log
      * @param attribute the attribute
      * @param requestedValues the values
diff --git a/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/matcher/saml/impl/MappedAttributeInMetadataMatcher.java b/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/matcher/saml/impl/MappedAttributeInMetadataMatcher.java
deleted file mode 100644
index 4109814..0000000
--- a/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/matcher/saml/impl/MappedAttributeInMetadataMatcher.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.filter.matcher.saml.impl;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.IdPRequestedAttribute;
-import net.shibboleth.idp.attribute.filter.Matcher;
-import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
-import net.shibboleth.idp.saml.attribute.mapping.AttributesMapContainer;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.saml.common.messaging.context.AttributeConsumingServiceContext;
-import org.opensaml.saml.common.messaging.context.SAMLMetadataContext;
-import org.opensaml.saml.common.messaging.context.navigate.AttributeConsumerServiceLookupFunction;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
-
-/**
- * Matcher that checks whether an attribute is enumerated in an SP's metadata as a required or optional attribute.
- * Also supports simple value filtering.
- * 
- * <p>This matcher compares attributes from the metadata that have been mapped into {@link IdPAttribute}s.</p> 
- */
-public class MappedAttributeInMetadataMatcher extends AbstractIdentifiableInitializableComponent implements Matcher {
-
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(MappedAttributeInMetadataMatcher.class);
-
-    /** Whether optionally requested attributes should be matched. */
-    private boolean onlyIfRequired = true;
-
-    /** Whether to return a match if the metadata does not contain an ACS descriptor. */
-    private boolean matchIfMetadataSilent;
-
-    /** The String used to prefix log message. */
-    private String logPrefix;
-
-    /** The strategy to get the appropriate XMLObject from the context. */
-    @Nonnull private Function<SAMLMetadataContext, ? extends XMLObject> objectStrategy;
-    
-    /** Constructor. */
-    public MappedAttributeInMetadataMatcher() {
-        objectStrategy = new AttributeConsumerServiceLookupFunction().compose(
-                new ChildContextLookup<>(AttributeConsumingServiceContext.class));
-    }
-
-    /**
-     * Gets whether optionally requested attributes should be matched.
-     * 
-     * @return Whether optionally requested attributes should be matched.
-     */
-    public boolean getOnlyIfRequired() {
-        return onlyIfRequired;
-    }
-
-    /**
-     * Sets whether optionally requested attributes should be matched.
-     * 
-     * @param flag whether optionally requested attributes should be matched
-     */
-    public void setOnlyIfRequired(final boolean flag) {
-        onlyIfRequired = flag;
-    }
-
-    /**
-     * Get the strategy to get the appropriate XMLObject from the context.
-     * 
-     * @return Returns the strategy.
-     */
-    public Function<SAMLMetadataContext, ? extends XMLObject> getXMLObjectStrategy() {
-        return objectStrategy;
-    }
-
-    /**
-     * Set the strategy to get the appropriate XMLObject from the context.
-     * 
-     * @param strategy what to set.
-     */
-    public void setObjectStrategy(@Nonnull final Function<SAMLMetadataContext, ? extends XMLObject> strategy) {
-        objectStrategy = Constraint.isNotNull(strategy, "ObjectStrategy must be non null");
-    }
-
-    /**
-     * Gets whether to matched if the metadata contains no AttributeConsumingService.
-     * 
-     * @return whether to match if the metadata contains no AttributeConsumingService
-     */
-    public boolean getMatchIfMetadataSilent() {
-        return matchIfMetadataSilent;
-    }
-
-    /**
-     * Sets whether to match if the metadata contains no AttributeConsumingService.
-     * 
-     * @param flag whether to match if the metadata contains no AttributeConsumingService
-     */
-    public void setMatchIfMetadataSilent(final boolean flag) {
-        matchIfMetadataSilent = flag;
-    }
-
-    /**
-     * Get the appropriate map of mapped attributes.
-     * 
-     * @param filterContext the context for the operation
-     * @return the map, or null.
-     */
-    @Nullable protected Multimap<String, ? extends IdPAttribute> getRequestedAttributes(
-            @Nonnull final AttributeFilterContext filterContext) {
-        final SAMLMetadataContext metadataContext = filterContext.getRequesterMetadataContext();
-        if (null == metadataContext) {
-            log.warn("{} No metadata context when filtering", getLogPrefix());
-            return null;
-        }
-
-        final XMLObject xmlObject = getXMLObjectStrategy().apply(metadataContext);
-        if (null == xmlObject) {
-            log.warn("{} No RP XML Object found when filtering", getLogPrefix());
-            return null;
-        }
-        final List<AttributesMapContainer> containerList =
-                xmlObject.getObjectMetadata().get(AttributesMapContainer.class);
-        if (null == containerList || containerList.isEmpty()) {
-            log.debug("{} No mapped attributes found when filtering", getLogPrefix());
-            return null;
-        }
-        if (containerList.size() > 1) {
-            log.warn("{} More than one set of mapped attributes found when filtering, please report an error",
-                    getLogPrefix());
-        }
-        return containerList.get(0).get();
-    }
-
-    /** {@inheritDoc} */
-    @Override @Nonnull public Set<IdPAttributeValue<?>> getMatchingValues(@Nonnull final IdPAttribute attribute,
-            @Nonnull final AttributeFilterContext filterContext) {
-
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-        final Multimap<String, ? extends IdPAttribute> requestedAttributes = getRequestedAttributes(filterContext);
-
-        if (null == requestedAttributes || requestedAttributes.isEmpty()) {
-            if (matchIfMetadataSilent) {
-                log.debug("{} The peer's metadata did not have appropriate requested attributes available"
-                        + ", returning all the input values", getLogPrefix());
-                return ImmutableSet.copyOf(attribute.getValues());
-            } else {
-                log.debug("{} The peer's metadata did not have appropriate requested attributes available"
-                        + ", returning no values", getLogPrefix());
-                return Collections.emptySet();
-            }
-        }
-
-        final Collection<? extends IdPAttribute> requestedAttributeList = requestedAttributes.get(attribute.getId());
-
-        if (null == requestedAttributeList) {
-            log.debug("{} Attribute {} not found in metadata", getLogPrefix(), attribute.getId());
-            return Collections.emptySet();
-        }
-
-        final Set<IdPAttributeValue<?>> values = new HashSet<>();
-
-        for (final IdPAttribute requestedAttribute : requestedAttributeList) {
-
-            if (null == requestedAttribute) {
-                log.info("{} Attribute {} found in metadata but with no values that"
-                        + " could be decoded, values not matched", getLogPrefix(), attribute.getId());
-                continue;
-            }
-
-            if (requestedAttribute instanceof IdPRequestedAttribute
-                    && !((IdPRequestedAttribute) requestedAttribute).getIsRequired() && onlyIfRequired) {
-                log.debug("{} Attribute {} found in metadata, but was not required, values not matched",
-                        getLogPrefix(), attribute.getId());
-                continue;
-            }
-
-            values.addAll(filterValues(attribute, requestedAttribute.getValues()));
-        }
-        return values;
-    }
-
-    /**
-     * Given an attribute and the requested values do the filtering.
-     * 
-     * @param attribute the attribute
-     * @param requestedValues the values
-     * @return the result of the filter
-     */
-    @Nonnull private Set<IdPAttributeValue<?>> filterValues(@Nullable final IdPAttribute attribute,
-            @Nonnull @NonnullElements final List<IdPAttributeValue<?>> requestedValues) {
-
-        if (null == requestedValues || requestedValues.isEmpty()) {
-            log.debug("{} Attribute {} found in metadata and no values specified", getLogPrefix(), attribute.getId());
-            return ImmutableSet.copyOf(attribute.getValues());
-        }
-
-        final ImmutableSet.Builder<IdPAttributeValue<?>> builder = ImmutableSet.builder();
-
-        for (final IdPAttributeValue attributeValue : attribute.getValues()) {
-            if (requestedValues.contains(attributeValue)) {
-                builder.add(attributeValue);
-            }
-        }
-        
-        final ImmutableSet<IdPAttributeValue<?>> result = builder.build();
-        log.debug("{} Values matched with metadata for Attribute {} : {}", getLogPrefix(), attribute.getId(), result);
-        return result;
-    }
-
-    /**
-     * return a string which is to be prepended to all log messages.
-     * 
-     * @return "Attribute Filter '<filterID>' :"
-     */
-    @Nonnull protected String getLogPrefix() {
-        // local cache of cached entry to allow unsynchronised clearing.
-        String prefix = logPrefix;
-        if (null == prefix) {
-            final StringBuilder builder = new StringBuilder("Attribute Filter '").append(getId()).append("':");
-            prefix = builder.toString();
-            if (null == logPrefix) {
-                logPrefix = prefix;
-            }
-        }
-        return prefix;
-    }
-
-}
\ No newline at end of file
diff --git a/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/matcher/saml/impl/AttributeInMetadataMatcherTest.java b/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/matcher/saml/impl/AttributeInMetadataMatcherTest.java
index d9405d4..50c5e90 100644
--- a/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/matcher/saml/impl/AttributeInMetadataMatcherTest.java
+++ b/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/matcher/saml/impl/AttributeInMetadataMatcherTest.java
@@ -17,10 +17,7 @@
 
 package net.shibboleth.idp.attribute.filter.matcher.saml.impl;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.*;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -43,12 +40,10 @@ import org.opensaml.saml.saml2.metadata.RequestedAttribute;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import net.shibboleth.idp.attribute.AttributeEncoder;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.IdPAttributeValue;
 import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
 import net.shibboleth.idp.attribute.filter.matcher.impl.DataSources;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML2StringAttributeEncoder;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 
 /**
@@ -73,12 +68,6 @@ public class AttributeInMetadataMatcherTest extends OpenSAMLInitBaseTestCase {
     private IdPAttribute makeAttribute(String id, List<IdPAttributeValue<?>> values) {
         final IdPAttribute attr = new IdPAttribute(id);
         attr.setValues(values);
-        
-        final SAML2StringAttributeEncoder encoder = new SAML2StringAttributeEncoder();
-        encoder.setName(id);
-        encoder.setNameFormat(Attribute.BASIC);
-        
-        attr.setEncoders(Collections.<AttributeEncoder<?>>singleton(encoder));
         return attr;
     }
 
@@ -155,7 +144,7 @@ public class AttributeInMetadataMatcherTest extends OpenSAMLInitBaseTestCase {
         final IdPAttribute attr =
                 makeAttribute("attr", Arrays.asList(DataSources.STRING_VALUE, DataSources.NON_MATCH_STRING_VALUE));
 
-        final AttributeInMetadataMatcher matcher = makeMatcher("test", true, false, null, null);
+        final AttributeInMetadataMatcher matcher = makeMatcher("test", true, false, "wrongAttr", Attribute.URI_REFERENCE);
         Set<IdPAttributeValue<?>> result = matcher.getMatchingValues(attr, makeContext(null));
 
         assertEquals(result.size(), 2);
@@ -197,13 +186,14 @@ public class AttributeInMetadataMatcherTest extends OpenSAMLInitBaseTestCase {
         req.setNameFormat(Attribute.BASIC);
         final AttributeFilterContext context = makeContext(req);
 
-        Set<IdPAttributeValue<?>> result = makeMatcher("test", false, false, null, null).getMatchingValues(attr, context);
+        Set<IdPAttributeValue<?>> result =
+                makeMatcher("test", false, false, "attr", Attribute.BASIC).getMatchingValues(attr, context);
 
         assertEquals(result.size(), 2);
         assertTrue(result.contains(DataSources.STRING_VALUE));
         assertTrue(result.contains(DataSources.NON_MATCH_STRING_VALUE));
 
-        result = makeMatcher("test", false, true, null, null).getMatchingValues(attr, context);
+        result = makeMatcher("test", false, true, "attr", Attribute.BASIC).getMatchingValues(attr, context);
         assertTrue(result.isEmpty());
     }
 
@@ -222,7 +212,8 @@ public class AttributeInMetadataMatcherTest extends OpenSAMLInitBaseTestCase {
 
         final AttributeFilterContext context = makeContext(req);
 
-        Set<IdPAttributeValue<?>> result = makeMatcher("test", false, true, null, null).getMatchingValues(attr, context);
+        final Set<IdPAttributeValue<?>> result =
+                makeMatcher("test", false, true, "attr", Attribute.BASIC).getMatchingValues(attr, context);
         assertEquals(result.size(), 1);
         assertTrue(result.contains(DataSources.STRING_VALUE));
     }
@@ -249,7 +240,8 @@ public class AttributeInMetadataMatcherTest extends OpenSAMLInitBaseTestCase {
 
         setRequestedAttributesInContext(context, Collections.singletonList(req));
 
-        Set<IdPAttributeValue<?>> result = makeMatcher("test", false, true, null, null).getMatchingValues(attr, context);
+        final Set<IdPAttributeValue<?>> result =
+                makeMatcher("test", false, true, "attr", Attribute.BASIC).getMatchingValues(attr, context);
         assertEquals(result.size(), 2);
         assertTrue(result.contains(DataSources.STRING_VALUE));
         assertTrue(result.contains(DataSources.NON_MATCH_STRING_VALUE));
diff --git a/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/matcher/saml/impl/MappedAttributeInMetadataMatcherTest.java b/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/matcher/saml/impl/MappedAttributeInMetadataMatcherTest.java
index 9b875fa..8063fa1 100644
--- a/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/matcher/saml/impl/MappedAttributeInMetadataMatcherTest.java
+++ b/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/matcher/saml/impl/MappedAttributeInMetadataMatcherTest.java
@@ -44,11 +44,11 @@ import net.shibboleth.idp.attribute.IdPAttributeValue;
 import net.shibboleth.idp.attribute.IdPRequestedAttribute;
 import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
 import net.shibboleth.idp.attribute.filter.matcher.impl.DataSources;
-import net.shibboleth.idp.saml.attribute.mapping.AttributesMapContainer;
+import net.shibboleth.idp.saml.attribute.transcoding.AttributesMapContainer;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 
 /**
- * Tests for {@link MappedAttributeInMetadataMatcher}.
+ * Tests for {@link AttributeInMetadataMatcher} using decoded attributes.
  */
 public class MappedAttributeInMetadataMatcherTest extends OpenSAMLInitBaseTestCase {
     
@@ -66,9 +66,9 @@ public class MappedAttributeInMetadataMatcherTest extends OpenSAMLInitBaseTestCa
         return attr;
     }
 
-    private MappedAttributeInMetadataMatcher makeMatcher(String id, boolean matchIfMetadataSilent, boolean onlyIfRequired)
+    private AttributeInMetadataMatcher makeMatcher(String id, boolean matchIfMetadataSilent, boolean onlyIfRequired)
             throws ComponentInitializationException {
-        MappedAttributeInMetadataMatcher matcher = new MappedAttributeInMetadataMatcher();
+        AttributeInMetadataMatcher matcher = new AttributeInMetadataMatcher();
         matcher.setMatchIfMetadataSilent(matchIfMetadataSilent);
         matcher.setOnlyIfRequired(onlyIfRequired);
         matcher.setId(id);
@@ -109,7 +109,7 @@ public class MappedAttributeInMetadataMatcherTest extends OpenSAMLInitBaseTestCa
     }
 
     @Test public void getters() throws ComponentInitializationException {
-        MappedAttributeInMetadataMatcher matcher = makeMatcher("test", true, true);
+        AttributeInMetadataMatcher matcher = makeMatcher("test", true, true);
         assertTrue(matcher.getMatchIfMetadataSilent());
         assertTrue(matcher.getOnlyIfRequired());
 
@@ -139,7 +139,7 @@ public class MappedAttributeInMetadataMatcherTest extends OpenSAMLInitBaseTestCa
         final IdPAttribute attr =
                 makeAttribute("attr", Arrays.asList(DataSources.STRING_VALUE, DataSources.NON_MATCH_STRING_VALUE));
 
-        final MappedAttributeInMetadataMatcher matcher = makeMatcher("test", true, true);
+        final AttributeInMetadataMatcher matcher = makeMatcher("test", true, true);
         Set<IdPAttributeValue<?>> result = matcher.getMatchingValues(attr, makeContext(null));
 
         assertEquals(result.size(), 2);
diff --git a/idp-attribute-filter-spring/.project b/idp-attribute-filter-spring/.project
index 142d855..200fb1b 100644
--- a/idp-attribute-filter-spring/.project
+++ b/idp-attribute-filter-spring/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/saml/impl/MappedAttributeInMetadataRuleParser.java b/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/saml/impl/MappedAttributeInMetadataRuleParser.java
index a73fe2f..d728030 100644
--- a/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/saml/impl/MappedAttributeInMetadataRuleParser.java
+++ b/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/saml/impl/MappedAttributeInMetadataRuleParser.java
@@ -20,9 +20,11 @@ package net.shibboleth.idp.attribute.filter.spring.saml.impl;
 import javax.annotation.Nonnull;
 import javax.xml.namespace.QName;
 
-import net.shibboleth.idp.attribute.filter.matcher.saml.impl.MappedAttributeInMetadataMatcher;
+import net.shibboleth.idp.attribute.filter.matcher.saml.impl.AttributeInMetadataMatcher;
 import net.shibboleth.idp.attribute.filter.spring.BaseFilterParser;
 import net.shibboleth.idp.attribute.filter.spring.matcher.BaseAttributeValueMatcherParser;
+import net.shibboleth.utilities.java.support.primitive.DeprecationSupport;
+import net.shibboleth.utilities.java.support.primitive.DeprecationSupport.ObjectType;
 import net.shibboleth.utilities.java.support.primitive.StringSupport;
 
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -39,13 +41,19 @@ public class MappedAttributeInMetadataRuleParser extends BaseAttributeValueMatch
             "MappedAttributeInMetadata");
 
     /** {@inheritDoc} */
-    @Override @Nonnull protected Class<MappedAttributeInMetadataMatcher> getNativeBeanClass() {
-        return MappedAttributeInMetadataMatcher.class;
+    @Override @Nonnull protected Class<AttributeInMetadataMatcher> getNativeBeanClass() {
+        return AttributeInMetadataMatcher.class;
     }
 
     /** {@inheritDoc} */
     @Override protected void doNativeParse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
             @Nonnull final BeanDefinitionBuilder builder) {
+        
+        // V4 deprecation
+        DeprecationSupport.warn(ObjectType.XSITYPE, SCHEMA_TYPE.toString(),
+                parserContext.getReaderContext().getResource().getDescription(),
+                AttributeInMetadataRuleParser.SCHEMA_TYPE.toString());
+        
         super.doParse(config, builder);
 
         if (config.hasAttributeNS(null, "onlyIfRequired")) {
diff --git a/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/saml/MappedAttributeInMetadataRuleParserTest.java b/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/saml/MappedAttributeInMetadataRuleParserTest.java
index a97cfac..e0d83f2 100644
--- a/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/saml/MappedAttributeInMetadataRuleParserTest.java
+++ b/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/saml/MappedAttributeInMetadataRuleParserTest.java
@@ -26,7 +26,7 @@ import net.shibboleth.ext.spring.context.FilesystemGenericApplicationContext;
 import net.shibboleth.idp.attribute.filter.AttributeRule;
 import net.shibboleth.idp.attribute.filter.PolicyFromMatcher;
 import net.shibboleth.idp.attribute.filter.PolicyRequirementRule;
-import net.shibboleth.idp.attribute.filter.matcher.saml.impl.MappedAttributeInMetadataMatcher;
+import net.shibboleth.idp.attribute.filter.matcher.saml.impl.AttributeInMetadataMatcher;
 import net.shibboleth.idp.attribute.filter.spring.BaseAttributeFilterParserTest;
 import net.shibboleth.idp.attribute.filter.spring.saml.impl.MappedAttributeInMetadataRuleParser;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
@@ -43,14 +43,14 @@ public class MappedAttributeInMetadataRuleParserTest extends  BaseAttributeFilte
         
         final AttributeRule rule = getBean(MATCHER_PATH + "mappedInMetadata.xml", AttributeRule.class, context);
         rule.initialize();
-        MappedAttributeInMetadataMatcher matcher = (MappedAttributeInMetadataMatcher) rule.getMatcher();
+        AttributeInMetadataMatcher matcher = (AttributeInMetadataMatcher) rule.getMatcher();
      
         assertTrue(matcher.getMatchIfMetadataSilent());
         assertTrue(matcher.getOnlyIfRequired());
         assertTrue(matcher.getId().endsWith(":PermitRule"));
     
         final PolicyFromMatcher policyRule = (PolicyFromMatcher) getBean(PolicyRequirementRule.class, context);
-        matcher = (MappedAttributeInMetadataMatcher) policyRule.getMatcher();
+        matcher = (AttributeInMetadataMatcher) policyRule.getMatcher();
         assertTrue(matcher.getMatchIfMetadataSilent());
         assertTrue(matcher.getOnlyIfRequired());
         assertTrue(matcher.getId().endsWith(":PRR"));
diff --git a/idp-attribute-impl/.project b/idp-attribute-impl/.project
index 93b9a0c..3d22eed 100644
--- a/idp-attribute-impl/.project
+++ b/idp-attribute-impl/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.springframework.ide.eclipse.boot.validation.springbootbuilder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-attribute-impl/pom.xml b/idp-attribute-impl/pom.xml
index a5329d5..1587f93 100644
--- a/idp-attribute-impl/pom.xml
+++ b/idp-attribute-impl/pom.xml
@@ -24,6 +24,12 @@
             <version>${project.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>idp-profile-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
         <!-- Provided Dependencies -->
 
         <!-- Runtime Dependencies -->
diff --git a/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryImpl.java b/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryImpl.java
new file mode 100644
index 0000000..24262d3
--- /dev/null
+++ b/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryImpl.java
@@ -0,0 +1,357 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.transcoding.impl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import net.shibboleth.ext.spring.service.AbstractServiceableComponent;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.profile.logic.RelyingPartyIdPredicate;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
+
+/** Service implementation of the {@link AttributeTranscoderRegistry} interface. */
+ at ThreadSafe
+public class AttributeTranscoderRegistryImpl extends AbstractServiceableComponent<AttributeTranscoderRegistry>
+        implements AttributeTranscoderRegistry {
+
+    /** Bean name for identifying an {@link AttributeTranscoder} object to install. */
+    @Nonnull @NotEmpty static final String PROP_TRANSCODER_BEAN = "transcoder_bean";
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(AttributeTranscoderRegistryImpl.class);
+    
+    /** Registry of transcoding instructions for a given "name" and type of object. */
+    @Nonnull private final Map<String,Multimap<Class<?>,TranscodingRule>> transcodingRegistry;
+    
+    /** Registry of naming functions for supported object types. */
+    @Nonnull private final Map<Class<?>,Function<?,String>> namingFunctionRegistry;
+    
+    /** Constructor. */
+    public AttributeTranscoderRegistryImpl() {
+        transcodingRegistry = new HashMap<>();
+        namingFunctionRegistry = new HashMap<>();
+    }
+    
+    /** {@inheritDoc} */
+    @Override @Nonnull public AttributeTranscoderRegistry getComponent() {
+        return this;
+    }
+    
+    /**
+     * Installs registry of naming functions mapped against the types of objects they support.
+     * 
+     * @param registry map of types to naming functions
+     */
+    public void setNamingRegistry(@Nonnull @NonnullElements final Map<Class<?>,Function<?,String>> registry) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        namingFunctionRegistry.clear();
+        
+        if (registry == null) {
+            return;
+        }
+        
+        registry.forEach((k,v) -> {
+            if (k != null && v != null) {
+                namingFunctionRegistry.put(k, v);
+            }
+        });
+    }
+
+    /**
+     * Installs the transcoder mappings en masse.
+     * 
+     * <p>Each map connects an {@link IdPAttribute} name to the rules for transcoding to/from it.</p>
+     * 
+     * <p>The rules MUST contain at least:</p>
+     * <ul>
+     *  <li>{@link #PROP_ID} - internal attribute ID to map to/from</li>
+     *  <li>{@link #PROP_TRANSCODER} - an {@link AttributeTranscoder} instance supporting the type</li>
+     * </ul>
+     * 
+     * Transcoders will generally require particular properties in their own right to function.
+     * 
+     * @param mappings transcoding rulesets
+     */
+    public void setTranscoderRegistry(@Nonnull @NonnullElements final Collection<TranscodingRule> mappings) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+       transcodingRegistry.clear();
+
+        if (mappings == null) {
+            return;
+        }
+        
+        for (final TranscodingRule mapping : Collections2.filter(mappings, Predicates.notNull())) {
+            
+            final String internalId = StringSupport.trimOrNull(mapping.get(PROP_ID, String.class));
+            if (internalId != null) {
+                final Predicate activationCondition = buildActivationCondition(mapping.getMap());
+                if (activationCondition != null) {
+                    mapping.getMap().put(PROP_CONDITION, activationCondition);
+                } else {
+                    mapping.getMap().remove(PROP_CONDITION);
+                }
+                
+                final AttributeTranscoder transcoder = buildAttributeTranscoder(mapping);
+                if (transcoder != null) {
+                    mapping.getMap().put(PROP_TRANSCODER, transcoder);
+                    addMapping(internalId, transcoder, mapping.getMap());
+                } else {
+                    log.warn("Unable to locate or build an AttributeTranscoder in rule for {}", internalId);
+                }
+            }
+        }
+    }
+    
+    /** {@inheritDoc} */
+    @Nonnull @NonnullElements @Unmodifiable public Collection<TranscodingRule> getTranscodingRules(
+            @Nonnull final IdPAttribute from, @Nonnull final Class<?> to) {
+        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+        Constraint.isNotNull(from, "IdPAttribute cannot be null");
+        Constraint.isNotNull(to, "Target type cannot be null");
+        
+        final Multimap<Class<?>,TranscodingRule> propertyCollections = transcodingRegistry.get(from.getId());
+        if (propertyCollections == null) {
+            return Collections.emptyList();
+        }
+        
+        final Class<?> effectiveType = getEffectiveType(to);
+        if (effectiveType == null) {
+            log.warn("Unsupported object type: {}", to.getClass().getName());
+            return Collections.emptyList();
+        }
+        
+        log.trace("Using rules for effective type {}", effectiveType.getName());
+        
+        return ImmutableList.copyOf(propertyCollections.get(effectiveType));
+    }
+
+    /** {@inheritDoc} */
+    @Nonnull @NonnullElements @Unmodifiable public <T> Collection<TranscodingRule> getTranscodingRules(
+            @Nonnull final T from) {
+        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+        Constraint.isNotNull(from, "Input object cannot be null");
+        
+        final Class<?> effectiveType = getEffectiveType(from.getClass());
+        if (effectiveType == null) {
+            log.warn("Unsupported object type: {}", from.getClass().getName());
+            return Collections.emptyList();
+        }
+        
+        log.trace("Using rules for effective type {}", effectiveType.getName());
+        
+        final Function<?,String> namingFunction = namingFunctionRegistry.get(effectiveType);
+        
+        // Don't know if we can work around this cast or not.
+        final String id = ((Function<? super T,String>) namingFunction).apply(from);
+        if (id != null) {
+            final Multimap<Class<?>,TranscodingRule> propertyCollections = transcodingRegistry.get(id);
+            
+            return propertyCollections != null ? ImmutableList.copyOf(propertyCollections.get(effectiveType))
+                    : Collections.emptyList();
+        } else {
+            log.warn("Object of type {} did not have a canonical name", from.getClass().getName());
+        }
+        
+        return Collections.emptyList();
+    }
+    
+    /**
+     * Get the appropriate {@link AttributeTranscoder} to use.
+     * 
+     * @param rule transcoding rule
+     * 
+     * @return a transcoder to install under the ruleset's {@link #PROP_TRANSCODER}
+     */
+    @Nullable private AttributeTranscoder buildAttributeTranscoder(@Nonnull final TranscodingRule rule) {
+        
+        AttributeTranscoder transcoder = rule.get(PROP_TRANSCODER, AttributeTranscoder.class);
+        if (transcoder != null) {
+            return transcoder;
+        }
+        
+        final String type = rule.get(PROP_TRANSCODER_CLASS, String.class);
+        if (type != null) {
+            try {
+                transcoder = (AttributeTranscoder) Class.forName(type).getDeclaredConstructor().newInstance();
+                transcoder.initialize();
+                return transcoder;
+            } catch (final InstantiationException | IllegalAccessException | IllegalArgumentException
+                    | InvocationTargetException | NoSuchMethodException | SecurityException
+                    | ClassNotFoundException | ComponentInitializationException e) {
+                log.warn("Unable to create AttributeTranscoder of specified type {}", type, e);
+                return null;
+            }
+        }
+        
+        final String id = rule.get(PROP_TRANSCODER_BEAN, String.class);
+        if (id != null) {
+            try {
+                transcoder = getApplicationContext().getBean(id, AttributeTranscoder.class);
+                transcoder.initialize();
+                return transcoder;
+            } catch (final Exception e) {
+                log.warn("Unable to locate AttributeTranscoder bean named {}", id, e);
+                return null;
+            }
+        }
+
+        return null;
+    }
+    
+    /**
+     * Add a mapping between an {@link IdPAttribute} name and a set of transcoding rules.
+     * 
+     * @param id name of the {@link IdPAttribute} to map to/from
+     * @param transcoder the transcoder for this rule
+     * @param ruleset transcoding rules
+     */
+    private void addMapping(@Nonnull @NotEmpty final String id, @Nonnull final AttributeTranscoder transcoder,
+            @Nonnull final Map<String,Object> ruleset) {
+
+        
+        final TranscodingRule copy = new TranscodingRule(ruleset);
+
+        final Class<?> type = transcoder.getEncodedType();
+        final String targetName = transcoder.getEncodedName(copy);
+        if (targetName != null) {
+            
+            log.debug("Attribute mapping: {} <-> {} via {}", id, targetName, transcoder.getClass().getSimpleName());
+            
+            // Install mapping back to IdPAttribute's trimmed name.
+            copy.getMap().put(PROP_ID, id);
+            
+            Multimap<Class<?>,TranscodingRule> rulesetsForIdPName = transcodingRegistry.get(id);
+            if (rulesetsForIdPName == null) {
+                rulesetsForIdPName = ArrayListMultimap.create();
+                transcodingRegistry.put(id, rulesetsForIdPName);
+            }
+            
+            rulesetsForIdPName.put(type, copy);
+
+            Multimap<Class<?>,TranscodingRule> rulesetsForEncodedName = transcodingRegistry.get(targetName);
+            if (rulesetsForEncodedName == null) {
+                rulesetsForEncodedName = ArrayListMultimap.create();
+                transcodingRegistry.put(targetName, rulesetsForEncodedName);
+            }
+            
+            rulesetsForEncodedName.put(type, copy);
+            
+        } else {
+            log.warn("Transcoding rule for {} into type {} did not produce an encoded name", id, type.getName());
+        }
+    }
+    
+    /**
+     * Build an appropriate {@link Predicate} to use as an activation condition within the ruleset.
+     * 
+     * @param ruleset transcoding rules
+     * 
+     * @return a predicate to install under the ruleset's {@link #PROP_CONDITION}
+     */
+    @Nullable private Predicate<ProfileRequestContext> buildActivationCondition(
+            @Nonnull final Map<String,Object> ruleset) {
+        
+        Predicate effectiveCondition = null;
+        
+        final Object baseCondition = ruleset.get(PROP_CONDITION);
+        if (baseCondition instanceof Predicate) {
+            effectiveCondition = (Predicate) baseCondition;
+        } else if (baseCondition != null) {
+            log.error("{} property did not contain a Predicate object, ignored", PROP_CONDITION);
+        }
+
+        Predicate relyingPartyCondition = null;
+
+        final Object relyingParties = ruleset.get(PROP_RELYINGPARTIES);
+        if (relyingParties instanceof Collection) {
+            relyingPartyCondition = new RelyingPartyIdPredicate((Collection<String>) relyingParties);
+        } else if (relyingParties instanceof String) {
+            final Collection<String> parsed = StringSupport.normalizeStringCollection(
+                    StringSupport.stringToList((String) relyingParties, " "));
+            relyingPartyCondition = new RelyingPartyIdPredicate(parsed);
+        } else if (relyingParties != null) {
+            log.error("{} property did not contain a Collection or String, ignored", PROP_RELYINGPARTIES);
+        }
+        
+        if (effectiveCondition == null) {
+            return relyingPartyCondition;
+        } else if (relyingPartyCondition != null) {
+            return effectiveCondition.and(relyingPartyCondition);
+        } else {
+            return effectiveCondition;
+        }
+    }
+
+    /**
+     * Convert an input type into the appropriate type (possibly itself) to use in looking up
+     * rules in the registry.
+     * 
+     * @param inputType the type passed into the registry operation
+     * 
+     * @return the appropriate type to use subsequently or null if not found
+     */
+    @Nullable private Class<?> getEffectiveType(@Nonnull final Class<?> inputType) {
+        
+        // Check for explicit support.
+        if (namingFunctionRegistry.containsKey(inputType)) {
+            return inputType;
+        }
+        
+        // Try each map entry for a match. Optimized around the assumption the
+        // map will be fairly small.
+        for (final Class<?> candidate : namingFunctionRegistry.keySet()) {
+            if (candidate.isAssignableFrom(inputType)) {
+                return candidate;
+            }
+        }
+        
+        return null;
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/TranscodingRuleLoader.java b/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/TranscodingRuleLoader.java
new file mode 100644
index 0000000..844e40c
--- /dev/null
+++ b/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/TranscodingRuleLoader.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.transcoding.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.FileSystemResource;
+
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.utilities.java.support.annotation.ParameterName;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+
+/**
+ * Wrapper around a {@link Map} representing a rule for transcoding, used to
+ * detect and load the rules at runtime from a Spring context.
+ */
+public class TranscodingRuleLoader {
+    
+    /** Class logger. */
+    @Nonnull private Logger log = LoggerFactory.getLogger(TranscodingRuleLoader.class);
+    
+    /** Rules loaded. */
+    private @Nonnull @NonnullElements final Collection<TranscodingRule> rules;
+    
+    /**
+     * Load rules from all files found below a directory root.
+     * 
+     * <p>Individual rules that fail to load will be skipped.</p>
+     * 
+     * @param dir root to search
+     * 
+     * @throws IOException if an error occurs
+     */
+    public TranscodingRuleLoader(@Nonnull @ParameterName(name="dir") final Path dir) throws IOException {
+
+        log.debug("Loading rules from directory ({})", dir);
+        rules = new ArrayList<>();
+        
+        try (final DirectoryStream<Path> dirstream  = Files.newDirectoryStream(dir)) {
+            for (final Path child : dirstream) {
+                final File file =  child.toFile();
+                if (file.isDirectory()) {
+                    try {
+                        rules.addAll(new TranscodingRuleLoader(child).getRules());
+                    } catch (final IOException e) {
+                        log.error("Failed to load rules from directory ({})", file, e);
+                    }
+                } else {
+                    log.debug("Loading rule from property set in file ({})", file);
+                    try {
+                        rules.add(TranscodingRule.fromResource(new FileSystemResource(file)));
+                    } catch (final IOException e) {
+                        log.error("Failed to load rule from file ({})", file, e);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Get the rules loaded by this object.
+     * 
+     * @return collection of rules
+     */
+    @Nonnull @NonnullElements public Collection<TranscodingRule> getRules() {
+        return rules;
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/package-info.java b/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/package-info.java
similarity index 85%
copy from idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/package-info.java
copy to idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/package-info.java
index 9b82bcc..b0a62a2 100644
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/package-info.java
+++ b/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/transcoding/impl/package-info.java
@@ -16,8 +16,7 @@
  */
 
 /**
- * Classes in this package are to do with the import of attributes
- * as an adjunct to filtering.
+ * Implementation classes for attribute transcoding.
  */
 
-package net.shibboleth.idp.saml.attribute.mapping;
\ No newline at end of file
+package net.shibboleth.idp.attribute.transcoding.impl;
\ No newline at end of file
diff --git a/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryImplTest.java b/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryImplTest.java
new file mode 100644
index 0000000..6bb0629
--- /dev/null
+++ b/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/AttributeTranscoderRegistryImplTest.java
@@ -0,0 +1,338 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.transcoding.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.testng.Assert.*;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+
+import net.shibboleth.idp.attribute.AttributeDecodingException;
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.EmptyAttributeValue;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
+import net.shibboleth.utilities.java.support.collection.Pair;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+/**
+ * Test for {@link AttributeTranscoderRegistryImpl}.
+ */
+public class AttributeTranscoderRegistryImplTest {
+    
+    private AttributeTranscoderRegistryImpl registry;
+    
+    @BeforeClass public void setUp() throws ComponentInitializationException {
+        registry = new AttributeTranscoderRegistryImpl();
+        registry.setId("test");
+        
+        registry.setNamingRegistry(Collections.singletonMap(Pair.class, (Pair p) -> "{Pair}" + p.getFirst().toString()));
+        
+        final PairTranscoder transcoder = new PairTranscoder();
+        transcoder.initialize();
+        
+        final Map<String,Object> ruleset1 = new HashMap<>();
+        ruleset1.put(AttributeTranscoderRegistry.PROP_ID, "foo");
+        ruleset1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        ruleset1.put("name", "bar");
+        
+        final Map<String,Object> ruleset2 = new HashMap<>();
+        ruleset2.put(AttributeTranscoderRegistry.PROP_ID, "foo");
+        ruleset2.put(AttributeTranscoderRegistry.PROP_TRANSCODER_CLASS,
+                "net.shibboleth.idp.attribute.transcoding.impl.PairTranscoder");
+        ruleset2.put("name", "baz");
+        
+        final Map<String,Object> ruleset3 = new HashMap<>();
+        ruleset3.put(AttributeTranscoderRegistry.PROP_ID, "foo");
+        ruleset3.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        ruleset3.put(AttributeTranscoderRegistry.PROP_CONDITION, Predicates.alwaysFalse());
+        ruleset3.put("name", "ban");
+
+        final Map<String,Object> ruleset4 = new HashMap<>();
+        ruleset4.put(AttributeTranscoderRegistry.PROP_ID, "foo2");
+        ruleset4.put(AttributeTranscoderRegistry.PROP_TRANSCODER_CLASS,
+                "net.shibboleth.idp.attribute.transcoding.impl.PairTranscoder");
+        ruleset4.put("name", "baz");
+        
+        registry.setTranscoderRegistry(Arrays.asList(
+                new TranscodingRule(ruleset1),
+                new TranscodingRule(ruleset2),
+                new TranscodingRule(ruleset3),
+                new TranscodingRule(ruleset4)));
+        
+        registry.initialize();
+    }
+    
+    @AfterClass public void tearDown() {
+        registry.destroy();
+        registry = null;
+    }
+
+
+    @Test public void testEncodeNoMappings() throws AttributeEncodingException {
+        
+        assertTrue(registry.getTranscodingRules(new IdPAttribute("frobnitz"), Pair.class).isEmpty());
+        assertTrue(registry.getTranscodingRules(new IdPAttribute("frobnitz"), MyPair.class).isEmpty());
+        assertTrue(registry.getTranscodingRules(new IdPAttribute("foo"), String.class).isEmpty());
+}
+
+    @Test public void testDecodeNoMappings() throws AttributeDecodingException {
+        
+        assertTrue(registry.getTranscodingRules(new Pair("foo", "value")).isEmpty());
+        assertTrue(registry.getTranscodingRules(new MyPair("foo", "value")).isEmpty());
+        assertTrue(registry.getTranscodingRules(new String("bar")).isEmpty());
+    }
+
+    @Test public void testDecodeInactive() throws AttributeDecodingException {
+
+        final Pair p = new Pair("ban", "value");
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(p);
+        assertEquals(rulesets.size(), 1);
+        
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final AttributeTranscoder<Pair> t = TranscoderSupport.getTranscoder(ruleset);
+        assertNull(t.decode(null, p, ruleset));
+    }
+
+    @Test public void testEncodeNoValues() throws AttributeEncodingException {
+        final IdPAttribute foo = new IdPAttribute("foo");
+        
+        final List<Pair> pairs = new ArrayList<>();
+        
+        for (final TranscodingRule ruleset : registry.getTranscodingRules(foo, Pair.class)) {
+            final AttributeTranscoder<Pair> t = TranscoderSupport.getTranscoder(ruleset);
+            final Pair p = t.encode(null, foo, Pair.class, ruleset);
+            if (p != null) {
+                pairs.add(p);
+            }
+        }
+        
+        assertEquals(pairs.size(), 2);
+        
+        assertEquals(pairs.get(0).getFirst(), "bar");
+        assertNull(pairs.get(0).getSecond());
+        
+        assertEquals(pairs.get(1).getFirst(), "baz");
+        assertNull(pairs.get(1).getSecond());
+    }
+
+    @Test public void testDecodeOneNoValues() throws AttributeDecodingException {
+        
+        final Pair bar = new Pair("bar", null);
+        
+        final List<IdPAttribute> attributes = new ArrayList<>();
+        
+        for (final TranscodingRule ruleset : registry.getTranscodingRules(bar)) {
+            final AttributeTranscoder<Pair> t = TranscoderSupport.getTranscoder(ruleset);
+            attributes.add(t.decode(null, bar, ruleset));
+        }
+        
+        assertEquals(attributes.size(), 1);
+        
+        assertEquals(attributes.get(0).getId(), "foo");
+        assertTrue(attributes.get(0).getValues().isEmpty());
+    }
+
+    @Test public void testDecodeTwoNoValues() throws AttributeDecodingException {
+        
+        final Pair baz = new Pair("baz", null);
+        
+        final List<IdPAttribute> attributes = new ArrayList<>();
+        
+        for (final TranscodingRule ruleset : registry.getTranscodingRules(baz)) {
+            final AttributeTranscoder<Pair> t = TranscoderSupport.getTranscoder(ruleset);
+            attributes.add(t.decode(null, baz, ruleset));
+        }
+        
+        assertEquals(attributes.size(), 2);
+        
+        assertEquals(attributes.get(0).getId(), "foo");
+        assertTrue(attributes.get(0).getValues().isEmpty());
+
+        assertEquals(attributes.get(1).getId(), "foo2");
+        assertTrue(attributes.get(1).getValues().isEmpty());
+    }
+
+    @Test public void testEncodeStringValues() throws AttributeEncodingException {
+        final IdPAttribute foo = new IdPAttribute("foo");
+        foo.setValues(Collections.singletonList(StringAttributeValue.valueOf("value")));
+        
+        final List<Pair> pairs = new ArrayList<>();
+        
+        for (final TranscodingRule ruleset : registry.getTranscodingRules(foo, Pair.class)) {
+            final AttributeTranscoder<Pair> t = TranscoderSupport.getTranscoder(ruleset);
+            final Pair p = t.encode(null, foo, Pair.class, ruleset);
+            if (p != null) {
+                pairs.add(p);
+            }
+        }
+        
+        assertEquals(pairs.size(), 2);
+        
+        assertEquals(pairs.get(0).getFirst(), "bar");
+        assertEquals(pairs.get(0).getSecond(), "value");
+        
+        assertEquals(pairs.get(1).getFirst(), "baz");
+        assertEquals(pairs.get(1).getSecond(), "value");
+    }
+
+    @Test public void testEncodeSubtypeStringValues() throws AttributeEncodingException {
+        final IdPAttribute foo = new IdPAttribute("foo");
+        foo.setValues(Collections.singletonList(StringAttributeValue.valueOf("value")));
+        
+        final List<MyPair> pairs = new ArrayList<>();
+        
+        for (final TranscodingRule ruleset : registry.getTranscodingRules(foo, MyPair.class)) {
+            final AttributeTranscoder<MyPair> t = TranscoderSupport.getTranscoder(ruleset);
+            final MyPair p = t.encode(null, foo, MyPair.class, ruleset);
+            if (p != null) {
+                pairs.add(p);
+            }
+        }
+        
+        assertEquals(pairs.size(), 2);
+        
+        assertEquals(pairs.get(0).getFirst(), "bar");
+        assertEquals(pairs.get(0).getSecond(), "value");
+        
+        assertEquals(pairs.get(1).getFirst(), "baz");
+        assertEquals(pairs.get(1).getSecond(), "value");
+    }
+    
+    @Test public void testDecodeOneStringValues() throws AttributeDecodingException {
+        
+        final Pair bar = new Pair("bar", "value");
+        
+        final List<IdPAttribute> attributes = new ArrayList<>();
+        
+        for (final TranscodingRule ruleset : registry.getTranscodingRules(bar)) {
+            final AttributeTranscoder<Pair> t = TranscoderSupport.getTranscoder(ruleset);
+            attributes.add(t.decode(null, bar, ruleset));
+        }
+        
+        assertEquals(attributes.size(), 1);
+        
+        assertEquals(attributes.get(0).getId(), "foo");
+        assertEquals(attributes.get(0).getValues().get(0).getValue(), "value");
+    }
+    
+    @Test public void testDecodeTwoStringValues() throws AttributeDecodingException {
+        
+        final Pair baz = new Pair("baz", "value");
+        
+        final List<IdPAttribute> attributes = new ArrayList<>();
+        
+        for (final TranscodingRule ruleset : registry.getTranscodingRules(baz)) {
+            final AttributeTranscoder<Pair> t = TranscoderSupport.getTranscoder(ruleset);
+            attributes.add(t.decode(null, baz, ruleset));
+        }
+        
+        assertEquals(attributes.size(), 2);
+        
+        assertEquals(attributes.get(0).getId(), "foo");
+        assertEquals(attributes.get(0).getValues().get(0).getValue(), "value");
+
+        assertEquals(attributes.get(1).getId(), "foo2");
+        assertEquals(attributes.get(1).getValues().get(0).getValue(), "value");
+    }
+
+    @Test public void testEncodeUnsupportedValues() throws AttributeEncodingException {
+        final IdPAttribute foo = new IdPAttribute("foo");
+        foo.setValues(Collections.singletonList(EmptyAttributeValue.ZERO_LENGTH));
+        
+        final List<Pair> pairs = new ArrayList<>();
+        
+        for (final TranscodingRule ruleset : registry.getTranscodingRules(foo, Pair.class)) {
+            final AttributeTranscoder<Pair> t = TranscoderSupport.getTranscoder(ruleset);
+            final Pair p = t.encode(null, foo, Pair.class, ruleset);
+            if (p != null) {
+                pairs.add(p);
+            }
+        }
+        
+        assertEquals(pairs.size(), 2);
+        
+        assertEquals(pairs.get(0).getFirst(), "bar");
+        assertNull(pairs.get(0).getSecond());
+        
+        assertEquals(pairs.get(1).getFirst(), "baz");
+        assertNull(pairs.get(0).getSecond());
+    }
+    
+    @Test public void testDecodeOneUnsupportedValues() throws AttributeDecodingException {
+        
+        final Pair bar = new Pair("bar", 0L);
+        
+        final List<IdPAttribute> attributes = new ArrayList<>();
+        
+        for (final TranscodingRule ruleset : registry.getTranscodingRules(bar)) {
+            final AttributeTranscoder<Pair> t = TranscoderSupport.getTranscoder(ruleset);
+            attributes.add(t.decode(null, bar, ruleset));
+        }
+        
+        assertEquals(attributes.size(), 1);
+        
+        assertEquals(attributes.get(0).getId(), "foo");
+        assertTrue(attributes.get(0).getValues().isEmpty());
+    }
+    
+    @Test public void testDecodeTwoUnsupportedValues() throws AttributeDecodingException {
+        
+        final Pair baz = new Pair("baz", 0L);
+        
+        final List<IdPAttribute> attributes = new ArrayList<>();
+        
+        for (final TranscodingRule ruleset : registry.getTranscodingRules(baz)) {
+            final AttributeTranscoder<Pair> t = TranscoderSupport.getTranscoder(ruleset);
+            attributes.add(t.decode(null, baz, ruleset));
+        }
+        
+        assertEquals(attributes.size(), 2);
+        
+        assertEquals(attributes.get(0).getId(), "foo");
+        assertTrue(attributes.get(0).getValues().isEmpty());
+
+        assertEquals(attributes.get(1).getId(), "foo2");
+        assertTrue(attributes.get(1).getValues().isEmpty());
+    }
+    
+    /** Marker class to exercise subtype support. */
+    
+    public static class MyPair extends Pair {
+        public MyPair(Object one, Object two) {
+            super(one, two);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/PairTranscoder.java b/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/PairTranscoder.java
new file mode 100644
index 0000000..abe4e10
--- /dev/null
+++ b/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/transcoding/impl/PairTranscoder.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.transcoding.impl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.attribute.AttributeDecodingException;
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AbstractAttributeTranscoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.utilities.java.support.collection.Pair;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * Sample transcoder for tests. 
+ */
+public class PairTranscoder extends AbstractAttributeTranscoder<Pair> {
+
+    /** {@inheritDoc} */
+    public Class<Pair> getEncodedType() {
+        return Pair.class;
+    }
+
+    /** {@inheritDoc} */
+    public String getEncodedName(TranscodingRule rule) {
+        final String name = rule.get("name", String.class);
+        if (name != null) {
+            return "{Pair}" + name;
+        } else {
+            return null;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Pair doEncode(ProfileRequestContext profileRequestContext, IdPAttribute attribute, Class<? extends Pair> to, TranscodingRule rule)
+            throws AttributeEncodingException {
+        
+        final String name = StringSupport.trimOrNull(rule.get("name", String.class));
+        if (name == null) {
+            throw new AttributeEncodingException("No name property");
+        }
+
+        try {
+            if (attribute.getValues().isEmpty() || !canEncodeValue(attribute, attribute.getValues().get(0))) {
+                return to.getDeclaredConstructor(Object.class, Object.class).newInstance(name, null);
+            } else {
+                return to.getDeclaredConstructor(Object.class, Object.class).newInstance(name, attribute.getValues().get(0).getValue());
+            }
+        } catch (final InstantiationException | IllegalAccessException | IllegalArgumentException
+                | InvocationTargetException | NoSuchMethodException | SecurityException e) {
+            throw new AttributeEncodingException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public IdPAttribute doDecode(ProfileRequestContext profileRequestContext, Pair input, TranscodingRule rule)
+            throws AttributeDecodingException {
+       
+        final String id = StringSupport.trimOrNull(rule.get(AttributeTranscoderRegistry.PROP_ID, String.class));
+        if (id == null) {
+            throw new AttributeDecodingException("No id property");
+        }
+        
+        final IdPAttribute idattr = new IdPAttribute(id);
+        
+        if (input.getSecond() instanceof String) {
+            idattr.setValues(Collections.singletonList(StringAttributeValue.valueOf((String) input.getSecond())));
+        }
+        
+        return idattr;
+    }
+
+    /** {@inheritDoc} */
+    protected boolean canEncodeValue(IdPAttribute idpAttribute, IdPAttributeValue value) {
+        return value instanceof StringAttributeValue;
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-attribute-resolver-api/.project b/idp-attribute-resolver-api/.project
index f1c493c..de89ab8 100644
--- a/idp-attribute-resolver-api/.project
+++ b/idp-attribute-resolver-api/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinition.java b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinition.java
index 8202da9..0c2f8c5 100644
--- a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinition.java
+++ b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinition.java
@@ -19,18 +19,15 @@ package net.shibboleth.idp.attribute.resolver;
 
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Set;
 import java.util.regex.Pattern;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.ThreadSafe;
 
-import net.shibboleth.idp.attribute.AttributeEncoder;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
 import net.shibboleth.idp.attribute.resolver.context.AttributeResolverWorkContext;
@@ -38,7 +35,6 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElemen
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.annotation.constraint.NullableElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
-import net.shibboleth.utilities.java.support.collection.CollectionSupport;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.primitive.StringSupport;
@@ -46,9 +42,7 @@ import net.shibboleth.utilities.java.support.primitive.StringSupport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
 
 /** Base class for attribute definition resolver plugins. */
 @ThreadSafe
@@ -61,9 +55,6 @@ public abstract class AbstractAttributeDefinition extends AbstractResolverPlugin
     /** Whether this attribute definition is only a dependency and thus its values should never be released. */
     private boolean dependencyOnly;
 
-    /** Attribute encoders associated with this definition. */
-    @Nonnull private Set<AttributeEncoder<?>> encoders = Collections.emptySet();
-
     /** Localized human intelligible attribute name. */
     @Nonnull private Map<Locale, String> displayNames = Collections.emptyMap();
 
@@ -159,31 +150,6 @@ public abstract class AbstractAttributeDefinition extends AbstractResolverPlugin
         displayNames = ImmutableMap.copyOf(checkedNames);
     }
 
-    /**
-     * Gets the unmodifiable encoders used to encode the values of this attribute in to protocol specific formats. The
-     * returned collection is never null nor contains any null.
-     * 
-     * @return encoders used to encode the values of this attribute in to protocol specific formats, never null
-     */
-    @Override
-    @Nonnull @NonnullElements @Unmodifiable public Set<AttributeEncoder<?>> getAttributeEncoders() {
-        return encoders;
-    }
-
-    /**
-     * Sets the encoders used to encode the values of this attribute in to protocol specific formats.
-     * 
-     * @param attributeEncoders encoders used to encode the values of this attribute in to protocol specific formats
-     */
-    public void setAttributeEncoders(@Nullable @NullableElements final Set<AttributeEncoder<?>> attributeEncoders) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
-
-        final Set<AttributeEncoder<?>> checkedEncoders = new HashSet<>();
-        CollectionSupport.addIf(checkedEncoders, attributeEncoders, Predicates.notNull());
-        encoders = ImmutableSet.copyOf(checkedEncoders);
-    }
-
     /** {@inheritDoc} */
     @Override
     protected void doInitialize() throws ComponentInitializationException {
@@ -235,10 +201,6 @@ public abstract class AbstractAttributeDefinition extends AbstractResolverPlugin
                 getDisplayNames());
         resolvedAttribute.setDisplayNames(getDisplayNames());
 
-        log.trace("{} associating the following encoders with the resolved attribute: {}", getLogPrefix(),
-                getAttributeEncoders());
-        resolvedAttribute.setEncoders(getAttributeEncoders());
-
         return resolvedAttribute;
     }
 
diff --git a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AttributeDefinition.java b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AttributeDefinition.java
index 4e66a8b..27a4850 100644
--- a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AttributeDefinition.java
+++ b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AttributeDefinition.java
@@ -19,11 +19,9 @@ package net.shibboleth.idp.attribute.resolver;
 
 import java.util.Locale;
 import java.util.Map;
-import java.util.Set;
 
 import javax.annotation.concurrent.ThreadSafe;
 
-import net.shibboleth.idp.attribute.AttributeEncoder;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
@@ -55,12 +53,4 @@ public interface AttributeDefinition extends ResolverPlugin<IdPAttribute> {
      */
     @NonnullAfterInit @NonnullElements @Unmodifiable Map<Locale, String> getDisplayNames();
 
-    /**
-     * Gets the unmodifiable encoders used to encode the values of this attribute in to protocol specific formats. The
-     * returned collection is never null nor contains any null.
-     * 
-     * @return encoders used to encode the values of this attribute in to protocol specific formats, never null
-     */
-    @NonnullAfterInit @NonnullElements @Unmodifiable Set<AttributeEncoder<?>> getAttributeEncoders();
-
 }
\ No newline at end of file
diff --git a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AttributeResolver.java b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AttributeResolver.java
index 1c7c8b3..ead6668 100644
--- a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AttributeResolver.java
+++ b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AttributeResolver.java
@@ -17,14 +17,10 @@
 
 package net.shibboleth.idp.attribute.resolver;
 
-import java.util.Collection;
-
 import javax.annotation.Nonnull;
 import javax.annotation.concurrent.ThreadSafe;
 
-import net.shibboleth.idp.attribute.AttributeEncoder;
 import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
-import net.shibboleth.utilities.java.support.collection.Pair;
 import net.shibboleth.utilities.java.support.component.IdentifiedComponent;
 
 /**
@@ -40,14 +36,6 @@ import net.shibboleth.utilities.java.support.component.IdentifiedComponent;
 public interface AttributeResolver extends IdentifiedComponent {
 
     /**
-     * Gets all the {@link AttributeEncoder}s for this resolver. Each encoder is linked
-     * (via being in an {@link Pair} to the name of the attribute it is associated with.
-     * 
-     * @return The Attribute Encoders loaded in to this resolver
-     */
-    @Nonnull Collection<Pair<String, AttributeEncoder<?>>> getAllEncoders();
-
-    /**
      * Resolves the attribute for the give request. Note, if attributes are requested,
      * {@link AttributeResolutionContext#getRequestedIdPAttributeNames()}, the resolver will <strong>not</strong> fail
      * if they can not be resolved. This information serves only as a hint to the resolver to, potentially, optimize the
diff --git a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/ResolvedAttributeDefinition.java b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/ResolvedAttributeDefinition.java
index 43e35d6..a382b7f 100644
--- a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/ResolvedAttributeDefinition.java
+++ b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/ResolvedAttributeDefinition.java
@@ -26,7 +26,6 @@ import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.ThreadSafe;
 
-import net.shibboleth.idp.attribute.AttributeEncoder;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
 import net.shibboleth.idp.attribute.resolver.context.AttributeResolverWorkContext;
@@ -78,11 +77,6 @@ public final class ResolvedAttributeDefinition extends AbstractAttributeDefiniti
     }
 
     /** {@inheritDoc} */
-    @Override @Nonnull @NonnullElements public Set<AttributeEncoder<?>> getAttributeEncoders() {
-        return resolvedDefinition.getAttributeEncoders();
-    }
-
-    /** {@inheritDoc} */
     @Override @Nonnull @NonnullElements public Set<ResolverAttributeDefinitionDependency> getAttributeDependencies() {
         return resolvedDefinition.getAttributeDependencies();
     }
diff --git a/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinitionTest.java b/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinitionTest.java
index 67fe772..3ddab43 100644
--- a/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinitionTest.java
+++ b/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/AbstractAttributeDefinitionTest.java
@@ -19,7 +19,6 @@ package net.shibboleth.idp.attribute.resolver;
 
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
@@ -27,7 +26,6 @@ import java.util.Set;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import net.shibboleth.idp.attribute.AttributeEncoder;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
 import net.shibboleth.idp.attribute.resolver.context.AttributeResolverWorkContext;
@@ -49,8 +47,6 @@ public class AbstractAttributeDefinitionTest {
 
         Assert.assertEquals(definition.getId(), "foo");
         Assert.assertFalse(definition.isDependencyOnly());
-        Assert.assertNotNull(definition.getAttributeEncoders());
-        Assert.assertTrue(definition.getAttributeEncoders().isEmpty());
         Assert.assertNotNull(definition.getDisplayDescriptions());
         Assert.assertTrue(definition.getDisplayDescriptions().isEmpty());
         Assert.assertNotNull(definition.getDisplayNames());
@@ -76,50 +72,6 @@ public class AbstractAttributeDefinitionTest {
         Assert.assertFalse(definition.isDependencyOnly());
     }
 
-    /** Tests setting and retrieving encoders. */
-    @Test
-    public void encoders() {
-        MockAttributeDefinition definition = new MockAttributeDefinition("foo", null);
-
-        MockAttributeEncoder enc1 = new MockAttributeEncoder(null, null);
-        MockAttributeEncoder enc2 = new MockAttributeEncoder(null, null);
-
-        Set<AttributeEncoder<?>> encoders = new HashSet<>(2);
-
-        definition.setAttributeEncoders(null);
-        Assert.assertNotNull(definition.getAttributeEncoders());
-        Assert.assertTrue(definition.getAttributeEncoders().isEmpty());
-
-        definition.setAttributeEncoders(encoders);
-        Assert.assertNotNull(definition.getAttributeEncoders());
-        Assert.assertTrue(definition.getAttributeEncoders().isEmpty());
-
-        encoders.add(enc1);
-        encoders.add(null);
-        encoders.add(enc2);
-        Assert.assertNotNull(definition.getAttributeEncoders());
-        Assert.assertTrue(definition.getAttributeEncoders().isEmpty());
-
-        definition.setAttributeEncoders(encoders);
-        Assert.assertEquals(definition.getAttributeEncoders().size(), 2);
-        Assert.assertTrue(definition.getAttributeEncoders().contains(enc1));
-        Assert.assertTrue(definition.getAttributeEncoders().contains(enc2));
-
-        encoders.clear();
-        encoders.add(enc2);
-        definition.setAttributeEncoders(encoders);
-        Assert.assertEquals(definition.getAttributeEncoders().size(), 1);
-        Assert.assertFalse(definition.getAttributeEncoders().contains(enc1));
-        Assert.assertTrue(definition.getAttributeEncoders().contains(enc2));
-
-        try {
-            definition.getAttributeEncoders().add(enc2);
-            Assert.fail("able to add entry to supposedly unmodifiable collection");
-        } catch (UnsupportedOperationException e) {
-            // expected this
-        }
-    }
-
     /** Tests that display descriptions are properly added and modified. */
     @Test
     public void displayDescriptions() {
@@ -228,30 +180,6 @@ public class AbstractAttributeDefinitionTest {
         testInvalidName(new MockAttributeDefinition("Name\rWith\tnonprinters", (IdPAttribute) null));
     }
     
-    @Test public void initDestroyValidate() throws ComponentInitializationException {
-        MockAttributeEncoder encoder = new MockAttributeEncoder("foo", "baz");
-        MockAttributeDefinition definition = new MockAttributeDefinition("foo", (IdPAttribute) null);
-        
-        Set<AttributeEncoder<?>> encoders = new HashSet<>(1);
-        encoders.add(encoder);
-        definition.setAttributeEncoders(encoders);
-        
-        Assert.assertFalse(encoder.isInitialized());
-        Assert.assertFalse(encoder.isDestroyed());
-
-        encoder.initialize();
-        definition.initialize();
-        Assert.assertTrue(encoder.isInitialized());
-        Assert.assertFalse(encoder.isDestroyed());
-
-        definition.destroy();
-        encoder.destroy();
-        Assert.assertTrue(encoder.isInitialized());
-        Assert.assertTrue(encoder.isDestroyed());
-        
-        
-    }
-
     /**
      * This class implements the minimal level of functionality and is meant only as a means of testing the abstract
      * {@link AttributeDefinition}.
diff --git a/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/MockAttributeEncoder.java b/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/MockAttributeEncoder.java
deleted file mode 100644
index 8eb77b2..0000000
--- a/idp-attribute-resolver-api/src/test/java/net/shibboleth/idp/attribute/resolver/MockAttributeEncoder.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.resolver;
-
-import java.util.function.Predicate;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-
-import com.google.common.base.Predicates;
-
-import net.shibboleth.idp.attribute.AttributeEncoder;
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
-
-/** Mock implementation of {@link AttributeEncoder}. */
-public class MockAttributeEncoder extends AbstractInitializableComponent implements AttributeEncoder<String> {
-
-    /** Static protocol value for this encoder. */
-    private final String staticProtocol;
-
-    /** Static value for this encoder. */
-    private final String staticValue;
-
-    /**
-     * Constructor.
-     * 
-     * @param protocol protocol value returned by {@link #getProtocol()}
-     * @param value value returned by {@link #encode(IdPAttribute)}
-     */
-    public MockAttributeEncoder(String protocol, String value) {
-        staticProtocol = protocol;
-        staticValue = value;
-    }
-
-    /** {@inheritDoc} */
-    public String getProtocol() {
-        return staticProtocol;
-    }
-
-    /** {@inheritDoc} */
-    public String encode(IdPAttribute attribute) throws AttributeEncodingException {
-        return staticValue;
-    }
-
-    /** {@inheritDoc} */
-    public Predicate<ProfileRequestContext> getActivationCondition() {
-        return Predicates.alwaysTrue();
-    }
-
-}
\ No newline at end of file
diff --git a/idp-attribute-resolver-impl/.project b/idp-attribute-resolver-impl/.project
index dcea4b2..fe73535 100644
--- a/idp-attribute-resolver-impl/.project
+++ b/idp-attribute-resolver-impl/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImpl.java b/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImpl.java
index d2168fe..fd41cd1 100644
--- a/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImpl.java
+++ b/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImpl.java
@@ -41,7 +41,6 @@ import org.slf4j.LoggerFactory;
 import com.google.common.collect.ImmutableMap;
 
 import net.shibboleth.ext.spring.service.AbstractServiceableComponent;
-import net.shibboleth.idp.attribute.AttributeEncoder;
 import net.shibboleth.idp.attribute.EmptyAttributeValue;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.IdPAttributeValue;
@@ -61,7 +60,6 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NullableEleme
 import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
 import net.shibboleth.utilities.java.support.collection.LazyList;
 import net.shibboleth.utilities.java.support.collection.LazySet;
-import net.shibboleth.utilities.java.support.collection.Pair;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
@@ -632,15 +630,4 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
         }
     }
 
-    /** {@inheritDoc} */
-    public Collection<Pair<String, AttributeEncoder<?>>> getAllEncoders() {
-        final Collection<Pair<String, AttributeEncoder<?>>> result = new HashSet<>();
-
-        for (final AttributeDefinition attributeDef : getAttributeDefinitions().values()) {
-            for (final AttributeEncoder encoder : attributeDef.getAttributeEncoders()) {
-                result.add(new Pair<>(attributeDef.getId(), encoder));
-            }
-        }
-        return result;
-    }
 }
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/.project b/idp-attribute-resolver-spring/.project
index e323203..d7c71e4 100644
--- a/idp-attribute-resolver-spring/.project
+++ b/idp-attribute-resolver-spring/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/ad/BaseAttributeDefinitionParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/ad/BaseAttributeDefinitionParser.java
index 5b83ed8..0e85bc5 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/ad/BaseAttributeDefinitionParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/ad/BaseAttributeDefinitionParser.java
@@ -31,7 +31,6 @@ import org.springframework.beans.factory.support.ManagedMap;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.w3c.dom.Element;
 
-import net.shibboleth.ext.spring.util.SpringSupport;
 import net.shibboleth.idp.attribute.resolver.spring.BaseResolverPluginParser;
 import net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverNamespaceHandler;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
@@ -87,16 +86,6 @@ public abstract class BaseAttributeDefinitionParser extends BaseResolverPluginPa
             log.debug("{} Setting dependencyOnly {}", getLogPrefix(), dependencyOnly);
             builder.addPropertyValue("dependencyOnly", dependencyOnly);
         }
-
-        final List<Element> attributeEncoders =
-                ElementSupport.getChildElements(config, new QName(AttributeResolverNamespaceHandler.NAMESPACE,
-                        "AttributeEncoder"));
-
-        if (attributeEncoders != null && !attributeEncoders.isEmpty()) {
-            log.debug("{} Adding {} encoders", getLogPrefix(), attributeEncoders.size());
-            builder.addPropertyValue("attributeEncoders",
-                    SpringSupport.parseCustomElements(attributeEncoders, parserContext));
-        }
     }
     // CheckStyle: CyclomaticComplexity ON
 
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseAttributeEncoderParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseAttributeEncoderParser.java
index 0250547..880adfe 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseAttributeEncoderParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseAttributeEncoderParser.java
@@ -17,11 +17,13 @@
 
 package net.shibboleth.idp.attribute.resolver.spring.enc;
 
+import java.util.Map;
+
 import javax.annotation.Nonnull;
 
-import net.shibboleth.ext.spring.util.SpringSupport;
 import net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverNamespaceHandler;
-import net.shibboleth.idp.profile.logic.RelyingPartyIdPredicate;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
 import net.shibboleth.idp.profile.logic.ScriptedPredicate;
 import net.shibboleth.idp.profile.spring.relyingparty.metadata.ScriptTypeBeanParser;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
@@ -30,8 +32,10 @@ import net.shibboleth.utilities.java.support.xml.ElementSupport;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.config.BeanReference;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.ManagedMap;
 import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.w3c.dom.Element;
@@ -47,77 +51,77 @@ public abstract class BaseAttributeEncoderParser extends AbstractSingleBeanDefin
     /** Log4j logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(BaseAttributeEncoderParser.class);
 
-    /** Whether the name property is required or not. */
-    private boolean nameRequired;
-
-    /** Constructor. */
-    public BaseAttributeEncoderParser() {
-        nameRequired = false;
+    /** {@inheritDoc} */
+    @Override
+    protected Class<TranscodingRule> getBeanClass(final Element element) {
+        return TranscodingRule.class;
     }
-
-    /**
-     * Set whether the name property is required or not.
-     * 
-     * @param flag flag to set
-     */
-    public void setNameRequired(final boolean flag) {
-        nameRequired = flag;
+    
+    /** {@inheritDoc} */
+    @Override
+    protected boolean shouldGenerateId() {
+        return true;
     }
 
     /** {@inheritDoc} */
-    @Override protected void doParse(final Element config, final ParserContext parserContext,
-            final BeanDefinitionBuilder builder) {
+    @Override
+    protected boolean shouldParseNameAsAliases() {
+        return false;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doParse(final Element config, final ParserContext context, final BeanDefinitionBuilder builder) {
+
+        final ManagedMap rule = new ManagedMap();
 
-        super.doParse(config, parserContext, builder);
+        builder.addConstructorArgValue(rule);
         
-        final String attributeName = StringSupport.trimOrNull(config.getAttributeNS(null, NAME_ATTRIBUTE_NAME));
-        if (nameRequired && attributeName == null) {
-            throw new BeanCreationException("Attribute encoder must contain a name property");
+        if (config.getParentNode() instanceof Element && ((Element)config.getParentNode()).hasAttributeNS(null, "id")) {
+            rule.put(AttributeTranscoderRegistry.PROP_ID,
+                    StringSupport.trimOrNull(((Element) config.getParentNode()).getAttributeNS(null, "id")));
+        } else {
+            log.warn("Parsing AttributeEncoder with no parent element, resulting rule will be ignored");
         }
 
         if (config.hasAttributeNS(null, "activationConditionRef")) {
             if (config.hasAttributeNS(null, "relyingParties")) {
                 log.warn("relyingParties ignored, using activationConditionRef");
             }
-            builder.addPropertyReference("activationCondition",
-                    StringSupport.trimOrNull(config.getAttributeNS(null, "activationConditionRef")));
+            rule.put(AttributeTranscoderRegistry.PROP_CONDITION, new RuntimeBeanReference(
+                    StringSupport.trimOrNull(config.getAttributeNS(null, "activationConditionRef"))));
         } else if (config.hasAttributeNS(null, "relyingParties")) {
-            final BeanDefinitionBuilder rpBuilder =
-                    BeanDefinitionBuilder.genericBeanDefinition(RelyingPartyIdPredicate.class);
-            rpBuilder.setFactoryMethod("fromCandidates");
-            rpBuilder .addConstructorArgValue(
-                    SpringSupport.getAttributeValueAsList(config.getAttributeNodeNS(null, "relyingParties")));
-            builder.addPropertyValue("activationCondition", rpBuilder.getBeanDefinition());
+            rule.put(AttributeTranscoderRegistry.PROP_RELYINGPARTIES, config.getAttributeNS(null, "relyingParties"));
         } else {
             final Element child = ElementSupport.getFirstChildElement(config);
             if (child != null && ElementSupport.isElementNamed(child,
                     AttributeResolverNamespaceHandler.NAMESPACE, "ActivationConditionScript")) {
-                builder.addPropertyValue("activationCondition",
+                rule.put(AttributeTranscoderRegistry.PROP_CONDITION,
                         ScriptTypeBeanParser.parseScriptType(ScriptedPredicate.class, child).getBeanDefinition());
             }
         }
 
-        if (config.hasAttributeNS(null, "encodeType")) {
-            builder.addPropertyValue("encodeType", StringSupport.trimOrNull(config.getAttributeNS(null, "encodeType")));
-        }
-
-        builder.setInitMethodName("initialize");
-        builder.setDestroyMethodName("destroy");
-        builder.addPropertyValue("name", attributeName);
-
+        rule.put(AttributeTranscoderRegistry.PROP_TRANSCODER, buildTranscoder());
+        
+        doParse(config, context, rule);
     }
+    
+    /**
+     * Inject any necessary elements into the mapping rule based on the specific encoder type.
+     * 
+     * @param config the encoder element being parsed
+     * @param parserContext the parser context
+     * @param rule the mapping rule
+     */
+    protected abstract void doParse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
+            @Nonnull final Map<String,Object> rule);
 
-    /** {@inheritDoc} */
-    @Override public boolean shouldGenerateId() {
-        return true;
-    }
 
     /**
-     * {@inheritDoc}. <br/>
-     * We parse the attribute "name" and we do not want Spring to. see #IDP-571.
+     * Return a bean definition for the transcoder to include in the mapping.
+     * 
+     * @return bean definition for an AttributeTranscoder
      */
-    @Override protected boolean shouldParseNameAsAliases() {
-        return false;
-    }
-
+    @Nonnull protected abstract BeanReference buildTranscoder();
+    
 }
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseSAML1AttributeEncoderParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseSAML1AttributeEncoderParser.java
new file mode 100644
index 0000000..95af88e
--- /dev/null
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseSAML1AttributeEncoderParser.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.resolver.spring.enc;
+
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.w3c.dom.Element;
+
+import net.shibboleth.ext.spring.util.SpringSupport;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * Base class for Spring bean definition parser for SAML 1 attribute encoders.
+ */
+public abstract class BaseSAML1AttributeEncoderParser extends BaseAttributeEncoderParser {
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doParse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
+            @Nonnull final Map<String,Object> rule) {
+        
+        if (config.hasAttributeNS(null, "name")) {
+            rule.put(SAMLAttributeTranscoder.PROP_NAME,
+                    StringSupport.trimOrNull(config.getAttributeNS(null, "name")));
+        } else {
+            throw new BeanDefinitionStoreException("Missing 'name' attribute, cannot create transcoder rule");
+        }
+
+        if (config.hasAttributeNS(null, "namespace")) {
+            rule.put(SAML1AttributeTranscoder.PROP_NAMESPACE,
+                    StringSupport.trimOrNull(config.getAttributeNS(null, "namespace")));
+        }
+
+        final String value = StringSupport.trimOrNull(config.getAttributeNS(null, "encodeType"));
+        if (value != null) {
+            rule.put(SAMLAttributeTranscoder.PROP_ENCODE_TYPE,SpringSupport.getStringValueAsBoolean(value));
+        }
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseSAML2AttributeEncoderParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseSAML2AttributeEncoderParser.java
new file mode 100644
index 0000000..2410a8a
--- /dev/null
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseSAML2AttributeEncoderParser.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.resolver.spring.enc;
+
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.w3c.dom.Element;
+
+import net.shibboleth.ext.spring.util.SpringSupport;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * Base class for Spring bean definition parser for SAML 1 attribute encoders.
+ */
+public abstract class BaseSAML2AttributeEncoderParser extends BaseAttributeEncoderParser {
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doParse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
+            @Nonnull final Map<String,Object> rule) {
+
+        if (config.hasAttributeNS(null, "name")) {
+            rule.put(SAMLAttributeTranscoder.PROP_NAME,
+                    StringSupport.trimOrNull(config.getAttributeNS(null, "name")));
+        } else {
+            throw new BeanDefinitionStoreException("Missing 'name' attribute, cannot create transcoder rule");
+        }
+
+        if (config.hasAttributeNS(null, "nameFormat")) {
+            rule.put(SAML2AttributeTranscoder.PROP_NAME_FORMAT,
+                    StringSupport.trimOrNull(config.getAttributeNS(null, "nameFormat")));
+        }
+        
+        if (config.hasAttributeNS(null, "friendlyName")) {
+            rule.put(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME,
+                    StringSupport.trimOrNull(config.getAttributeNS(null, "friendlyName")));
+        }
+        
+        final String value = StringSupport.trimOrNull(config.getAttributeNS(null, "encodeType"));
+        if (value != null) {
+            rule.put(SAMLAttributeTranscoder.PROP_ENCODE_TYPE,SpringSupport.getStringValueAsBoolean(value));
+        }
+    }
+}
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseScopedAttributeEncoderParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseScopedAttributeEncoderParser.java
deleted file mode 100644
index 18aea46..0000000
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/BaseScopedAttributeEncoderParser.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.resolver.spring.enc;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
-
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.xml.ParserContext;
-import org.w3c.dom.Element;
-
-/**
- * Base class for Spring bean definition parser for Shibboleth scoped attribute encoders.
- */
-public abstract class BaseScopedAttributeEncoderParser extends BaseAttributeEncoderParser {
-
-    /** Local name of scope type attribute. */
-    @Nonnull @NotEmpty public static final String SCOPE_TYPE_ATTRIBUTE_NAME = "scopeType";
-
-    /** Local name of scope delimiter attribute. */
-    @Nonnull @NotEmpty public static final String SCOPE_DELIMITER_ATTRIBUTE_NAME = "scopeDelimiter";
-
-    /** Local name of scope attribute attribute. */
-    @Nonnull @NotEmpty public static final String SCOPE_ATTRIBUTE_ATTRIBUTE_NAME = "scopeAttribute";
-
-    /** {@inheritDoc} */
-    @Override protected void doParse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
-            @Nonnull final BeanDefinitionBuilder builder) {
-        super.doParse(config, parserContext, builder);
-
-        if (config.hasAttributeNS(null, SCOPE_DELIMITER_ATTRIBUTE_NAME)) {
-            builder.addPropertyValue("scopeDelimiter",
-                    StringSupport.trimOrNull(config.getAttributeNS(null, SCOPE_DELIMITER_ATTRIBUTE_NAME)));
-        }
-
-        if (config.hasAttributeNS(null, SCOPE_ATTRIBUTE_ATTRIBUTE_NAME)) {
-            builder.addPropertyValue("scopeAttributeName",
-                    StringSupport.trimOrNull(config.getAttributeNS(null, SCOPE_ATTRIBUTE_ATTRIBUTE_NAME)));
-        }
-    }
-
-}
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1Base64AttributeEncoderParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1Base64AttributeEncoderParser.java
index fd06ee2..6cfe0b1 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1Base64AttributeEncoderParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1Base64AttributeEncoderParser.java
@@ -18,49 +18,28 @@
 package net.shibboleth.idp.attribute.resolver.spring.enc.impl;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import javax.xml.namespace.QName;
 
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.xml.ParserContext;
-import org.w3c.dom.Element;
+import org.springframework.beans.factory.config.BeanReference;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
 
-import net.shibboleth.idp.attribute.resolver.spring.enc.BaseAttributeEncoderParser;
+import net.shibboleth.idp.attribute.resolver.spring.enc.BaseSAML1AttributeEncoderParser;
 import net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverNamespaceHandler;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML1ByteAttributeEncoder;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1ByteAttributeTranscoder;
 
 /**
- * Spring Bean Definition Parser for {@link SAML1ByteAttributeEncoder}.
+ * Spring Bean Definition Parser for {@link SAML1ByteAttributeTranscoder}.
  */
-public class SAML1Base64AttributeEncoderParser extends BaseAttributeEncoderParser {
+public class SAML1Base64AttributeEncoderParser extends BaseSAML1AttributeEncoderParser {
 
     /** Schema type name. */
     @Nonnull public static final QName TYPE_NAME_RESOLVER = new QName(AttributeResolverNamespaceHandler.NAMESPACE, 
             "SAML1Base64");
 
-    /** Local name of namespace attribute. */
-    @Nonnull @NotEmpty public static final String NAMESPACE_ATTRIBUTE_NAME = "namespace";
-
-    /** Constructor. */
-    public SAML1Base64AttributeEncoderParser() {
-        setNameRequired(true);
-    }
-
     /** {@inheritDoc} */
-    @Override protected Class<SAML1ByteAttributeEncoder> getBeanClass(@Nullable final Element element) {
-        return SAML1ByteAttributeEncoder.class;
+    @Override
+    protected BeanReference buildTranscoder() {
+        return new RuntimeBeanReference("SAML1ByteTranscoder");
     }
 
-    /** {@inheritDoc} */
-    @Override protected void doParse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
-            @Nonnull final BeanDefinitionBuilder builder) {
-        super.doParse(config, parserContext, builder);
-
-        if (config.hasAttributeNS(null, NAMESPACE_ATTRIBUTE_NAME)) {
-            final String namespace = StringSupport.trimOrNull(config.getAttributeNS(null, NAMESPACE_ATTRIBUTE_NAME));
-            builder.addPropertyValue("namespace", namespace);
-        }
-    }
 }
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1ScopedStringAttributeEncoderParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1ScopedStringAttributeEncoderParser.java
index 3b39b37..5446ddf 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1ScopedStringAttributeEncoderParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1ScopedStringAttributeEncoderParser.java
@@ -17,55 +17,55 @@
 
 package net.shibboleth.idp.attribute.resolver.spring.enc.impl;
 
+import java.util.Map;
+
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import javax.xml.namespace.QName;
 
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.config.BeanReference;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.w3c.dom.Element;
 
-import net.shibboleth.idp.attribute.resolver.spring.enc.BaseScopedAttributeEncoderParser;
+import net.shibboleth.idp.attribute.resolver.spring.enc.BaseSAML1AttributeEncoderParser;
 import net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverNamespaceHandler;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML1ScopedStringAttributeEncoder;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1ScopedStringAttributeTranscoder;
 import net.shibboleth.utilities.java.support.primitive.StringSupport;
 
 /**
- * Spring Bean Definition Parser for {@link SAML1ScopedStringAttributeEncoder}.
+ * Spring Bean Definition Parser for {@link SAML1ScopedStringAttributeTranscoder}.
  */
-public class SAML1ScopedStringAttributeEncoderParser extends BaseScopedAttributeEncoderParser {
+public class SAML1ScopedStringAttributeEncoderParser extends BaseSAML1AttributeEncoderParser {
 
     /** Schema type name. */
     @Nonnull public static final QName TYPE_NAME_RESOLVER = new QName(AttributeResolverNamespaceHandler.NAMESPACE,
             "SAML1ScopedString");
 
-    /** Local name of namespace attribute. */
-    @Nonnull @NotEmpty public static final String NAMESPACE_ATTRIBUTE_NAME = "namespace";
-
-    /** Constructor. */
-    public SAML1ScopedStringAttributeEncoderParser() {
-        setNameRequired(true);
-    }
-
-    /** {@inheritDoc} */
-    @Override protected Class<SAML1ScopedStringAttributeEncoder> getBeanClass(@Nullable final Element element) {
-        return SAML1ScopedStringAttributeEncoder.class;
-    }
-
     /** {@inheritDoc} */
     @Override protected void doParse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
-            @Nonnull final BeanDefinitionBuilder builder) {
-        super.doParse(config, parserContext, builder);
+            @Nonnull final Map<String,Object> rule) {
+        super.doParse(config, parserContext, rule);
 
-        if (config.hasAttributeNS(null, SCOPE_TYPE_ATTRIBUTE_NAME)) {
-            builder.addPropertyValue("scopeType",
-                    StringSupport.trimOrNull(config.getAttributeNS(null, SCOPE_TYPE_ATTRIBUTE_NAME)));
+        if (config.hasAttributeNS(null, "scopeType")) {
+            rule.put(SAML1ScopedStringAttributeTranscoder.PROP_SCOPE_TYPE,
+                    StringSupport.trimOrNull(config.getAttributeNS(null, "scopeType")));
+        }
+        
+        if (config.hasAttributeNS(null, "scopeDelimiter")) {
+            rule.put(SAML1ScopedStringAttributeTranscoder.PROP_SCOPE_DELIMITER,
+                    StringSupport.trimOrNull(config.getAttributeNS(null, "scopeDelimiter")));
         }
 
-        if (config.hasAttributeNS(null, NAMESPACE_ATTRIBUTE_NAME)) {
-            final String namespace = StringSupport.trimOrNull(config.getAttributeNS(null, NAMESPACE_ATTRIBUTE_NAME));
-            builder.addPropertyValue("namespace", namespace);
+        if (config.hasAttributeNS(null, "scopeAttribute")) {
+            rule.put(SAML1ScopedStringAttributeTranscoder.PROP_SCOPE_ATTR_NAME,
+                    StringSupport.trimOrNull(config.getAttributeNS(null, "scopeAttribute")));
         }
     }
+
+    /** {@inheritDoc} */
+    @Override
+    protected BeanReference buildTranscoder() {
+        return new RuntimeBeanReference("SAML1ScopedStringTranscoder");
+    }
+
 }
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1StringAttributeEncoderParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1StringAttributeEncoderParser.java
index a7a4768..e71f747 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1StringAttributeEncoderParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1StringAttributeEncoderParser.java
@@ -18,55 +18,28 @@
 package net.shibboleth.idp.attribute.resolver.spring.enc.impl;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import javax.xml.namespace.QName;
 
-import org.springframework.beans.factory.BeanCreationException;
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.xml.ParserContext;
-import org.w3c.dom.Element;
+import org.springframework.beans.factory.config.BeanReference;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
 
-import net.shibboleth.idp.attribute.resolver.spring.enc.BaseAttributeEncoderParser;
+import net.shibboleth.idp.attribute.resolver.spring.enc.BaseSAML1AttributeEncoderParser;
 import net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverNamespaceHandler;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML1StringAttributeEncoder;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1StringAttributeTranscoder;
 
 /**
- * Spring Bean Definition Parser for {@link SAML1StringAttributeEncoder}.
+ * Spring Bean Definition Parser for {@link SAML1StringAttributeTranscoder}.
  */
-public class SAML1StringAttributeEncoderParser extends BaseAttributeEncoderParser {
+public class SAML1StringAttributeEncoderParser extends BaseSAML1AttributeEncoderParser {
 
     /** Schema type name.. */
     @Nonnull public static final QName TYPE_NAME_RESOLVER = new QName(AttributeResolverNamespaceHandler.NAMESPACE, 
             "SAML1String");
 
-    /** Local name of namespace attribute. */
-    @Nonnull @NotEmpty public static final String NAMESPACE_ATTRIBUTE_NAME = "namespace";
-
-    /** Constructor. */
-    public SAML1StringAttributeEncoderParser() {
-        setNameRequired(true);
-    }
-
     /** {@inheritDoc} */
-    @Override protected Class<SAML1StringAttributeEncoder> getBeanClass(@Nullable final Element element) {
-        return SAML1StringAttributeEncoder.class;
+    @Override
+    protected BeanReference buildTranscoder() {
+        return new RuntimeBeanReference("SAML1StringTranscoder");
     }
 
-    /** {@inheritDoc} */
-    @Override protected void doParse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
-            @Nonnull final BeanDefinitionBuilder builder) {
-        super.doParse(config, parserContext, builder);
-
-        if (config.hasAttributeNS(null, NAMESPACE_ATTRIBUTE_NAME)) {
-            final String namespace = StringSupport.trimOrNull(config.getAttributeNS(null, NAMESPACE_ATTRIBUTE_NAME));
-            builder.addPropertyValue("namespace", namespace);
-        }
-
-        final String attributeName = StringSupport.trimOrNull(config.getAttributeNS(null, "name"));
-        if (attributeName == null) {
-            throw new BeanCreationException("SAML 1 attribute encoders must contain a name");
-        }
-    }
 }
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1XMLObjectAttributeEncoderParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1XMLObjectAttributeEncoderParser.java
index 21ebb73..97a0309 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1XMLObjectAttributeEncoderParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML1XMLObjectAttributeEncoderParser.java
@@ -18,47 +18,26 @@
 package net.shibboleth.idp.attribute.resolver.spring.enc.impl;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import javax.xml.namespace.QName;
 
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.xml.ParserContext;
-import org.w3c.dom.Element;
+import org.springframework.beans.factory.config.BeanReference;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
 
-import net.shibboleth.idp.attribute.resolver.spring.enc.BaseAttributeEncoderParser;
+import net.shibboleth.idp.attribute.resolver.spring.enc.BaseSAML1AttributeEncoderParser;
 import net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverNamespaceHandler;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML1XMLObjectAttributeEncoder;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1XMLObjectAttributeTranscoder;
 
-/** Spring Bean Definition Parser for {@link SAML1XMLObjectAttributeEncoder}. */
-public class SAML1XMLObjectAttributeEncoderParser extends BaseAttributeEncoderParser {
+/** Spring Bean Definition Parser for {@link SAML1XMLObjectAttributeTranscoder}. */
+public class SAML1XMLObjectAttributeEncoderParser extends BaseSAML1AttributeEncoderParser {
 
     /** Schema type name.. */
     @Nonnull public static final QName TYPE_NAME_RESOLVER =
             new QName(AttributeResolverNamespaceHandler.NAMESPACE, "SAML1XMLObject");
 
-    /** Local name of namespace attribute. */
-    @Nonnull @NotEmpty public static final String NAMESPACE_ATTRIBUTE_NAME = "namespace";
-
-    /** Constructor. */
-    public SAML1XMLObjectAttributeEncoderParser() {
-        setNameRequired(true);
-    }
-
     /** {@inheritDoc} */
-    @Override protected Class<SAML1XMLObjectAttributeEncoder> getBeanClass(@Nullable final Element element) {
-        return SAML1XMLObjectAttributeEncoder.class;
+    @Override
+    protected BeanReference buildTranscoder() {
+        return new RuntimeBeanReference("SAML1XMLObjectTranscoder");
     }
 
-    /** {@inheritDoc} */
-    @Override protected void doParse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
-            @Nonnull final BeanDefinitionBuilder builder) {
-        super.doParse(config, parserContext, builder);
-
-        if (config.hasAttributeNS(null, NAMESPACE_ATTRIBUTE_NAME)) {
-            final String namespace = StringSupport.trimOrNull(config.getAttributeNS(null, NAMESPACE_ATTRIBUTE_NAME));
-            builder.addPropertyValue("namespace", namespace);
-        }
-    }
 }
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2Base64AttributeEncoderParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2Base64AttributeEncoderParser.java
index b8ac4fc..8b78057 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2Base64AttributeEncoderParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2Base64AttributeEncoderParser.java
@@ -18,54 +18,28 @@
 package net.shibboleth.idp.attribute.resolver.spring.enc.impl;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import javax.xml.namespace.QName;
 
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.xml.ParserContext;
-import org.w3c.dom.Element;
+import org.springframework.beans.factory.config.BeanReference;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
 
-import net.shibboleth.idp.attribute.resolver.spring.enc.BaseAttributeEncoderParser;
+import net.shibboleth.idp.attribute.resolver.spring.enc.BaseSAML2AttributeEncoderParser;
 import net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverNamespaceHandler;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML2ByteAttributeEncoder;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2ByteAttributeTranscoder;
 
 /**
- * Spring Bean Definition Parser for {@link SAML2ByteAttributeEncoder}.
+ * Spring Bean Definition Parser for {@link SAML2ByteAttributeTranscoder}.
  */
-public class SAML2Base64AttributeEncoderParser extends BaseAttributeEncoderParser {
+public class SAML2Base64AttributeEncoderParser extends BaseSAML2AttributeEncoderParser {
 
     /** Schema type name.. */
     @Nonnull public static final QName TYPE_NAME_RESOLVER = new QName(AttributeResolverNamespaceHandler.NAMESPACE, 
             "SAML2Base64");
 
-    /** Local name of name format attribute. */
-    @Nonnull @NotEmpty public static final String NAME_FORMAT_ATTRIBUTE_NAME = "nameFormat";
-
-    /** Local name of friendly name attribute. */
-    @Nonnull @NotEmpty public static final String FRIENDLY_NAME_ATTRIBUTE_NAME = "friendlyName";
-
-    /** Constructor. */
-    public SAML2Base64AttributeEncoderParser() {
-        setNameRequired(true);
-    }
-
     /** {@inheritDoc} */
-    @Override protected Class<SAML2ByteAttributeEncoder> getBeanClass(@Nullable final Element element) {
-        return SAML2ByteAttributeEncoder.class;
+    @Override
+    protected BeanReference buildTranscoder() {
+        return new RuntimeBeanReference("SAML2ByteTranscoder");
     }
 
-    /** {@inheritDoc} */
-    @Override protected void doParse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
-            @Nonnull final BeanDefinitionBuilder builder) {
-        super.doParse(config, parserContext, builder);
-
-        if (config.hasAttributeNS(null, NAME_FORMAT_ATTRIBUTE_NAME)) {
-            final String nameFormat = StringSupport.trimOrNull(config.getAttributeNS(null, NAME_FORMAT_ATTRIBUTE_NAME));
-            builder.addPropertyValue("nameFormat", nameFormat);
-        }
-
-        builder.addPropertyValue("friendlyName", config.getAttributeNS(null, FRIENDLY_NAME_ATTRIBUTE_NAME));
-    }
 }
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2ScopedStringAttributeEncoderParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2ScopedStringAttributeEncoderParser.java
index 8cbb0f6..3c0643a 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2ScopedStringAttributeEncoderParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2ScopedStringAttributeEncoderParser.java
@@ -17,62 +17,55 @@
 
 package net.shibboleth.idp.attribute.resolver.spring.enc.impl;
 
+import java.util.Map;
+
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import javax.xml.namespace.QName;
 
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.config.BeanReference;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.w3c.dom.Element;
 
-import net.shibboleth.idp.attribute.resolver.spring.enc.BaseScopedAttributeEncoderParser;
+import net.shibboleth.idp.attribute.resolver.spring.enc.BaseSAML2AttributeEncoderParser;
 import net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverNamespaceHandler;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML2ScopedStringAttributeEncoder;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2ScopedStringAttributeTranscoder;
 import net.shibboleth.utilities.java.support.primitive.StringSupport;
 
 /**
- * Spring Bean Definition Parser for {@link SAML2ScopedStringAttributeEncoder}.
+ * Spring Bean Definition Parser for {@link SAML2ScopedStringAttributeTranscoder}.
  */
-public class SAML2ScopedStringAttributeEncoderParser extends BaseScopedAttributeEncoderParser {
+public class SAML2ScopedStringAttributeEncoderParser extends BaseSAML2AttributeEncoderParser {
 
     /** Schema type name.. */
     @Nonnull public static final QName TYPE_NAME_RESOLVER = new QName(AttributeResolverNamespaceHandler.NAMESPACE,
             "SAML2ScopedString");
 
-    /** Local name of name format attribute. */
-    @Nonnull @NotEmpty public static final String NAME_FORMAT_ATTRIBUTE_NAME = "nameFormat";
-
-    /** Local name of friendly name attribute. */
-    @Nonnull @NotEmpty public static final String FRIENDLY_NAME_ATTRIBUTE_NAME = "friendlyName";
-
-    /** Constructor. */
-    public SAML2ScopedStringAttributeEncoderParser() {
-        setNameRequired(true);
-    }
-
-    /** {@inheritDoc} */
-    @Override protected Class<SAML2ScopedStringAttributeEncoder> getBeanClass(@Nullable final Element element) {
-        return SAML2ScopedStringAttributeEncoder.class;
-    }
-
     /** {@inheritDoc} */
     @Override protected void doParse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
-            @Nonnull final BeanDefinitionBuilder builder) {
-        super.doParse(config, parserContext, builder);
+            @Nonnull final Map<String,Object> rule) {
+        super.doParse(config, parserContext, rule);
 
-        if (config.hasAttributeNS(null, SCOPE_TYPE_ATTRIBUTE_NAME)) {
-            builder.addPropertyValue("scopeType",
-                    StringSupport.trimOrNull(config.getAttributeNS(null, SCOPE_TYPE_ATTRIBUTE_NAME)));
+        if (config.hasAttributeNS(null, "scopeType")) {
+            rule.put(SAML2ScopedStringAttributeTranscoder.PROP_SCOPE_TYPE,
+                    StringSupport.trimOrNull(config.getAttributeNS(null, "scopeType")));
+        }
+        
+        if (config.hasAttributeNS(null, "scopeDelimiter")) {
+            rule.put(SAML2ScopedStringAttributeTranscoder.PROP_SCOPE_DELIMITER,
+                    StringSupport.trimOrNull(config.getAttributeNS(null, "scopeDelimiter")));
         }
 
-        if (config.hasAttributeNS(null, NAME_FORMAT_ATTRIBUTE_NAME)) {
-            final String nameFormat = StringSupport.trimOrNull(config.getAttributeNS(null, NAME_FORMAT_ATTRIBUTE_NAME));
-            builder.addPropertyValue("nameFormat", nameFormat);
+        if (config.hasAttributeNS(null, "scopeAttribute")) {
+            rule.put(SAML2ScopedStringAttributeTranscoder.PROP_SCOPE_ATTR_NAME,
+                    StringSupport.trimOrNull(config.getAttributeNS(null, "scopeAttribute")));
         }
+    }
 
-        builder.addPropertyValue("friendlyName",
-                StringSupport.trimOrNull(config.getAttributeNS(null, FRIENDLY_NAME_ATTRIBUTE_NAME)));
+    /** {@inheritDoc} */
+    @Override
+    protected BeanReference buildTranscoder() {
+        return new RuntimeBeanReference("SAML2ScopedStringTranscoder");
     }
 
 }
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2StringAttributeEncoderParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2StringAttributeEncoderParser.java
index 89c64e7..0ae5203 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2StringAttributeEncoderParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2StringAttributeEncoderParser.java
@@ -18,55 +18,28 @@
 package net.shibboleth.idp.attribute.resolver.spring.enc.impl;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import javax.xml.namespace.QName;
 
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.xml.ParserContext;
-import org.w3c.dom.Element;
+import org.springframework.beans.factory.config.BeanReference;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
 
-import net.shibboleth.idp.attribute.resolver.spring.enc.BaseAttributeEncoderParser;
+import net.shibboleth.idp.attribute.resolver.spring.enc.BaseSAML2AttributeEncoderParser;
 import net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverNamespaceHandler;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML2StringAttributeEncoder;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2StringAttributeTranscoder;
 
 /**
- * Spring Bean Definition Parser for {@link SAML2StringAttributeEncoder}.
+ * Spring Bean Definition Parser for {@link SAML2StringAttributeTranscoder}.
  */
-public class SAML2StringAttributeEncoderParser extends BaseAttributeEncoderParser {
+public class SAML2StringAttributeEncoderParser extends BaseSAML2AttributeEncoderParser {
 
     /** Schema type name.. */
     @Nonnull public static final QName TYPE_NAME_RESOLVER = new QName(AttributeResolverNamespaceHandler.NAMESPACE, 
             "SAML2String");
 
-
-    /** Local name of name format attribute. */
-    @Nonnull @NotEmpty public static final String NAME_FORMAT_ATTRIBUTE_NAME = "nameFormat";
-
-    /** Local name of friendly name attribute. */
-    @Nonnull @NotEmpty public static final String FRIENDLY_NAME_ATTRIBUTE_NAME = "friendlyName";
-
-    /** Constructor. */
-    public SAML2StringAttributeEncoderParser() {
-        setNameRequired(true);
-    }
-
     /** {@inheritDoc} */
-    @Override protected Class<SAML2StringAttributeEncoder> getBeanClass(@Nullable final Element element) {
-        return SAML2StringAttributeEncoder.class;
+    @Override
+    protected BeanReference buildTranscoder() {
+        return new RuntimeBeanReference("SAML2StringTranscoder");
     }
 
-    /** {@inheritDoc} */
-    @Override protected void doParse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
-            @Nonnull final BeanDefinitionBuilder builder) {
-        super.doParse(config, parserContext, builder);
-
-        if (config.hasAttributeNS(null, NAME_FORMAT_ATTRIBUTE_NAME)) {
-            final String nameFormat = StringSupport.trimOrNull(config.getAttributeNS(null, NAME_FORMAT_ATTRIBUTE_NAME));
-            builder.addPropertyValue("nameFormat", nameFormat);
-        }
-
-        builder.addPropertyValue("friendlyName", config.getAttribute(FRIENDLY_NAME_ATTRIBUTE_NAME));
-    }
 }
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2XMLObjectAttributeEncoderParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2XMLObjectAttributeEncoderParser.java
index fe21954..2cf1a92 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2XMLObjectAttributeEncoderParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/enc/impl/SAML2XMLObjectAttributeEncoderParser.java
@@ -18,53 +18,26 @@
 package net.shibboleth.idp.attribute.resolver.spring.enc.impl;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import javax.xml.namespace.QName;
 
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.xml.ParserContext;
-import org.w3c.dom.Element;
+import org.springframework.beans.factory.config.BeanReference;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
 
-import net.shibboleth.idp.attribute.resolver.spring.enc.BaseAttributeEncoderParser;
+import net.shibboleth.idp.attribute.resolver.spring.enc.BaseSAML2AttributeEncoderParser;
 import net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverNamespaceHandler;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML2XMLObjectAttributeEncoder;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2XMLObjectAttributeTranscoder;
 
-/** Spring Bean Definition Parser for SAML2 XMLObject attribute encoder. */
-public class SAML2XMLObjectAttributeEncoderParser extends BaseAttributeEncoderParser {
+/** Spring Bean Definition Parser for {@link SAML2XMLObjectAttributeTranscoder}. */
+public class SAML2XMLObjectAttributeEncoderParser extends BaseSAML2AttributeEncoderParser {
 
     /** Schema type name.. */
     @Nonnull public static final QName TYPE_NAME_RESOLVER = new QName(AttributeResolverNamespaceHandler.NAMESPACE,
             "SAML2XMLObject");
 
-    /** Local name of name format attribute. */
-    @Nonnull @NotEmpty public static final String NAME_FORMAT_ATTRIBUTE_NAME = "nameFormat";
-
-    /** Local name of friendly name attribute. */
-    @Nonnull @NotEmpty public static final String FRIENDLY_NAME_ATTRIBUTE_NAME = "friendlyName";
-
-    /** Constructor. */
-    public SAML2XMLObjectAttributeEncoderParser() {
-        setNameRequired(true);
-    }
-
     /** {@inheritDoc} */
-    @Override protected Class<SAML2XMLObjectAttributeEncoder> getBeanClass(@Nullable final Element element) {
-        return SAML2XMLObjectAttributeEncoder.class;
+    @Override
+    protected BeanReference buildTranscoder() {
+        return new RuntimeBeanReference("SAML2XMLObjectTranscoder");
     }
 
-    /** {@inheritDoc} */
-    @Override protected void doParse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
-            @Nonnull final BeanDefinitionBuilder builder) {
-        super.doParse(config, parserContext, builder);
-
-        if (config.hasAttributeNS(null, NAME_FORMAT_ATTRIBUTE_NAME)) {
-            final String nameFormat = StringSupport.trimOrNull(config.getAttributeNS(null, NAME_FORMAT_ATTRIBUTE_NAME));
-            builder.addPropertyValue("nameFormat", nameFormat);
-        }
-
-        builder.addPropertyValue("friendlyName",
-                StringSupport.trimOrNull(config.getAttributeNS(null, FRIENDLY_NAME_ATTRIBUTE_NAME)));
-    }
 }
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/impl/AttributeResolverParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/impl/AttributeResolverParser.java
index 63b1f09..6e2536d 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/impl/AttributeResolverParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/impl/AttributeResolverParser.java
@@ -23,26 +23,23 @@ import java.util.Map;
 import javax.annotation.Nonnull;
 import javax.xml.namespace.QName;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.xml.BeanDefinitionParser;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
 
 import net.shibboleth.ext.spring.util.SpringSupport;
 import net.shibboleth.idp.attribute.resolver.spring.ad.BaseAttributeDefinitionParser;
 import net.shibboleth.idp.attribute.resolver.spring.dc.AbstractDataConnectorParser;
-import net.shibboleth.idp.attribute.resolver.spring.enc.BaseAttributeEncoderParser;
 import net.shibboleth.utilities.java.support.xml.ElementSupport;
 
 /**
  * Bean definition parser for an {@link net.shibboleth.idp.attribute.resolver.AttributeResolver}. <br/>
  * 
  * There is no bean being summoned up here. Rather we just parse all the children. Then over in the service all the
- * {@link net.shibboleth.idp.attribute.resolver.AttributeDefinition},
- * {@link net.shibboleth.idp.attribute.resolver.DataConnector} and
- * {@link net.shibboleth.idp.saml.attribute.principalconnector.impl.PrincipalConnector} beans are sucked out of Spring
+ * {@link net.shibboleth.idp.attribute.resolver.AttributeDefinition} and
+ * {@link net.shibboleth.idp.attribute.resolver.DataConnector} beans are sucked out of Spring
  * by type and injected into a new {@link net.shibboleth.idp.attribute.resolver.impl.AttributeResolverImpl} via a
  * {@link net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverServiceStrategy}.
  */
@@ -55,9 +52,6 @@ public class AttributeResolverParser implements BeanDefinitionParser {
     /** Schema type. */
     @Nonnull public static final QName SCHEMA_TYPE = new QName(AttributeResolverNamespaceHandler.NAMESPACE,
             "AttributeResolverType");
-    
-    /** Log4j logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(BaseAttributeEncoderParser.class);
 
     /**
      * {@inheritDoc}
@@ -68,11 +62,17 @@ public class AttributeResolverParser implements BeanDefinitionParser {
         List<Element> children;
 
         children = configChildren.get(BaseAttributeDefinitionParser.ELEMENT_NAME);
-        SpringSupport.parseCustomElements(children, context);
+        SpringSupport.parseLazyInitCustomElements(children, context);
 
         children = configChildren.get(AbstractDataConnectorParser.ELEMENT_NAME);
-        SpringSupport.parseCustomElements(children, context);
+        SpringSupport.parseLazyInitCustomElements(children, context);
 
+        final NodeList attributeEncoders =
+                config.getElementsByTagNameNS(AttributeResolverNamespaceHandler.NAMESPACE, "AttributeEncoder");
+        for (int i = 0; i < attributeEncoders.getLength(); ++i) {
+            SpringSupport.parseLazyInitCustomElement((Element) attributeEncoders.item(i), context);
+        }
+        
         return null;
     }
 
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/transcoding/spring/impl/AttributeRegistryServiceStrategy.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/transcoding/spring/impl/AttributeRegistryServiceStrategy.java
new file mode 100644
index 0000000..c290c23
--- /dev/null
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/transcoding/spring/impl/AttributeRegistryServiceStrategy.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.transcoding.spring.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContext;
+
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
+import net.shibboleth.idp.attribute.transcoding.impl.TranscodingRuleLoader;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.utilities.java.support.service.ServiceException;
+import net.shibboleth.utilities.java.support.service.ServiceableComponent;
+
+/**
+ * Strategy for summoning up an {@link AttributeTranscoderRegistryImpl} from a populated {@link ApplicationContext}.
+ */
+public class AttributeRegistryServiceStrategy extends AbstractIdentifiableInitializableComponent implements
+        Function<ApplicationContext,ServiceableComponent<AttributeTranscoderRegistry>> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(AttributeRegistryServiceStrategy.class);
+
+    /** Name of bean to supply naming function registry property. */
+    @Nullable @NotEmpty private String namingRegistry;
+    
+    /**
+     * Set the name of the bean providing the {@link AttributeTranscoderRegistryImpl#setNamingRegistry()} value.
+     * 
+     * @param beanName name of bean of type {@link Map}
+     */
+    public void setNamingRegistry(@Nullable @NotEmpty final String beanName) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        namingRegistry = StringSupport.trimOrNull(beanName);
+    }
+    
+    /** {@inheritDoc} */
+    @Nullable public ServiceableComponent<AttributeTranscoderRegistry> apply(
+            @Nullable final ApplicationContext appContext) {
+
+        final Map<Class<?>,Function<?,String>> namingRegistryBean = appContext.getBean(namingRegistry, Map.class);
+        
+        final Collection<TranscodingRule> mappingBeans = appContext.getBeansOfType(TranscodingRule.class).values();
+        final Collection<TranscodingRuleLoader> loaderBeans =
+                appContext.getBeansOfType(TranscodingRuleLoader.class).values();
+
+        final Collection<TranscodingRule> holder = new ArrayList<>();
+        if (mappingBeans != null) {
+            holder.addAll(mappingBeans);
+        }
+        if (loaderBeans != null) {
+            loaderBeans.forEach(loader -> holder.addAll(loader.getRules()));
+        }
+        
+        final AttributeTranscoderRegistryImpl registry = new AttributeTranscoderRegistryImpl();
+        registry.setId(getId());
+        registry.setApplicationContext(appContext);
+        registry.setNamingRegistry(namingRegistryBean);
+        registry.setTranscoderRegistry(holder);
+
+        try {
+            registry.initialize();
+        } catch (final ComponentInitializationException e) {
+            throw new ServiceException("Unable to initialize attribute transcoder registry for "
+                    + appContext.getDisplayName(), e);
+        }
+        return registry;
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/package-info.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/transcoding/spring/impl/package-info.java
similarity index 83%
rename from idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/package-info.java
rename to idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/transcoding/spring/impl/package-info.java
index 9b82bcc..1278015 100644
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/package-info.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/transcoding/spring/impl/package-info.java
@@ -16,8 +16,8 @@
  */
 
 /**
- * Classes in this package are to do with the import of attributes
- * as an adjunct to filtering.
+ * For want of a better place. If there are additional classes needed, this
+ * may move to a dedicated module.
  */
 
-package net.shibboleth.idp.saml.attribute.mapping;
\ No newline at end of file
+package net.shibboleth.idp.attribute.transcoding.spring.impl;
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeMapperTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeMapperTest.java
index da44ed6..ab688f0 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeMapperTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeMapperTest.java
@@ -17,9 +17,7 @@
 
 package net.shibboleth.idp.attribute.resolver.spring;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
+import static org.testng.Assert.*;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -27,7 +25,6 @@ import java.util.HashSet;
 
 import org.opensaml.core.OpenSAMLInitBaseTestCase;
 import org.opensaml.saml.saml2.core.Attribute;
-import org.opensaml.saml.saml2.metadata.RequestedAttribute;
 import org.springframework.context.support.ConversionServiceFactoryBean;
 import org.springframework.context.support.GenericApplicationContext;
 import org.testng.annotations.AfterMethod;
@@ -35,15 +32,13 @@ import org.testng.annotations.Test;
 
 import net.shibboleth.ext.spring.config.StringToDurationConverter;
 import net.shibboleth.ext.spring.util.SchemaTypeAwareXMLBeanDefinitionReader;
-import net.shibboleth.idp.attribute.IdPRequestedAttribute;
-import net.shibboleth.idp.attribute.resolver.AttributeResolver;
+import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.resolver.ResolutionException;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeMapper;
-import net.shibboleth.idp.saml.attribute.mapping.AttributeMapper;
-import net.shibboleth.idp.saml.attribute.mapping.impl.RequestedAttributesMapper;
-import net.shibboleth.idp.saml.attribute.mapping.impl.ScopedStringAttributeValueMapper;
-import net.shibboleth.idp.saml.attribute.mapping.impl.StringAttributeValueMapper;
-import net.shibboleth.idp.saml.attribute.mapping.impl.XMLObjectAttributeValueMapper;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2ScopedStringAttributeTranscoder;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.service.ReloadableService;
 import net.shibboleth.utilities.java.support.service.ServiceException;
@@ -88,33 +83,29 @@ public class AttributeMapperTest extends OpenSAMLInitBaseTestCase {
         beanDefinitionReader.loadBeanDefinitions("net/shibboleth/idp/attribute/resolver/spring/mapperTest.xml");
         context.refresh();
 
-        final ReloadableService<AttributeResolver> attributeResolverService = context.getBean(ReloadableService.class);
+        final ReloadableService<AttributeTranscoderRegistry> transcoderRegistry = context.getBean(ReloadableService.class);
 
-        attributeResolverService.initialize();
-
-        ServiceableComponent<AttributeResolver> serviceableComponent = null;
-        RequestedAttributesMapper attributesMapper = null;
+        ServiceableComponent<AttributeTranscoderRegistry> serviceableComponent = null;
         try {
-            serviceableComponent = attributeResolverService.getServiceableComponent();
-
-            attributesMapper = new RequestedAttributesMapper(serviceableComponent.getComponent());
+            serviceableComponent = transcoderRegistry.getServiceableComponent();
+            
+            Collection<TranscodingRule> rulesets = serviceableComponent.getComponent().getTranscodingRules(
+                    new IdPAttribute("eduPersonScopedAffiliation"), Attribute.class);
+            assertEquals(rulesets.size(), 1);
+            final TranscodingRule rule = rulesets.iterator().next();
+            assertEquals(rule.get(SAML2AttributeTranscoder.PROP_NAME, String.class), "urn:oid:1.3.6.1.4.1.5923.1.1.1.9");
+            assertNull(rule.get(SAML2AttributeTranscoder.PROP_NAME_FORMAT, String.class));
+            assertEquals(rule.get(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME, String.class), "feduPersonScopedAffiliation");
+            assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER, AttributeTranscoder.class) instanceof SAML2ScopedStringAttributeTranscoder);
+            assertEquals(rule.get(SAML2ScopedStringAttributeTranscoder.PROP_SCOPE_DELIMITER, String.class), "#");
         } finally {
             serviceableComponent.unpinComponent();
         }
 
-        Collection<AttributeMapper<RequestedAttribute, IdPRequestedAttribute>> mappers = attributesMapper.getMappers();
-        assertEquals(mappers.size(), 5);
-
+        /*
         for (AttributeMapper<RequestedAttribute, IdPRequestedAttribute> mapper : mappers) {
             AbstractSAMLAttributeMapper sMappers = (AbstractSAMLAttributeMapper) mapper;
             if (mapper.getId().equals("MapperForfeduPersonScopedAffiliation")) {
-                assertEquals(sMappers.getSAMLName(), "urn:oid:1.3.6.1.4.1.5923.1.1.1.9");
-                assertEquals(sMappers.getAttributeFormat(), Attribute.URI_REFERENCE);
-                assertEquals(sMappers.getAttributeIds().size(), 1);
-                assertEquals(sMappers.getAttributeIds().get(0), "eduPersonScopedAffiliation");
-                ScopedStringAttributeValueMapper valueMapper =
-                        (ScopedStringAttributeValueMapper) sMappers.getValueMapper();
-                assertEquals(valueMapper.getDelimiter(), "#");
             } else if (mapper.getId().equals("MapperForfeduPersonAssurance")) {
                 assertEquals(sMappers.getSAMLName(), "urn:oid:1.3.6.1.4.1.5923.1.1.1.11");
                 assertEquals(sMappers.getAttributeFormat(), Attribute.URI_REFERENCE);
@@ -144,5 +135,8 @@ public class AttributeMapperTest extends OpenSAMLInitBaseTestCase {
                 fail();
             }
         }
+        */
+        
     }
-}
+    
+}
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeMappingNodeProcessorTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeMappingNodeProcessorTest.java
index 6fd00ff..02df1e3 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeMappingNodeProcessorTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeMappingNodeProcessorTest.java
@@ -32,7 +32,7 @@ import org.opensaml.saml.saml2.metadata.AttributeConsumingService;
 import org.opensaml.saml.saml2.metadata.EntityDescriptor;
 import org.springframework.context.support.ConversionServiceFactoryBean;
 import org.springframework.context.support.GenericApplicationContext;
-import org.testng.annotations.AfterMethod;
+import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -43,8 +43,8 @@ import net.shibboleth.ext.spring.util.SchemaTypeAwareXMLBeanDefinitionReader;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.IdPRequestedAttribute;
 import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.idp.attribute.resolver.AttributeResolver;
-import net.shibboleth.idp.saml.attribute.mapping.AttributesMapContainer;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.saml.attribute.transcoding.AttributesMapContainer;
 import net.shibboleth.idp.saml.metadata.impl.AttributeMappingNodeProcessor;
 import net.shibboleth.utilities.java.support.service.ReloadableService;
 
@@ -55,13 +55,13 @@ public class AttributeMappingNodeProcessorTest extends XMLObjectBaseTestCase {
 
     private EntityDescriptor entityDescriptor;
 
-    private ReloadableService<AttributeResolver> service;
+    private ReloadableService<AttributeTranscoderRegistry> service;
 
     private AttributeMappingNodeProcessor processor;
 
     private GenericApplicationContext pendingTeardownContext = null;
     
-    @AfterMethod public void tearDownTestContext() {
+    @AfterClass public void tearDownTestContext() {
         if (null == pendingTeardownContext ) {
             return;
         }
@@ -82,7 +82,7 @@ public class AttributeMappingNodeProcessorTest extends XMLObjectBaseTestCase {
         processor = new AttributeMappingNodeProcessor(service);
     }
 
-    private ReloadableService<AttributeResolver> getService() {
+    private ReloadableService<AttributeTranscoderRegistry> getService() {
         GenericApplicationContext context = new GenericApplicationContext();
         setTestContext(context);
         context.setDisplayName("ApplicationContext: ");
@@ -97,11 +97,12 @@ public class AttributeMappingNodeProcessorTest extends XMLObjectBaseTestCase {
         SchemaTypeAwareXMLBeanDefinitionReader beanDefinitionReader =
                 new SchemaTypeAwareXMLBeanDefinitionReader(context);
 
-        beanDefinitionReader.loadBeanDefinitions("/net/shibboleth/idp/attribute/resolver/filter/service.xml");
+        beanDefinitionReader.loadBeanDefinitions(
+                "/net/shibboleth/idp/attribute/resolver/spring/customBean.xml",
+                "/net/shibboleth/idp/attribute/resolver/filter/service.xml");
         context.refresh();
 
-        final ReloadableService<AttributeResolver> attributeResolverService = context.getBean(ReloadableService.class);
-        return attributeResolverService;
+        return context.getBean(ReloadableService.class);
     }
 
     @Test public void entityAttributes() throws FilterException {
@@ -138,7 +139,7 @@ public class AttributeMappingNodeProcessorTest extends XMLObjectBaseTestCase {
 
         final AttributesMapContainer container = acs.getObjectMetadata().get(AttributesMapContainer.class).get(0);
 
-        final Multimap<String, IdPRequestedAttribute> map = container.get();
+        final Multimap<String,IdPRequestedAttribute> map = container.get();
 
         assertEquals(map.size(), 3);
 
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/BaseAttributeDefinitionParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/BaseAttributeDefinitionParserTest.java
index 082fc1f..0066528 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/BaseAttributeDefinitionParserTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/BaseAttributeDefinitionParserTest.java
@@ -36,7 +36,6 @@ import net.shibboleth.ext.spring.config.StringToIPRangeConverter;
 import net.shibboleth.ext.spring.config.StringToResourceConverter;
 import net.shibboleth.ext.spring.context.FilesystemGenericApplicationContext;
 import net.shibboleth.ext.spring.util.SchemaTypeAwareXMLBeanDefinitionReader;
-import net.shibboleth.idp.attribute.AttributeEncoder;
 import net.shibboleth.idp.attribute.resolver.AttributeDefinition;
 import net.shibboleth.idp.attribute.resolver.DataConnector;
 import net.shibboleth.idp.attribute.resolver.impl.AttributeResolverImpl;
@@ -63,7 +62,7 @@ public abstract class BaseAttributeDefinitionParserTest extends OpenSAMLInitBase
 
     public static final String PRINCIPALCONNECTOR_FILE_PATH = BEAN_FILE_PATH + "pc/";
     
-    private GenericApplicationContext pendingTeardownContext = null;
+    protected GenericApplicationContext pendingTeardownContext = null;
     
     @AfterMethod public void tearDownTestContext() {
         if (null == pendingTeardownContext ) {
@@ -178,8 +177,6 @@ public abstract class BaseAttributeDefinitionParserTest extends OpenSAMLInitBase
         return getDataConnector(fileName, claz, false);
     }
     
-    
-
     private <Type extends DataConnector> Type
             getDataConnector(final String fileName, final Class<Type> claz, final boolean supressValid) {
 
@@ -191,23 +188,6 @@ public abstract class BaseAttributeDefinitionParserTest extends OpenSAMLInitBase
         return getBean(DATACONNECTOR_FILE_PATH + fileName, claz, context, supressValid);
     }
 
-    protected <Type extends AttributeEncoder> Type getAttributeEncoder(final String fileName, final Class<Type> claz) {
-
-        final GenericApplicationContext context = new GenericApplicationContext();
-        setTestContext(context);
-        context.setDisplayName("ApplicationContext: " + claz);
-
-        return getAttributeEncoder(fileName, claz, context);
-
-    }
-
-    protected <Type extends AttributeEncoder> Type getAttributeEncoder(final String fileName, final Class<Type> claz,
-            final GenericApplicationContext context) {
-
-        return getBean(ENCODER_FILE_PATH + fileName, claz, context);
-
-    }
-
     static public AttributeResolverImpl getResolver(final ApplicationContext appCtx) {
         final AttributeResolverServiceStrategy strategy = new AttributeResolverServiceStrategy();
         strategy.setId("testResolver");
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/BaseEncoderDefinitionParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/BaseEncoderDefinitionParserTest.java
new file mode 100644
index 0000000..f6a0437
--- /dev/null
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/BaseEncoderDefinitionParserTest.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.attribute.resolver.spring;
+
+import static net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder.PROP_ENCODE_TYPE;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Map;
+
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.core.env.MutablePropertySources;
+import org.springframework.core.env.StandardEnvironment;
+import org.springframework.mock.env.MockPropertySource;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+
+/**
+ * Base class for testing Attribute Encoding Parsers
+ */
+public abstract class BaseEncoderDefinitionParserTest extends BaseAttributeDefinitionParserTest {
+
+    protected TranscodingRule getAttributeTranscoderRule(final String fileName) {
+        return getAttributeTranscoderRule(fileName, null, null);
+    }
+    
+    protected TranscodingRule getAttributeTranscoderRule(final String fileName,final boolean activation, final Boolean encodeType) {
+        final String encodeTypeString;
+        if (null == encodeType) {
+            encodeTypeString = null;
+        } else if (encodeType ){  
+            encodeTypeString = "true";
+        } else { 
+            encodeTypeString = "false";
+        } 
+        return getAttributeTranscoderRule(fileName, activation?"true":"false", encodeTypeString);
+    }
+
+    private TranscodingRule getAttributeTranscoderRule(final String fileName,
+            final String activationValue,
+            final String encodeType) {
+
+        final GenericApplicationContext context = new GenericApplicationContext();
+
+        final MockPropertySource mockEnvVars = new MockPropertySource();
+        if (activationValue != null) {
+            mockEnvVars.setProperty("the.activation.property", activationValue);
+        }
+        if (encodeType != null) {
+            mockEnvVars.setProperty("the.encodeType.property", encodeType);
+        }
+        mockEnvVars.setProperty("the.empty.property", "");
+        final MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
+        propertySources.replace(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, mockEnvVars);
+
+        return getAttributeTranscoderRule(fileName, context);
+    }
+    
+    protected TranscodingRule getAttributeTranscoderRule(final String fileName, final GenericApplicationContext context) {
+
+        setTestContext(context);
+        context.setDisplayName("ApplicationContext for enccoder");
+
+        return getBean(ENCODER_FILE_PATH + fileName, TranscodingRule.class, context);
+
+    }
+
+    static protected void checkEncodeType(final Map<String,Object> rule, boolean expectedValue) {
+        final Object encodeType = rule.getOrDefault(PROP_ENCODE_TYPE, Boolean.TRUE);
+        if (encodeType instanceof Boolean) {
+            assertTrue(encodeType.equals(expectedValue));
+        } else {
+            assertTrue(expectedValue);
+        }
+    }
+    
+    abstract protected void testWithProperties(final boolean activation, final Boolean encodeType);
+    
+    @Test public void values() {
+        testWithProperties(true);
+        testWithProperties(false);
+    }
+    
+    private void testWithProperties(final boolean activation) {
+        testWithProperties(activation, null);
+        testWithProperties(activation, true);        
+        testWithProperties(activation, false);
+    }
+}
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/ad/SimpleAttributeParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/ad/SimpleAttributeParserTest.java
index 2857dd6..4d3c397 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/ad/SimpleAttributeParserTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/ad/SimpleAttributeParserTest.java
@@ -17,12 +17,9 @@
 
 package net.shibboleth.idp.attribute.resolver.spring.ad;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.*;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
@@ -44,8 +41,6 @@ import net.shibboleth.idp.attribute.resolver.spring.BaseAttributeDefinitionParse
 import net.shibboleth.idp.attribute.resolver.spring.ad.impl.SimpleAttributeDefinitionParser;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.idp.profile.logic.RelyingPartyIdPredicate;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML1StringAttributeEncoder;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML2StringAttributeEncoder;
 import net.shibboleth.idp.saml.impl.TestSources;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 
@@ -63,7 +58,8 @@ public class SimpleAttributeParserTest extends BaseAttributeDefinitionParserTest
         assertTrue(attrDef.getDisplayDescriptions().isEmpty(), "getDisplayDescriptions().isEmpty()");
         assertTrue(attrDef.getDisplayNames().isEmpty(), "getDisplayNames().isEmpty()");
         assertEquals(attrDef.getAttributeDependencies().size(), 1);
-        assertTrue(attrDef.getAttributeEncoders().isEmpty(), "getgetAttributeEncoders().isEmpty()");
+        
+        assertTrue(pendingTeardownContext.getBeansOfType(Collection.class).isEmpty());
     }
 
     @Test public void simplePopulated() throws ComponentInitializationException {
@@ -96,13 +92,6 @@ public class SimpleAttributeParserTest extends BaseAttributeDefinitionParserTest
         final ResolverDataConnectorDependency dcDep = dcDeps.iterator().next();
         assertEquals(dcDep.getDependencyPluginId(), "con1");
         assertTrue(dcDep.getAttributeNames().contains("dep1"));
-
-        assertEquals(attrDef.getAttributeEncoders().size(), 1);
-        final SAML2StringAttributeEncoder e1 =
-                (SAML2StringAttributeEncoder) attrDef.getAttributeEncoders().iterator().next();
-        assertEquals(e1.getName(), "urn:oid:0.9.2342.19200300.100.1.3");
-        assertEquals(e1.getFriendlyName(), "mail");
-
     }
 
     @Test public void populated2() throws ComponentInitializationException {
@@ -126,24 +115,6 @@ public class SimpleAttributeParserTest extends BaseAttributeDefinitionParserTest
 
         final Set<ResolverDataConnectorDependency> dcDeps = attrDef.getDataConnectorDependencies();
         assertEquals(dcDeps.size(), 0, "getDataConnectorDependencies");
-
-        
-        assertEquals(attrDef.getAttributeEncoders().size(), 2);
-        final List a = new ArrayList(attrDef.getAttributeEncoders());
-
-        final SAML2StringAttributeEncoder saml2;
-        final SAML1StringAttributeEncoder saml1;
-        if (a.get(0) instanceof SAML2StringAttributeEncoder) {
-            saml2 = (SAML2StringAttributeEncoder) a.get(0);
-            saml1 = (SAML1StringAttributeEncoder) a.get(1);
-        } else {
-            saml2 = (SAML2StringAttributeEncoder) a.get(1);
-            saml1 = (SAML1StringAttributeEncoder) a.get(0);
-        }
-        assertEquals(saml2.getName(), "urn:oid:0.9.2342.19200300.100.1.3");
-        assertEquals(saml2.getFriendlyName(), "mail");
-
-        assertEquals(saml1.getName(), "urn:mace:dir:attribute-def:mail");
     }
 
     @Test public void bad() throws ComponentInitializationException {
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/Regressions.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/Regressions.java
index a0201e3..136a68b 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/Regressions.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/Regressions.java
@@ -17,19 +17,22 @@
 
 package net.shibboleth.idp.attribute.resolver.spring.enc;
 
-import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.*;
 
 import java.util.Collection;
+import java.util.Map;
 
 import org.springframework.context.support.GenericApplicationContext;
 import org.testng.annotations.Test;
 
-import net.shibboleth.idp.attribute.AttributeEncoder;
 import net.shibboleth.idp.attribute.resolver.AttributeDefinition;
 import net.shibboleth.idp.attribute.resolver.spring.BaseAttributeDefinitionParserTest;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2StringAttributeTranscoder;
 
 /**
- *
+ * Test for regressions identified and patched.
  */
 public class Regressions extends BaseAttributeDefinitionParserTest {
 
@@ -39,10 +42,15 @@ public class Regressions extends BaseAttributeDefinitionParserTest {
         loadFile(ENCODER_FILE_PATH + "resolver/idp-571.xml", context);
         context.refresh();
      
-        Collection<AttributeEncoder> encoders = context.getBeansOfType(AttributeEncoder.class).values();
         Collection<AttributeDefinition> definitions = context.getBeansOfType(AttributeDefinition.class).values();
+        Collection<TranscodingRule> transcoderRules = context.getBeansOfType(TranscodingRule.class).values();
         
-        assertEquals(encoders.size(), 1);
         assertEquals(definitions.size(), 1);
+        assertEquals(transcoderRules.size(), 1);
+        
+        final Map<String,Object> rule = transcoderRules.iterator().next().getMap();
+        assertEquals(rule.get(AttributeTranscoderRegistry.PROP_ID), "skillsoftdept");
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML2StringAttributeTranscoder);
     }
-}
+
+}
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1Base64AttributeEncoderParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1Base64AttributeEncoderParserTest.java
index 4d0e03a..cacbd1e 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1Base64AttributeEncoderParserTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1Base64AttributeEncoderParserTest.java
@@ -18,38 +18,52 @@
 package net.shibboleth.idp.attribute.resolver.spring.enc;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Map;
+import java.util.function.Predicate;
 
 import org.springframework.beans.factory.BeanDefinitionStoreException;
 import org.testng.annotations.Test;
 
-import net.shibboleth.idp.attribute.resolver.spring.BaseAttributeDefinitionParserTest;
+import net.shibboleth.idp.attribute.resolver.spring.BaseEncoderDefinitionParserTest;
 import net.shibboleth.idp.attribute.resolver.spring.enc.impl.SAML1Base64AttributeEncoderParser;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML1ByteAttributeEncoder;
-import net.shibboleth.idp.saml.xml.SAMLConstants;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1ByteAttributeTranscoder;
 
 /**
  * Test for {@link SAML1Base64AttributeEncoderParser}.
  */
-public class SAML1Base64AttributeEncoderParserTest extends BaseAttributeDefinitionParserTest {
+public class SAML1Base64AttributeEncoderParserTest extends BaseEncoderDefinitionParserTest {
   
-    @Test public void resolver() {
-        final SAML1ByteAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml1Base64.xml", SAML1ByteAttributeEncoder.class);
+    protected void testWithProperties(final boolean activation, final Boolean encodeType) {
+        
+        final Map<String,Object> rule =
+                getAttributeTranscoderRule("resolver/saml1Base64.xml", activation, encodeType).getMap();
 
-        assertEquals(encoder.getName(), "SAML1_BASE64_ATTRIBUTE_NAME");
-        assertEquals(encoder.getNamespace(),"SAML1_BASE64_ATTRIBUTE_NAME_SPACE");
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML1ByteAttributeTranscoder);
+        assertEquals(rule.get(SAMLAttributeTranscoder.PROP_NAME), "SAML1_BASE64_ATTRIBUTE_NAME");
+        assertEquals(rule.get(SAML1AttributeTranscoder.PROP_NAMESPACE), "SAML1_BASE64_ATTRIBUTE_NAME_SPACE");
+        assertEquals(activation, ((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, encodeType==null ? false: encodeType);
     }
 
     
     @Test public void defaultCase() {
-        final SAML1ByteAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml1Base64Default.xml", SAML1ByteAttributeEncoder.class);
+        final Map<String,Object> rule = getAttributeTranscoderRule("resolver/saml1Base64Default.xml").getMap();
 
-        assertEquals(encoder.getName(), "Base64_ATTRIBUTE");
-        assertEquals(encoder.getNamespace(), SAMLConstants.SAML1_ATTR_NAMESPACE_URI);
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML1ByteAttributeTranscoder);
+        assertEquals(rule.get(SAML1AttributeTranscoder.PROP_NAME), "Base64_ATTRIBUTE");
+        assertNull(rule.get(SAML1AttributeTranscoder.PROP_NAMESPACE));
+        assertFalse(((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, true);
     }
     
     @Test(expectedExceptions={BeanDefinitionStoreException.class,})  public void noName() {
-        getAttributeEncoder("resolver/saml1Base64NoName.xml", SAML1ByteAttributeEncoder.class);
+        getAttributeTranscoderRule("resolver/saml1Base64NoName.xml");
     }
 }
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1ScopedStringAttributeEncoderParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1ScopedStringAttributeEncoderParserTest.java
index b31b7ed..cbbcb5a 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1ScopedStringAttributeEncoderParserTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1ScopedStringAttributeEncoderParserTest.java
@@ -18,44 +18,59 @@
 package net.shibboleth.idp.attribute.resolver.spring.enc;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Map;
+import java.util.function.Predicate;
 
 import org.springframework.beans.factory.BeanDefinitionStoreException;
 import org.testng.annotations.Test;
 
-import net.shibboleth.idp.attribute.resolver.spring.BaseAttributeDefinitionParserTest;
+import net.shibboleth.idp.attribute.resolver.spring.BaseEncoderDefinitionParserTest;
 import net.shibboleth.idp.attribute.resolver.spring.enc.impl.SAML1ScopedStringAttributeEncoderParser;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML1ScopedStringAttributeEncoder;
-import net.shibboleth.idp.saml.xml.SAMLConstants;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1ScopedStringAttributeTranscoder;
 
 /**
  * Test for {@link SAML1ScopedStringAttributeEncoderParser}.
  */
-public class SAML1ScopedStringAttributeEncoderParserTest extends BaseAttributeDefinitionParserTest {
+public class SAML1ScopedStringAttributeEncoderParserTest extends BaseEncoderDefinitionParserTest {
 
-    @Test public void resolver() {
-        final SAML1ScopedStringAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml1Scoped.xml", SAML1ScopedStringAttributeEncoder.class);
+    protected void testWithProperties(final boolean activation, final Boolean encodeType) {
+        
+        final Map<String,Object> rule =
+                getAttributeTranscoderRule("resolver/saml1Scoped.xml", activation, encodeType).getMap();
 
-        assertEquals(encoder.getName(), "SAML1_SCOPED_ATTRIBUTE_NAME");
-        assertEquals(encoder.getNamespace(),"SAML1_SCOPED_ATTRIBUTE_NAME_FORMAT");
-        assertEquals(encoder.getScopeType(),"attribute");
-        assertEquals(encoder.getScopeAttributeName(),"saml1ScopeAttrib");
-        assertEquals(encoder.getScopeDelimiter(),"#@#");
-    }
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML1ScopedStringAttributeTranscoder);
+        assertEquals(rule.get(SAMLAttributeTranscoder.PROP_NAME), "SAML1_SCOPED_ATTRIBUTE_NAME");
+        assertEquals(rule.get(SAML1AttributeTranscoder.PROP_NAMESPACE), "SAML1_SCOPED_ATTRIBUTE_NAME_FORMAT");
+        assertEquals(rule.get(SAML1ScopedStringAttributeTranscoder.PROP_SCOPE_TYPE), "attribute");
+        assertEquals(rule.get(SAML1ScopedStringAttributeTranscoder.PROP_SCOPE_ATTR_NAME), "saml1ScopeAttrib");
+        assertEquals(rule.get(SAML1ScopedStringAttributeTranscoder.PROP_SCOPE_DELIMITER), "#@#");
+        assertEquals(activation, ((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, encodeType!=null ? encodeType : false);
+}
 
     
     @Test public void defaultCase() {
-        final SAML1ScopedStringAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml1ScopedDefault.xml", SAML1ScopedStringAttributeEncoder.class);
-
-        assertEquals(encoder.getName(), "saml1_scoped_name");
-        assertEquals(encoder.getNamespace(), SAMLConstants.SAML1_ATTR_NAMESPACE_URI);
-        assertEquals(encoder.getScopeType(),"attribute");
-        assertEquals(encoder.getScopeDelimiter(),"@");
-        assertEquals(encoder.getScopeAttributeName(),"Scope");
+        final Map<String,Object> rule = getAttributeTranscoderRule("resolver/saml1ScopedDefault.xml").getMap();
+
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML1ScopedStringAttributeTranscoder);
+        assertEquals(rule.get(SAMLAttributeTranscoder.PROP_NAME), "saml1_scoped_name");
+        assertNull(rule.get(SAML1AttributeTranscoder.PROP_NAMESPACE));
+        assertNull(rule.get(SAML1ScopedStringAttributeTranscoder.PROP_SCOPE_TYPE));
+        assertNull(rule.get(SAML1ScopedStringAttributeTranscoder.PROP_SCOPE_ATTR_NAME));
+        assertNull(rule.get(SAML1ScopedStringAttributeTranscoder.PROP_SCOPE_DELIMITER));
+        assertFalse(((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, true);
     }
     
     @Test(expectedExceptions={BeanDefinitionStoreException.class,})  public void noName() {
-        getAttributeEncoder("resolver/saml1ScopedNoName.xml", SAML1ScopedStringAttributeEncoder.class);
+        getAttributeTranscoderRule("resolver/saml1ScopedNoName.xml");
     }
-}
+
+}
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1StringAttributeEncoderParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1StringAttributeEncoderParserTest.java
index 9cd4a60..ee124ba 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1StringAttributeEncoderParserTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1StringAttributeEncoderParserTest.java
@@ -18,38 +18,53 @@
 package net.shibboleth.idp.attribute.resolver.spring.enc;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Map;
+import java.util.function.Predicate;
 
 import org.springframework.beans.factory.BeanDefinitionStoreException;
 import org.testng.annotations.Test;
 
-import net.shibboleth.idp.attribute.resolver.spring.BaseAttributeDefinitionParserTest;
+import net.shibboleth.idp.attribute.resolver.spring.BaseEncoderDefinitionParserTest;
 import net.shibboleth.idp.attribute.resolver.spring.enc.impl.SAML1StringAttributeEncoderParser;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML1StringAttributeEncoder;
-import net.shibboleth.idp.saml.xml.SAMLConstants;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1StringAttributeTranscoder;
 
 /**
  * Test for {@link SAML1StringAttributeEncoderParser}.
  */
-public class SAML1StringAttributeEncoderParserTest extends BaseAttributeDefinitionParserTest {
+public class SAML1StringAttributeEncoderParserTest extends BaseEncoderDefinitionParserTest {
 
-    @Test public void newNameSpace() {
-        final SAML1StringAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml1String.xml", SAML1StringAttributeEncoder.class);
+    protected void testWithProperties(final boolean activation, final Boolean encodeType) {
+        
+        final Map<String,Object> rule =
+                getAttributeTranscoderRule("resolver/saml1String.xml", activation, encodeType).getMap();
 
-        assertEquals(encoder.getName(), "SAML1_STRING_ATTRIBUTE_NAME");
-        assertEquals(encoder.getNamespace(),"SAML1_STRING_ATTRIBUTE_NAME_SPACE");
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML1StringAttributeTranscoder);
+        assertEquals(rule.get(SAMLAttributeTranscoder.PROP_NAME), "SAML1_STRING_ATTRIBUTE_NAME");
+        assertEquals(rule.get(SAML1AttributeTranscoder.PROP_NAMESPACE), "SAML1_STRING_ATTRIBUTE_NAME_SPACE");
+        assertEquals(activation, ((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, true);
     }
 
     
     @Test public void defaultCase() {
-        final SAML1StringAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml1StringDefault.xml", SAML1StringAttributeEncoder.class);
+        final Map<String,Object> rule = getAttributeTranscoderRule("resolver/saml1StringDefault.xml").getMap();
 
-        assertEquals(encoder.getName(), "ATTRIBUTE");
-        assertEquals(encoder.getNamespace(), SAMLConstants.SAML1_ATTR_NAMESPACE_URI);
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML1StringAttributeTranscoder);
+        assertEquals(rule.get(SAMLAttributeTranscoder.PROP_NAME), "ATTRIBUTE");
+        assertNull(rule.get(SAML1AttributeTranscoder.PROP_NAMESPACE));
+        assertFalse(((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, true);
     }
     
     @Test(expectedExceptions={BeanDefinitionStoreException.class,})  public void noName() {
-        getAttributeEncoder("resolver/saml1StringNoName.xml", SAML1StringAttributeEncoder.class);
+        getAttributeTranscoderRule("resolver/saml1StringNoName.xml");
     }
-}
+    
+}
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1XMLObjectAttributeEncoderParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1XMLObjectAttributeEncoderParserTest.java
index ccda6b2..7f9f62a 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1XMLObjectAttributeEncoderParserTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML1XMLObjectAttributeEncoderParserTest.java
@@ -18,37 +18,51 @@
 package net.shibboleth.idp.attribute.resolver.spring.enc;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Map;
+import java.util.function.Predicate;
 
 import org.springframework.beans.factory.BeanDefinitionStoreException;
 import org.testng.annotations.Test;
 
-import net.shibboleth.idp.attribute.resolver.spring.BaseAttributeDefinitionParserTest;
+import net.shibboleth.idp.attribute.resolver.spring.BaseEncoderDefinitionParserTest;
 import net.shibboleth.idp.attribute.resolver.spring.enc.impl.SAML1XMLObjectAttributeEncoderParser;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML1XMLObjectAttributeEncoder;
-import net.shibboleth.idp.saml.xml.SAMLConstants;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1XMLObjectAttributeTranscoder;
 
 /**
  * Test for {@link SAML1XMLObjectAttributeEncoderParser}.
  */
-public class SAML1XMLObjectAttributeEncoderParserTest extends BaseAttributeDefinitionParserTest {
+public class SAML1XMLObjectAttributeEncoderParserTest extends BaseEncoderDefinitionParserTest {
 
-    @Test public void resolver() {
-        final SAML1XMLObjectAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml1XmlObject.xml", SAML1XMLObjectAttributeEncoder.class);
+    protected void testWithProperties(final boolean activation, final Boolean encodeType) {
+        
+        final Map<String,Object> rule =
+                getAttributeTranscoderRule("resolver/saml1XmlObject.xml", activation, encodeType).getMap();
 
-        assertEquals(encoder.getName(), "SAML1_XMLObject_ATTRIBUTE_NAME");
-        assertEquals(encoder.getNamespace(),"SAML1_XMLObject_ATTRIBUTE_NAME_SPACE");
-    }
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML1XMLObjectAttributeTranscoder);
+        assertEquals(rule.get(SAMLAttributeTranscoder.PROP_NAME), "SAML1_XMLObject_ATTRIBUTE_NAME");
+        assertEquals(rule.get(SAML1AttributeTranscoder.PROP_NAMESPACE), "SAML1_XMLObject_ATTRIBUTE_NAME_SPACE");
+        assertEquals(activation, ((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, encodeType!=null ? encodeType : true);
+}
     
     @Test public void defaultCase() {
-        final SAML1XMLObjectAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml1XmlObjectDefault.xml", SAML1XMLObjectAttributeEncoder.class);
+        final Map<String,Object> rule = getAttributeTranscoderRule("resolver/saml1XmlObjectDefault.xml").getMap();
 
-        assertEquals(encoder.getName(), "XMLObject_ATTRIBUTE");
-        assertEquals(encoder.getNamespace(), SAMLConstants.SAML1_ATTR_NAMESPACE_URI);
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML1XMLObjectAttributeTranscoder);
+        assertEquals(rule.get(SAMLAttributeTranscoder.PROP_NAME), "XMLObject_ATTRIBUTE");
+        assertNull(rule.get(SAML1AttributeTranscoder.PROP_NAMESPACE));
+        assertFalse(((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, true);
     }
     
     @Test(expectedExceptions={BeanDefinitionStoreException.class,})  public void noName() {
-        getAttributeEncoder("resolver/saml1XmlObjectNoName.xml", SAML1XMLObjectAttributeEncoder.class);
+        getAttributeTranscoderRule("resolver/saml1XmlObjectNoName.xml");
     }
 }
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2Base64AttributeEncoderParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2Base64AttributeEncoderParserTest.java
index bef5195..17c6e54 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2Base64AttributeEncoderParserTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2Base64AttributeEncoderParserTest.java
@@ -18,40 +18,53 @@
 package net.shibboleth.idp.attribute.resolver.spring.enc;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Map;
+import java.util.function.Predicate;
 
-import org.opensaml.saml.saml2.core.Attribute;
 import org.springframework.beans.factory.BeanDefinitionStoreException;
 import org.testng.annotations.Test;
 
-import net.shibboleth.idp.attribute.resolver.spring.BaseAttributeDefinitionParserTest;
+import net.shibboleth.idp.attribute.resolver.spring.BaseEncoderDefinitionParserTest;
 import net.shibboleth.idp.attribute.resolver.spring.enc.impl.SAML2Base64AttributeEncoderParser;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML2ByteAttributeEncoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2ByteAttributeTranscoder;
 
 /**
  * Test for {@link SAML2Base64AttributeEncoderParser}.
  */
-public class SAML2Base64AttributeEncoderParserTest extends BaseAttributeDefinitionParserTest {
+public class SAML2Base64AttributeEncoderParserTest extends BaseEncoderDefinitionParserTest {
 
-    @Test public void resolver() {
-        final SAML2ByteAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml2Base64.xml", SAML2ByteAttributeEncoder.class);
+    protected void testWithProperties(final boolean activation, final Boolean encodeType) {
+        
+        final Map<String,Object> rule =
+                getAttributeTranscoderRule("resolver/saml2Base64.xml", activation, encodeType).getMap();
 
-        assertEquals(encoder.getName(), "Saml2Base64_ATTRIBUTE_NAME");
-        assertEquals(encoder.getFriendlyName(),"Saml2Base64_ATTRIBUTE_FRIENDLY_NAME"); 
-        assertEquals(encoder.getNameFormat(),"Saml2Base64_ATTRIBUTE_NAME_FORMAT");
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML2ByteAttributeTranscoder);
+        assertEquals(rule.get(SAMLAttributeTranscoder.PROP_NAME), "Saml2Base64_ATTRIBUTE_NAME");
+        assertEquals(rule.get(SAML2AttributeTranscoder.PROP_NAME_FORMAT), "Saml2Base64_ATTRIBUTE_NAME_FORMAT");
+        assertEquals(rule.get(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME), "Saml2Base64_ATTRIBUTE_FRIENDLY_NAME");
+        assertEquals(activation, ((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, true);
     }
     
     @Test public void defaultCase() {
-        final SAML2ByteAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml2Base64Default.xml", SAML2ByteAttributeEncoder.class);
+        final Map<String,Object> rule = getAttributeTranscoderRule("resolver/saml2Base64Default.xml").getMap();
 
-        assertEquals(encoder.getName(), "Base64Name");
-        assertNull(encoder.getFriendlyName()); 
-        assertEquals(encoder.getNameFormat(), Attribute.URI_REFERENCE);
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML2ByteAttributeTranscoder);
+        assertEquals(rule.get(SAMLAttributeTranscoder.PROP_NAME), "Base64Name");
+        assertNull(rule.get(SAML2AttributeTranscoder.PROP_NAME_FORMAT));
+        assertNull(rule.get(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME));
+        assertFalse(((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, true);
     }
     
     @Test(expectedExceptions={BeanDefinitionStoreException.class,})  public void noName() {
-        getAttributeEncoder("resolver/saml2Base64NoName.xml", SAML2ByteAttributeEncoder.class);
+        getAttributeTranscoderRule("resolver/saml2Base64NoName.xml");
     }
 }
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2ScopedStringAttributeEncoderParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2ScopedStringAttributeEncoderParserTest.java
index 0dcabfa..1741af2 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2ScopedStringAttributeEncoderParserTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2ScopedStringAttributeEncoderParserTest.java
@@ -18,46 +18,59 @@
 package net.shibboleth.idp.attribute.resolver.spring.enc;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Map;
+import java.util.function.Predicate;
 
-import org.opensaml.saml.saml2.core.Attribute;
 import org.springframework.beans.factory.BeanDefinitionStoreException;
 import org.testng.annotations.Test;
 
-import net.shibboleth.idp.attribute.resolver.spring.BaseAttributeDefinitionParserTest;
+import net.shibboleth.idp.attribute.resolver.spring.BaseEncoderDefinitionParserTest;
 import net.shibboleth.idp.attribute.resolver.spring.enc.impl.SAML2ScopedStringAttributeEncoderParser;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML2ScopedStringAttributeEncoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2ScopedStringAttributeTranscoder;
 
 /**
  * Test for {@link SAML2ScopedStringAttributeEncoderParser}.
  */
-public class SAML2ScopedStringAttributeEncoderParserTest extends BaseAttributeDefinitionParserTest {
-
-    @Test public void resolver() {
-        final SAML2ScopedStringAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml2Scoped.xml", SAML2ScopedStringAttributeEncoder.class);
-
-        assertEquals(encoder.getName(), "ATTRIBUTE_NAME");
-        assertEquals(encoder.getFriendlyName(),"ATTRIBUTE_FRIENDLY_NAME"); 
-        assertEquals(encoder.getNameFormat(),"ATTRIBUTE_NAME_FORMAT");
-        assertEquals(encoder.getScopeType(),"attribute");
-        assertEquals(encoder.getScopeAttributeName(),"scopeAttrib");
-        assertEquals(encoder.getScopeDelimiter(),"###");
+public class SAML2ScopedStringAttributeEncoderParserTest extends BaseEncoderDefinitionParserTest {
+
+    protected void testWithProperties(final boolean activation, final Boolean encodeType) {
+        
+        final Map<String,Object> rule =
+                getAttributeTranscoderRule("resolver/saml2Scoped.xml", activation, encodeType).getMap();
+
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML2ScopedStringAttributeTranscoder);
+        assertEquals(rule.get(SAMLAttributeTranscoder.PROP_NAME), "ATTRIBUTE_NAME");
+        assertEquals(rule.get(SAML2AttributeTranscoder.PROP_NAME_FORMAT), "ATTRIBUTE_NAME_FORMAT");
+        assertEquals(rule.get(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME), "ATTRIBUTE_FRIENDLY_NAME");
+        assertEquals(rule.get(SAML2ScopedStringAttributeTranscoder.PROP_SCOPE_TYPE), "attribute");
+        assertEquals(rule.get(SAML2ScopedStringAttributeTranscoder.PROP_SCOPE_ATTR_NAME), "scopeAttrib");
+        assertEquals(rule.get(SAML2ScopedStringAttributeTranscoder.PROP_SCOPE_DELIMITER), "###");
+        assertEquals(activation, ((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, encodeType!=null ? encodeType : false);
     }
     
     @Test public void defaultCase() {
-        final SAML2ScopedStringAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml2ScopedDefault.xml", SAML2ScopedStringAttributeEncoder.class);
-
-        assertEquals(encoder.getName(), "name");
-        assertNull(encoder.getFriendlyName()); 
-        assertEquals(encoder.getNameFormat(), Attribute.URI_REFERENCE);
-        assertEquals(encoder.getScopeType(),"inline");
-        assertEquals(encoder.getScopeDelimiter(),"@");
-        assertEquals(encoder.getScopeAttributeName(),"Scope");
+        final Map<String,Object> rule = getAttributeTranscoderRule("resolver/saml2ScopedDefault.xml").getMap();
+
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML2ScopedStringAttributeTranscoder);
+        assertEquals(rule.get(SAMLAttributeTranscoder.PROP_NAME), "name");
+        assertNull(rule.get(SAML2AttributeTranscoder.PROP_NAME_FORMAT));
+        assertNull(rule.get(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME));
+        assertNull(rule.get(SAML2ScopedStringAttributeTranscoder.PROP_SCOPE_TYPE));
+        assertNull(rule.get(SAML2ScopedStringAttributeTranscoder.PROP_SCOPE_ATTR_NAME));
+        assertNull(rule.get(SAML2ScopedStringAttributeTranscoder.PROP_SCOPE_DELIMITER));
+        assertFalse(((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, true);
     }
     
     @Test(expectedExceptions={BeanDefinitionStoreException.class,})  public void noName() {
-        getAttributeEncoder("resolver/saml2ScopedNoName.xml", SAML2ScopedStringAttributeEncoder.class);
+        getAttributeTranscoderRule("resolver/saml2ScopedNoName.xml");
     }
 }
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2StringAttributeEncoderParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2StringAttributeEncoderParserTest.java
index 0961d4e..67cb02c 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2StringAttributeEncoderParserTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2StringAttributeEncoderParserTest.java
@@ -20,72 +20,52 @@ package net.shibboleth.idp.attribute.resolver.spring.enc;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
 
-import org.opensaml.saml.saml2.core.Attribute;
+import java.util.Map;
+import java.util.function.Predicate;
+
 import org.springframework.beans.factory.BeanDefinitionStoreException;
-import org.springframework.context.support.GenericApplicationContext;
 import org.testng.annotations.Test;
 
-import com.google.common.base.Predicates;
-
-import net.shibboleth.idp.attribute.resolver.spring.BaseAttributeDefinitionParserTest;
+import net.shibboleth.idp.attribute.resolver.spring.BaseEncoderDefinitionParserTest;
 import net.shibboleth.idp.attribute.resolver.spring.enc.impl.SAML2StringAttributeEncoderParser;
-import net.shibboleth.idp.profile.logic.ScriptedPredicate;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML2StringAttributeEncoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2StringAttributeTranscoder;
 
 /**
  * Test for {@link SAML2StringAttributeEncoderParser}.
  */
-public class SAML2StringAttributeEncoderParserTest extends BaseAttributeDefinitionParserTest {
+public class SAML2StringAttributeEncoderParserTest extends BaseEncoderDefinitionParserTest {
   
-    @Test public void resolver() {
-        final SAML2StringAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml2String.xml", SAML2StringAttributeEncoder.class);
+    protected void testWithProperties(final boolean activation, final Boolean encodeType) {
+        
+        final Map<String,Object> rule =
+                getAttributeTranscoderRule("resolver/saml2String.xml", activation, encodeType).getMap();
 
-        assertEquals(encoder.getName(), "Saml2String_ATTRIBUTE_NAME");
-        assertEquals(encoder.getFriendlyName(),"Saml2String_ATTRIBUTE_FRIENDLY_NAME"); 
-        assertEquals(encoder.getNameFormat(),"Saml2String_ATTRIBUTE_NAME_FORMAT");
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML2StringAttributeTranscoder);
+        assertEquals(rule.get(SAMLAttributeTranscoder.PROP_NAME), "Saml2String_ATTRIBUTE_NAME");
+        assertEquals(rule.get(SAML2AttributeTranscoder.PROP_NAME_FORMAT), "Saml2String_ATTRIBUTE_NAME_FORMAT");
+        assertEquals(rule.get(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME), "Saml2String_ATTRIBUTE_FRIENDLY_NAME");
+        assertEquals(activation, ((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, encodeType!=null ? encodeType : false);
     }
     
     @Test public void defaultCase() {
-        final SAML2StringAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml2StringDefault.xml", SAML2StringAttributeEncoder.class);
+        final Map<String,Object> rule = getAttributeTranscoderRule("resolver/saml2StringDefault.xml").getMap();
 
-        assertSame(encoder.getActivationCondition(), Predicates.alwaysTrue());
-        assertTrue(encoder.getActivationCondition().test(null));
-        assertEquals(encoder.getName(), "Saml2StringName");
-        assertNull(encoder.getFriendlyName()); 
-        assertEquals(encoder.getNameFormat(), Attribute.URI_REFERENCE);
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML2StringAttributeTranscoder);
+        assertEquals(rule.get(SAMLAttributeTranscoder.PROP_NAME), "Saml2StringName");
+        assertNull(rule.get(SAML2AttributeTranscoder.PROP_NAME_FORMAT));
+        assertNull(rule.get(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME));
+        assertFalse(((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, true);
     }
     
     @Test(expectedExceptions={BeanDefinitionStoreException.class,})  public void noName() {
-        getAttributeEncoder("resolver/saml2StringNoName.xml", SAML2StringAttributeEncoder.class);
+        getAttributeTranscoderRule("resolver/saml2StringNoName.xml");
     }
     
-    @Test public void conditional() {
-        final GenericApplicationContext context = new GenericApplicationContext();
-        setTestContext(context);
-
-        loadFile(ENCODER_FILE_PATH + "predicates.xml", context);
-        
-        final SAML2StringAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml2StringConditional.xml", SAML2StringAttributeEncoder.class, context);
-
-        assertSame(encoder.getActivationCondition(), Predicates.alwaysFalse());
-        assertFalse(encoder.getActivationCondition().test(null));
-    }
-
-    @Test public void conditionalScript() {
-        final GenericApplicationContext context = new GenericApplicationContext();
-        setTestContext(context);
-        
-        final SAML2StringAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml2String.xml", SAML2StringAttributeEncoder.class, context);
-
-        assertTrue(encoder.getActivationCondition() instanceof ScriptedPredicate);
-        assertFalse(encoder.getActivationCondition().test(null));
-    }
-
-}
+}
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2XMLObjectAttributeEncoderParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2XMLObjectAttributeEncoderParserTest.java
index a74e957..513cc6a 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2XMLObjectAttributeEncoderParserTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/enc/SAML2XMLObjectAttributeEncoderParserTest.java
@@ -18,40 +18,54 @@
 package net.shibboleth.idp.attribute.resolver.spring.enc;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Map;
+import java.util.function.Predicate;
 
-import org.opensaml.saml.saml2.core.Attribute;
 import org.springframework.beans.factory.BeanDefinitionStoreException;
 import org.testng.annotations.Test;
 
-import net.shibboleth.idp.attribute.resolver.spring.BaseAttributeDefinitionParserTest;
+import net.shibboleth.idp.attribute.resolver.spring.BaseEncoderDefinitionParserTest;
 import net.shibboleth.idp.attribute.resolver.spring.enc.impl.SAML2XMLObjectAttributeEncoderParser;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML2XMLObjectAttributeEncoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2XMLObjectAttributeTranscoder;
 
 /**
  * Test for {@link SAML2XMLObjectAttributeEncoderParser}.
  */
-public class SAML2XMLObjectAttributeEncoderParserTest extends BaseAttributeDefinitionParserTest {
+public class SAML2XMLObjectAttributeEncoderParserTest extends BaseEncoderDefinitionParserTest {
 
-    @Test public void resolver() {
-        final SAML2XMLObjectAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml2XmlObject.xml", SAML2XMLObjectAttributeEncoder.class);
+    protected void testWithProperties(final boolean activation, final Boolean encodeType) {
+        
+        final Map<String,Object> rule =
+                getAttributeTranscoderRule("resolver/saml2XmlObject.xml", activation, encodeType).getMap();
 
-        assertEquals(encoder.getName(), "Saml2XmlObject_ATTRIBUTE_NAME");
-        assertEquals(encoder.getFriendlyName(),"Saml2XmlObject_ATTRIBUTE_FRIENDLY_NAME"); 
-        assertEquals(encoder.getNameFormat(),"Saml2XmlObject_ATTRIBUTE_NAME_FORMAT");
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML2XMLObjectAttributeTranscoder);
+        assertEquals(rule.get(SAMLAttributeTranscoder.PROP_NAME), "Saml2XmlObject_ATTRIBUTE_NAME");
+        assertEquals(rule.get(SAML2AttributeTranscoder.PROP_NAME_FORMAT), "Saml2XmlObject_ATTRIBUTE_NAME_FORMAT");
+        assertEquals(rule.get(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME), "Saml2XmlObject_ATTRIBUTE_FRIENDLY_NAME");
+        assertEquals(activation, ((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, encodeType!=null ? encodeType : true);
     }
 
     @Test public void defaultCase() {
-        final SAML2XMLObjectAttributeEncoder encoder =
-                getAttributeEncoder("resolver/saml2XmlObjectDefault.xml", SAML2XMLObjectAttributeEncoder.class);
+        final Map<String,Object> rule = getAttributeTranscoderRule("resolver/saml2XmlObjectDefault.xml").getMap();
 
-        assertEquals(encoder.getName(), "XmlObjectName");
-        assertNull(encoder.getFriendlyName()); 
-        assertEquals(encoder.getNameFormat(), Attribute.URI_REFERENCE);
+        assertTrue(rule.get(AttributeTranscoderRegistry.PROP_TRANSCODER) instanceof SAML2XMLObjectAttributeTranscoder);
+        assertEquals(rule.get(SAMLAttributeTranscoder.PROP_NAME), "XmlObjectName");
+        assertNull(rule.get(SAML2AttributeTranscoder.PROP_NAME_FORMAT));
+        assertNull(rule.get(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME));
+        assertFalse(((Predicate) rule.get(AttributeTranscoderRegistry.PROP_CONDITION)).test(null));
+        checkEncodeType(rule, true);
     }
     
     @Test(expectedExceptions={BeanDefinitionStoreException.class,})  public void noName() {
-        getAttributeEncoder("resolver/saml2XmlObjectNoName.xml", SAML2XMLObjectAttributeEncoder.class);
+        getAttributeTranscoderRule("resolver/saml2XmlObjectNoName.xml");
     }
-}
+
+}
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/logback-test.xml b/idp-attribute-resolver-spring/src/test/resources/logback-test.xml
index bc9eaee..5c5330f 100644
--- a/idp-attribute-resolver-spring/src/test/resources/logback-test.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/logback-test.xml
@@ -2,8 +2,11 @@
 
 <configuration>
     <logger name="net.shibboleth.idp.attribute.resolver.spring" level="DEBUG"/>
+    <logger name="net.shibboleth.idp.attribute.transcoding.spring" level="DEBUG"/>
     <logger name="net.shibboleth.idp.attribute.resolver" level="DEBUG"/>
+    <logger name="net.shibboleth.idp.attribute.transcoding" level="DEBUG"/>
     <logger name="net.shibboleth.idp.saml.attribute.resolver" level="DEBUG"/>
+    <logger name="net.shibboleth.idp.saml.attribute.transcoding" level="DEBUG"/>
     <logger name="net.shibboleth.ext.spring" level="DEBUG"/>
     <logger name="net.shibboleth.ext.spring.util" level="DEBUG"/>
     <logger name="org.springframework" level="WARN"/>
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/filter/service.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/filter/service.xml
index 0a82971..957ac48 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/filter/service.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/filter/service.xml
@@ -5,37 +5,47 @@
 	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                             http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
 	default-init-method="initialize" default-destroy-method="destroy">
-	<bean id="shibboleth.VelocityEngine"
-		class="net.shibboleth.ext.spring.velocity.VelocityEngineFactoryBean">
-		<property name="velocityProperties">
-			<props>
-				<prop key="resource.loader">classpath, string</prop>
-				<prop key="classpath.resource.loader.class">
-					org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
-				</prop>
-				<prop key="string.resource.loader.class">
-					org.apache.velocity.runtime.resource.loader.StringResourceLoader
-				</prop>
-			</props>
-		</property>
-	</bean>
-	<bean id="shibboleth.AttributeResolverService" class="net.shibboleth.ext.spring.service.ReloadableSpringService"
+
+	<bean id="shibboleth.AttributeRegistryService" class="net.shibboleth.ext.spring.service.ReloadableSpringService"
 		depends-on="shibboleth.VelocityEngine" p:failFast="true"
 		p:reloadCheckDelay="0">
 
 		<constructor-arg name="claz"
-			value="net.shibboleth.idp.attribute.resolver.AttributeResolver" />
+			value="net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry" />
 		<constructor-arg name="strategy">
-			<bean
-				class="net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverServiceStrategy"
-				p:id="ShibbolethAttributeResolver" />
+			<bean class="net.shibboleth.idp.attribute.transcoding.spring.impl.AttributeRegistryServiceStrategy"
+				p:id="shibboleth.AttributeRegistryService"
+				p:namingRegistry="DefaultNamingRegistry" />
 		</constructor-arg>
 		<property name="serviceConfigurations">
 			<util:list>
-				<value>/net/shibboleth/idp/attribute/resolver/filter/resolver.xml
-				</value>
+				<value>/net/shibboleth/idp/attribute/resolver/filter/resolver.xml</value>
 			</util:list>
 		</property>
 	</bean>
 
+    <util:map id="DefaultNamingRegistry">
+        <entry key="#{T(org.opensaml.saml.saml2.core.Attribute)}">
+            <bean class="net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder.NamingFunction" />
+        </entry>
+        <entry key="#{T(org.opensaml.saml.saml1.core.AttributeDesignator)}">
+            <bean class="net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML1AttributeTranscoder.NamingFunction" />
+        </entry>
+    </util:map>
+
+    <bean id="shibboleth.VelocityEngine"
+        class="net.shibboleth.ext.spring.velocity.VelocityEngineFactoryBean">
+        <property name="velocityProperties">
+            <props>
+                <prop key="resource.loader">classpath, string</prop>
+                <prop key="classpath.resource.loader.class">
+                    org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
+                </prop>
+                <prop key="string.resource.loader.class">
+                    org.apache.velocity.runtime.resource.loader.StringResourceLoader
+                </prop>
+            </props>
+        </property>
+    </bean>
+
 </beans>
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/customBean.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/customBean.xml
index ef730da..19a1bb3 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/customBean.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/customBean.xml
@@ -10,17 +10,41 @@
                            
        default-init-method="initialize"
        default-destroy-method="destroy">
+       
     <util:map id="shibboleth.CustomScriptObject">
         <entry key="foo" value="bar"/>
-    </util:map>    
+    </util:map>
+        
     <util:map id="other.CustomScriptObject">
         <entry key="bar" value="foo"/>
-    </util:map>    
+    </util:map>
+    
+    <bean id="shibboleth.Predicate" class="com.google.common.base.Predicates" factory-method="alwaysFalse"/>
     
     <bean id="shibboleth.PropertySourcesPlaceholderConfigurer"
         class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
         p:placeholderPrefix="%{" p:placeholderSuffix="}" />
 
+    <!-- Necessary for encoder parsing to function, normally part of registry wiring. -->
+    
+    <bean id="SAML2StringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2StringAttributeTranscoder" />
+    <bean id="SAML2ScopedStringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2ScopedStringAttributeTranscoder" />
+    <bean id="SAML2ByteTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2ByteAttributeTranscoder" />
+    <bean id="SAML2XMLObjectTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2XMLObjectAttributeTranscoder" />
+        
+    <bean id="SAML1StringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1StringAttributeTranscoder" />
+    <bean id="SAML1ScopedStringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1ScopedStringAttributeTranscoder" />
+    <bean id="SAML1ByteTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1ByteAttributeTranscoder" />
+    <bean id="SAML1XMLObjectTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1XMLObjectAttributeTranscoder" />
+
 </beans>
 
        
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/predicates.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/predicates.xml
deleted file mode 100644
index 8b4b613..0000000
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/predicates.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-	xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
-	xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
-                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
-                        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
-
-    <bean id="predicate" class="com.google.common.base.Predicates" factory-method="alwaysFalse"/>
-</beans>
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/idp-571.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/idp-571.xml
index 81ca522..304812b 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/idp-571.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/idp-571.xml
@@ -8,13 +8,10 @@
     <AttributeEncoder xsi:type="SAML2String"  name="skillsoftdept" friendlyName="dept name for skillsoft" nameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" />
 
     <Script><![CDATA[
-
        importPackage(Packages.edu.internet2.middleware.shibboleth.common.attribute.provider);
 
         // Create attribute to be returned from definition
         skillsoftdept = new BasicAttribute("skillsoftdept");
-
-
     ]]></Script>
     
   </AttributeDefinition>
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1Base64.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1Base64.xml
index d41c048..389ee75 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1Base64.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1Base64.xml
@@ -4,6 +4,11 @@
 	xmlns="urn:mace:shibboleth:2.0:resolver"
 	name="SAML1_BASE64_ATTRIBUTE_NAME" 
 	namespace="SAML1_BASE64_ATTRIBUTE_NAME_SPACE"
-	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
-/> 
- 
\ No newline at end of file
+	encodeType="%{the.encodeType.property:false}"
+	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">
+
+    <ActivationConditionScript>
+        <Script>%{the.activation.property}</Script>
+    </ActivationConditionScript>
+
+</AttributeEncoder>
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1Base64Default.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1Base64Default.xml
index 972e98b..20b8a07 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1Base64Default.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1Base64Default.xml
@@ -1,4 +1,6 @@
 <AttributeEncoder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mace:shibboleth:2.0:resolver"
 	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
-    xsi:type="SAML1Base64" name="Base64_ATTRIBUTE"/> 
+    xsi:type="SAML1Base64"
+    name="Base64_ATTRIBUTE"
+    activationConditionRef="shibboleth.Predicate"/> 
  
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1Scoped.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1Scoped.xml
index 0e73efd..43beb17 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1Scoped.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1Scoped.xml
@@ -7,5 +7,12 @@
 	scopeType="attribute"
 	scopeAttribute="saml1ScopeAttrib"
 	scopeDelimiter="#@#"
-	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"/> 
+    encodeType="%{the.encodeType.property:false}"
+	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">
+
+    <ActivationConditionScript>
+        <Script>%{the.activation.property}</Script>
+    </ActivationConditionScript>
+	
+</AttributeEncoder>
  
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1ScopedDefault.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1ScopedDefault.xml
index 39f4943..13366a5 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1ScopedDefault.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1ScopedDefault.xml
@@ -1,5 +1,6 @@
 <AttributeEncoder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mace:shibboleth:2.0:resolver"
 	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
     xsi:type="SAML1ScopedString"
-	name="saml1_scoped_name" /> 
+	name="saml1_scoped_name"
+	activationConditionRef="shibboleth.Predicate"/> 
  
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1String.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1String.xml
index c78ae5b..2f07950 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1String.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1String.xml
@@ -4,6 +4,11 @@
 	xmlns="urn:mace:shibboleth:2.0:resolver"
 	name="SAML1_STRING_ATTRIBUTE_NAME" 
 	namespace="SAML1_STRING_ATTRIBUTE_NAME_SPACE"
-	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
-/> 
- 
\ No newline at end of file
+	encodeType="%{the.empty.property}"
+	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">
+
+    <ActivationConditionScript>
+        <Script>%{the.activation.property}</Script>
+    </ActivationConditionScript>
+
+</AttributeEncoder>
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1StringDefault.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1StringDefault.xml
index dfae337..d351751 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1StringDefault.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1StringDefault.xml
@@ -1,5 +1,6 @@
 <AttributeEncoder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mace:shibboleth:2.0:resolver"
 	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
     xsi:type="SAML1String"
-	name="ATTRIBUTE"/> 
+	name="ATTRIBUTE"
+	activationConditionRef="shibboleth.Predicate"/>
  
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1XmlObject.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1XmlObject.xml
index baa15a5..8ad6608 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1XmlObject.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1XmlObject.xml
@@ -4,6 +4,11 @@
 	xmlns="urn:mace:shibboleth:2.0:resolver"
 	name="SAML1_XMLObject_ATTRIBUTE_NAME" 
 	namespace="SAML1_XMLObject_ATTRIBUTE_NAME_SPACE"
-	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
-/> 
- 
\ No newline at end of file
+	encodeType="%{the.encodeType.property:true}"
+	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">
+
+    <ActivationConditionScript>
+        <Script>%{the.activation.property}</Script>
+    </ActivationConditionScript>
+	
+</AttributeEncoder>
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1XmlObjectDefault.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1XmlObjectDefault.xml
index 7da2157..ded115f 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1XmlObjectDefault.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml1XmlObjectDefault.xml
@@ -3,5 +3,6 @@
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 	xmlns="urn:mace:shibboleth:2.0:resolver"
 	name="XMLObject_ATTRIBUTE"
+	activationConditionRef="shibboleth.Predicate"
 	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"/> 
  
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2Base64.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2Base64.xml
index 3681d25..bf07b56 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2Base64.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2Base64.xml
@@ -5,5 +5,11 @@
 	name="Saml2Base64_ATTRIBUTE_NAME" 
 	nameFormat="Saml2Base64_ATTRIBUTE_NAME_FORMAT"
 	friendlyName="Saml2Base64_ATTRIBUTE_FRIENDLY_NAME"
-	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
-/> 
\ No newline at end of file
+	encodeType="%{the.empty.property}"
+	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">
+
+    <ActivationConditionScript>
+        <Script>%{the.activation.property}</Script>
+    </ActivationConditionScript>
+	
+</AttributeEncoder>
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2Base64Default.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2Base64Default.xml
index 430b049..a0d61fc 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2Base64Default.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2Base64Default.xml
@@ -3,5 +3,6 @@
     xsi:type="SAML2Base64"
  	xmlns="urn:mace:shibboleth:2.0:resolver"
 	name="Base64Name"
+	activationConditionRef="shibboleth.Predicate"
 	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"/> 
  
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2Scoped.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2Scoped.xml
index 4b01de5..8b9c3ae 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2Scoped.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2Scoped.xml
@@ -8,5 +8,11 @@
 	scopeType="attribute"
 	scopeAttribute="scopeAttrib"
 	scopeDelimiter="###"
-	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd" /> 
- 
\ No newline at end of file
+	encodeType="%{the.encodeType.property:false}"
+	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">
+
+    <ActivationConditionScript>
+        <Script>%{the.activation.property}</Script>
+    </ActivationConditionScript>
+	
+</AttributeEncoder>
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2ScopedDefault.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2ScopedDefault.xml
index 7f0778b..a44248a 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2ScopedDefault.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2ScopedDefault.xml
@@ -3,5 +3,6 @@
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 	xmlns="urn:mace:shibboleth:2.0:resolver"
 	name="name"
+	activationConditionRef="shibboleth.Predicate"
 	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"/> 
  
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2ScopedNoName.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2ScopedNoName.xml
index d2a50c0..c6222ea 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2ScopedNoName.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2ScopedNoName.xml
@@ -1,13 +1,8 @@
-<resolver:AttributeEncoder 
+<AttributeEncoder 
     xsi:type="enc:SAML2ScopedString"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
-	xmlns:enc="urn:mace:shibboleth:2.0:attribute:encoder" 
-	xmlns:resolver="urn:mace:shibboleth:2.0:resolver"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:enc="urn:mace:shibboleth:2.0:attribute:encoder" 
+	xmlns="urn:mace:shibboleth:2.0:resolver"
 	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd
-                        urn:mace:shibboleth:2.0:resolver:pc http://shibboleth.net/schema/idp/shibboleth-attribute-resolver-pc.xsd
-                        urn:mace:shibboleth:2.0:resolver:ad http://shibboleth.net/schema/idp/shibboleth-attribute-resolver-ad.xsd
-                        urn:mace:shibboleth:2.0:resolver:dc http://shibboleth.net/schema/idp/shibboleth-attribute-resolver-dc.xsd
-                        urn:mace:shibboleth:2.0:attribute:encoder http://shibboleth.net/schema/idp/shibboleth-attribute-encoder.xsd
-                        urn:mace:shibboleth:2.0:security http://shibboleth.net/schema/idp/shibboleth-security.xsd"
+                        urn:mace:shibboleth:2.0:attribute:encoder http://shibboleth.net/schema/idp/shibboleth-attribute-encoder.xsd"
  /> 
  
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2String.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2String.xml
index 1300dd2..97ab520 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2String.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2String.xml
@@ -4,10 +4,12 @@
 	xmlns="urn:mace:shibboleth:2.0:resolver"
 	name="Saml2String_ATTRIBUTE_NAME" 
 	nameFormat="Saml2String_ATTRIBUTE_NAME_FORMAT"
-	friendlyName="Saml2String_ATTRIBUTE_FRIENDLY_NAME" 
+	friendlyName="Saml2String_ATTRIBUTE_FRIENDLY_NAME"
+	encodeType="%{the.encodeType.property:false}"
 	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">
 
     <ActivationConditionScript>
-        <Script>false</Script>
-    </ActivationConditionScript>	
+        <Script>%{the.activation.property}</Script>
+    </ActivationConditionScript>
+    
 </AttributeEncoder>
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2StringConditional.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2StringConditional.xml
index 741d16b..63c1736 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2StringConditional.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2StringConditional.xml
@@ -3,6 +3,6 @@
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 	xmlns="urn:mace:shibboleth:2.0:resolver"
 	name="Saml2StringConditional"
-	activationConditionRef="predicate"
+	activationConditionRef="shibboleth.Predicate"
 	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd" /> 
  
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2StringDefault.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2StringDefault.xml
index ef45b88..1f04ab4 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2StringDefault.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2StringDefault.xml
@@ -3,5 +3,6 @@
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 	xmlns="urn:mace:shibboleth:2.0:resolver"
 	name="Saml2StringName"
+	activationConditionRef="shibboleth.Predicate"
 	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd" /> 
  
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2XmlObject.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2XmlObject.xml
index 0ef9d0e..97d74f0 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2XmlObject.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2XmlObject.xml
@@ -4,5 +4,12 @@
 	xmlns="urn:mace:shibboleth:2.0:resolver"
 	name="Saml2XmlObject_ATTRIBUTE_NAME" 
 	nameFormat="Saml2XmlObject_ATTRIBUTE_NAME_FORMAT"
-	friendlyName="Saml2XmlObject_ATTRIBUTE_FRIENDLY_NAME" 
-	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"/> 
\ No newline at end of file
+	friendlyName="Saml2XmlObject_ATTRIBUTE_FRIENDLY_NAME"
+	encodeType="%{the.encodeType.property:true}"
+	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">
+
+    <ActivationConditionScript>
+        <Script>%{the.activation.property}</Script>
+    </ActivationConditionScript>
+	
+</AttributeEncoder>
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2XmlObjectDefault.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2XmlObjectDefault.xml
index f77cbf9..2339c03 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2XmlObjectDefault.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/enc/resolver/saml2XmlObjectDefault.xml
@@ -3,5 +3,6 @@
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 	xmlns="urn:mace:shibboleth:2.0:resolver"
 	name="XmlObjectName"
+	activationConditionRef="shibboleth.Predicate"
 	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"/> 
  
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/mapperTest-attributes.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/mapperTest-attributes.xml
index 76b690b..6dacf39 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/mapperTest-attributes.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/mapperTest-attributes.xml
@@ -2,47 +2,45 @@
 <AttributeResolver xmlns="urn:mace:shibboleth:2.0:resolver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                             xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">
 
-    <!-- ========================================== -->
-    <!--      Attribute Definitions                 -->
-    <!-- ========================================== -->
-
-    <DataConnector id="staticAttributes" xsi:type="Static">
-        <Attribute id="assurance">
-            <Value>tin</Value>
-            <Value>pewter</Value>
-        </Attribute>
-        <Attribute id="oAssurance">
-            <Value>bakelite</Value>
-            <Value>dross</Value>
-        </Attribute>
-        <Attribute id="eduPersonEntitlement">
-            <Value>urn:example.org:entitlement:entitlement1</Value>
-            <Value>urn:mace:dir:entitlement:common-lib-terms</Value>
-        </Attribute>
+    <!--
+    Unusable connector to ensure it's being lazily eval'd.
+    -->
+    <DataConnector id="myLDAP" xsi:type="LDAPDirectory"
+        ldapURL="ldaps://localhost:6666" 
+        baseDN="dc=shibboleth,dc=net"
+        connectTimeout="PT2S"
+        responseTimeout="PT2S"
+        principal="cn=Directory Manager"
+        principalCredential="password">
+        <FilterTemplate>
+            <![CDATA[
+                (uid=$resolutionContext.principal)
+            ]]>
+        </FilterTemplate>
     </DataConnector>
 
     <AttributeDefinition xsi:type="Simple" id="eduPersonAssurance">
-        <InputDataConnector allAttributes="true" ref="staticAttributes" />
+        <InputDataConnector allAttributes="true" ref="myLDAP" />
         <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:eduPersonAssurance" />
         <AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" friendlyName="feduPersonAssurance" />
         <AttributeEncoder xsi:type="SAML2String" nameFormat="http://example.org/Format" name="http://example.org/name/for/Attribute" friendlyName="fotherSAMLName" />
     </AttributeDefinition>
 
     <AttributeDefinition xsi:type="Simple" id="otherPersonAssurance" >
-        <InputDataConnector allAttributes="true"  ref="staticAttributes" />
+        <InputDataConnector allAttributes="true"  ref="myLDAP" />
         <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:eduPersonAssurance" />
         <AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" friendlyName="feduPersonAssurance" />
     </AttributeDefinition>
         
     <AttributeDefinition xsi:type="Simple" id="otherFormatPersonAssurance" >
-        <InputDataConnector allAttributes="true"  ref="staticAttributes" />
+        <InputDataConnector allAttributes="true"  ref="myLDAP" />
         <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:eduPersonAssurance" />
         <AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" nameFormat="http://example.org/Format" friendlyName="fOeduPersonAssurance" />
     </AttributeDefinition>
 
     <AttributeDefinition xsi:type="Scoped" id="eduPersonScopedAffiliation" scope="example.org" 
                                   >
-        <InputDataConnector allAttributes="true"  ref="staticAttributes" />
+        <InputDataConnector allAttributes="true"  ref="myLDAP" />
         <AttributeEncoder xsi:type="SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonScopedAffiliation" />
         <AttributeEncoder xsi:type="SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" friendlyName="feduPersonScopedAffiliation" 
                scopeDelimiter='#'/>
@@ -50,7 +48,7 @@
     
     <AttributeDefinition xsi:type="SAML2NameID" id="eduPersonTID" 
                                   nameIdFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" >
-        <InputDataConnector allAttributes="true"  ref="staticAttributes" />
+        <InputDataConnector allAttributes="true"  ref="myLDAP" />
         <AttributeEncoder xsi:type="SAML1XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" />
         <AttributeEncoder xsi:type="SAML2XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" friendlyName="feduPersonTargetedID" />
     </AttributeDefinition>
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/mapperTest.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/mapperTest.xml
index bf29c12..b86d5a7 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/mapperTest.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/mapperTest.xml
@@ -8,6 +8,9 @@
                             http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
         default-init-method="initialize"
         default-destroy-method="destroy">
+        
+    <import resource="customBean.xml" />
+    
     <bean id="shibboleth.VelocityEngine" class="net.shibboleth.ext.spring.velocity.VelocityEngineFactoryBean">
         <property name="velocityProperties">
             <props>
@@ -21,16 +24,17 @@
             </props>
         </property>
     </bean>
-    <bean id="shibboleth.AttributeResolverService" class="net.shibboleth.ext.spring.service.ReloadableSpringService"
-        depends-on="shibboleth.VelocityEngine" p:failFast="false"
+
+    <bean id="shibboleth.AttributeRegistryService" class="net.shibboleth.ext.spring.service.ReloadableSpringService"
+        depends-on="shibboleth.VelocityEngine" p:failFast="true"
         p:reloadCheckDelay="0">
 
         <constructor-arg name="claz"
-            value="net.shibboleth.idp.attribute.resolver.AttributeResolver" />
+            value="net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry" />
         <constructor-arg name="strategy">
-            <bean
-                class="net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverServiceStrategy"
-                p:id="TestID" />
+            <bean class="net.shibboleth.idp.attribute.transcoding.spring.impl.AttributeRegistryServiceStrategy"
+                p:id="shibboleth.AttributeRegistryService"
+                p:namingRegistry="DefaultNamingRegistry" />
         </constructor-arg>
         <property name="serviceConfigurations">
             <util:list>
@@ -38,4 +42,14 @@
             </util:list>
         </property>
     </bean>
+
+    <util:map id="DefaultNamingRegistry">
+        <entry key="#{T(org.opensaml.saml.saml2.core.Attribute)}">
+            <bean class="net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder.NamingFunction" />
+        </entry>
+        <entry key="#{T(org.opensaml.saml.saml1.core.AttributeDesignator)}">
+            <bean class="net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML1AttributeTranscoder.NamingFunction" />
+        </entry>
+    </util:map>
+
 </beans>
\ No newline at end of file
diff --git a/idp-authn-api/.project b/idp-authn-api/.project
index eac45e1..8b15bc1 100644
--- a/idp-authn-api/.project
+++ b/idp-authn-api/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-authn-impl/.project b/idp-authn-impl/.project
index 93f0107..14170b7 100644
--- a/idp-authn-impl/.project
+++ b/idp-authn-impl/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-bom/.project b/idp-bom/.project
index f286c93..937f4e2 100644
--- a/idp-bom/.project
+++ b/idp-bom/.project
@@ -6,11 +6,6 @@
 	</projects>
 	<buildSpec>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-cas-api/.project b/idp-cas-api/.project
index 23cfd9c..7db0de0 100644
--- a/idp-cas-api/.project
+++ b/idp-cas-api/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-cas-impl/.project b/idp-cas-impl/.project
index 22f763b..2adfe34 100644
--- a/idp-cas-impl/.project
+++ b/idp-cas-impl/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-conf/.project b/idp-conf/.project
index 1f94f11..4c8f229 100644
--- a/idp-conf/.project
+++ b/idp-conf/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-conf/src/main/resources/attributes/saml1/eduPersonPrincipalName.txt b/idp-conf/src/main/resources/attributes/saml1/eduPersonPrincipalName.txt
new file mode 100644
index 0000000..008831d
--- /dev/null
+++ b/idp-conf/src/main/resources/attributes/saml1/eduPersonPrincipalName.txt
@@ -0,0 +1,4 @@
+id = eduPersonPrincipalName
+transcoder_bean = SAML1ScopedStringTranscoder
+name = urn:mace:dir:attribute-def:eduPersonPrincipalName
+encodeType = false
diff --git a/idp-conf/src/main/resources/attributes/saml1/eduPersonScopedAffiliation.txt b/idp-conf/src/main/resources/attributes/saml1/eduPersonScopedAffiliation.txt
new file mode 100644
index 0000000..e6c505b
--- /dev/null
+++ b/idp-conf/src/main/resources/attributes/saml1/eduPersonScopedAffiliation.txt
@@ -0,0 +1,4 @@
+id = eduPersonScopedAffiliation
+transcoder_bean = SAML1ScopedStringTranscoder
+name = urn:mace:dir:attribute-def:eduPersonScopedAffiliation
+encodeType = false
diff --git a/idp-conf/src/main/resources/attributes/saml1/mail.txt b/idp-conf/src/main/resources/attributes/saml1/mail.txt
new file mode 100644
index 0000000..22f4da9
--- /dev/null
+++ b/idp-conf/src/main/resources/attributes/saml1/mail.txt
@@ -0,0 +1,4 @@
+id = mail
+transcoder_bean = SAML1StringTranscoder
+name = urn:mace:dir:attribute-def:mail
+encodeType = false
diff --git a/idp-conf/src/main/resources/attributes/saml1/uid.txt b/idp-conf/src/main/resources/attributes/saml1/uid.txt
new file mode 100644
index 0000000..209d6a0
--- /dev/null
+++ b/idp-conf/src/main/resources/attributes/saml1/uid.txt
@@ -0,0 +1,4 @@
+id = uid
+transcoder_bean = SAML1StringTranscoder
+name = urn:mace:dir:attribute-def:uid
+encodeType = false
diff --git a/idp-conf/src/main/resources/attributes/saml2/eduPersonPrincipalName.txt b/idp-conf/src/main/resources/attributes/saml2/eduPersonPrincipalName.txt
new file mode 100644
index 0000000..970f0da
--- /dev/null
+++ b/idp-conf/src/main/resources/attributes/saml2/eduPersonPrincipalName.txt
@@ -0,0 +1,4 @@
+id = eduPersonPrincipalName
+transcoder_bean = SAML2ScopedStringTranscoder
+name = urn:oid:1.3.6.1.4.1.5923.1.1.1.6
+encodeType = false
diff --git a/idp-conf/src/main/resources/attributes/saml2/eduPersonScopedAffiliation.txt b/idp-conf/src/main/resources/attributes/saml2/eduPersonScopedAffiliation.txt
new file mode 100644
index 0000000..aedfcb5
--- /dev/null
+++ b/idp-conf/src/main/resources/attributes/saml2/eduPersonScopedAffiliation.txt
@@ -0,0 +1,4 @@
+id = eduPersonScopedAffiliation
+transcoder_bean = SAML2ScopedStringTranscoder
+name = urn:oid:1.3.6.1.4.1.5923.1.1.1.9
+encodeType = false
diff --git a/idp-conf/src/main/resources/attributes/saml2/mail.txt b/idp-conf/src/main/resources/attributes/saml2/mail.txt
new file mode 100644
index 0000000..8e28190
--- /dev/null
+++ b/idp-conf/src/main/resources/attributes/saml2/mail.txt
@@ -0,0 +1,4 @@
+id = mail
+transcoder_bean = SAML2StringTranscoder
+name = urn:oid:0.9.2342.19200300.100.1.3
+encodeType = false
diff --git a/idp-conf/src/main/resources/attributes/saml2/uid.txt b/idp-conf/src/main/resources/attributes/saml2/uid.txt
new file mode 100644
index 0000000..0f259a9
--- /dev/null
+++ b/idp-conf/src/main/resources/attributes/saml2/uid.txt
@@ -0,0 +1,4 @@
+id = uid
+transcoder_bean = SAML2StringTranscoder
+name = urn:oid:0.9.2342.19200300.100.1.1
+encodeType = false
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/customBean.xml b/idp-conf/src/main/resources/conf/attribute-registry.xml
similarity index 67%
copy from idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/customBean.xml
copy to idp-conf/src/main/resources/conf/attribute-registry.xml
index ef730da..da86845 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/customBean.xml
+++ b/idp-conf/src/main/resources/conf/attribute-registry.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:util="http://www.springframework.org/schema/util"
@@ -10,17 +11,8 @@
                            
        default-init-method="initialize"
        default-destroy-method="destroy">
-    <util:map id="shibboleth.CustomScriptObject">
-        <entry key="foo" value="bar"/>
-    </util:map>    
-    <util:map id="other.CustomScriptObject">
-        <entry key="bar" value="foo"/>
-    </util:map>    
-    
-    <bean id="shibboleth.PropertySourcesPlaceholderConfigurer"
-        class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
-        p:placeholderPrefix="%{" p:placeholderSuffix="}" />
 
+    <!-- Default location for mappings. -->
+    <bean parent="shibboleth.TranscodingRuleLoader" c:_0="%{idp.home}/attributes" />
+    
 </beans>
-
-       
\ No newline at end of file
diff --git a/idp-conf/src/main/resources/conf/attribute-resolver.xml b/idp-conf/src/main/resources/conf/attribute-resolver.xml
index 471bf0b..d6b479e 100644
--- a/idp-conf/src/main/resources/conf/attribute-resolver.xml
+++ b/idp-conf/src/main/resources/conf/attribute-resolver.xml
@@ -32,8 +32,6 @@
     -->
     <AttributeDefinition id="eduPersonPrincipalName" xsi:type="Scoped" scope="%{idp.scope}">
         <InputAttributeDefinition ref="uid" />
-        <AttributeEncoder xsi:type="SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonPrincipalName" encodeType="false" />
-        <AttributeEncoder xsi:type="SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" friendlyName="eduPersonPrincipalName" encodeType="false" />
     </AttributeDefinition>
 
     <!--
@@ -41,10 +39,7 @@
     representing a local username, but you should generally *never*
     expose uid to federated services, as it is rarely globally unique.
     -->
-    <AttributeDefinition id="uid" xsi:type="PrincipalName">
-        <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:uid" encodeType="false" />
-        <AttributeEncoder xsi:type="SAML2String" name="urn:oid:0.9.2342.19200300.100.1.1" friendlyName="uid" encodeType="false" />
-    </AttributeDefinition>
+    <AttributeDefinition id="uid" xsi:type="PrincipalName" />
 
     <!--
     In the rest of the world, the email address is the standard identifier,
@@ -53,8 +48,6 @@
     -->
     <AttributeDefinition id="mail" xsi:type="Template">
         <InputAttributeDefinition ref="uid" />
-        <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:mail" encodeType="false" />
-        <AttributeEncoder xsi:type="SAML2String" name="urn:oid:0.9.2342.19200300.100.1.3" friendlyName="mail" encodeType="false" />
         <Template>
           <![CDATA[
                ${uid}@example.org
@@ -68,8 +61,6 @@
     -->
     <AttributeDefinition id="eduPersonScopedAffiliation" xsi:type="Scoped" scope="%{idp.scope}">
         <InputDataConnector ref="staticAttributes" attributeNames="affiliation" />
-        <AttributeEncoder xsi:type="SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonScopedAffiliation" encodeType="false" />
-        <AttributeEncoder xsi:type="SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" friendlyName="eduPersonScopedAffiliation" encodeType="false" />
     </AttributeDefinition>
 
 
diff --git a/idp-conf/src/main/resources/conf/services.properties b/idp-conf/src/main/resources/conf/services.properties
index 9e9b102..257dade 100644
--- a/idp-conf/src/main/resources/conf/services.properties
+++ b/idp-conf/src/main/resources/conf/services.properties
@@ -21,6 +21,11 @@ idp.service.relyingparty.checkInterval = PT15M
 #idp.service.metadata.failFast = false
 #idp.service.metadata.checkInterval = PT0S
 
+# Set to shibboleth.LegacyAttributeRegistryResources to support only AttributeEncoders
+#idp.service.attribute.registry.resources = shibboleth.AttributeRegistryResources
+#idp.service.attribute.registry.failFast = false
+idp.service.attribute.registry.checkInterval = PT15M
+
 #idp.service.attribute.resolver.resources = shibboleth.AttributeResolverResources
 #idp.service.attribute.resolver.failFast = false
 idp.service.attribute.resolver.checkInterval = PT15M
diff --git a/idp-conf/src/main/resources/conf/services.xml b/idp-conf/src/main/resources/conf/services.xml
index e04ac8f..558ae50 100644
--- a/idp-conf/src/main/resources/conf/services.xml
+++ b/idp-conf/src/main/resources/conf/services.xml
@@ -69,6 +69,18 @@
         <value>%{idp.home}/conf/attribute-resolver.xml</value>
     </util:list>
 
+    <!-- This set of resources relies on (at least) the new registry mappings. -->
+    <util:list id ="shibboleth.AttributeRegistryResources">
+        <value>%{idp.home}/conf/attribute-registry.xml</value>
+        <value>%{idp.home}/system/conf/attribute-registry-system.xml</value>
+    </util:list>
+
+    <!-- This set of resources uses only AttributeEncoders for compatibility. -->
+    <util:list id ="shibboleth.LegacyAttributeRegistryResources">
+        <value>%{idp.home}/conf/attribute-resolver.xml</value>
+        <value>%{idp.home}/system/conf/attribute-registry-system.xml</value>
+    </util:list>
+
     <util:list id ="shibboleth.AttributeFilterResources">
         <value>%{idp.home}/conf/attribute-filter.xml</value>
     </util:list>
diff --git a/idp-conf/src/main/resources/system/conf/attribute-registry-system.xml b/idp-conf/src/main/resources/system/conf/attribute-registry-system.xml
new file mode 100644
index 0000000..cf21b80
--- /dev/null
+++ b/idp-conf/src/main/resources/system/conf/attribute-registry-system.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:p="http://www.springframework.org/schema/p"
+       xmlns:c="http://www.springframework.org/schema/c"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+                           
+       default-init-method="initialize"
+       default-destroy-method="destroy">
+
+    <bean id="SAML2StringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2StringAttributeTranscoder" />
+    <bean id="SAML2ScopedStringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2ScopedStringAttributeTranscoder" />
+    <bean id="SAML2ByteTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2ByteAttributeTranscoder" />
+    <bean id="SAML2XMLObjectTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2XMLObjectAttributeTranscoder" />
+        
+    <bean id="SAML1StringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1StringAttributeTranscoder" />
+    <bean id="SAML1ScopedStringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1ScopedStringAttributeTranscoder" />
+    <bean id="SAML1ByteTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1ByteAttributeTranscoder" />
+    <bean id="SAML1XMLObjectTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1XMLObjectAttributeTranscoder" />
+    
+    <util:map id="DefaultNamingRegistry">
+        <entry key="#{T(org.opensaml.saml.saml2.core.Attribute)}">
+            <bean class="net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder.NamingFunction" />
+        </entry>
+        <entry key="#{T(org.opensaml.saml.saml1.core.AttributeDesignator)}">
+            <bean class="net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML1AttributeTranscoder.NamingFunction" />
+        </entry>
+    </util:map>
+    
+    <bean id="shibboleth.TranscodingRule"
+        class="net.shibboleth.idp.attribute.transcoding.TranscodingRule" abstract="true" />
+        
+    <bean id="shibboleth.TranscodingRuleLoader"
+        class="net.shibboleth.idp.attribute.transcoding.impl.TranscodingRuleLoader" abstract="true" />
+    
+</beans>
diff --git a/idp-conf/src/main/resources/system/conf/services-system.xml b/idp-conf/src/main/resources/system/conf/services-system.xml
index 8718a7f..8062fdc 100644
--- a/idp-conf/src/main/resources/system/conf/services-system.xml
+++ b/idp-conf/src/main/resources/system/conf/services-system.xml
@@ -54,6 +54,26 @@
         </constructor-arg>
     </bean>
 
+    <bean id="shibboleth.AttributeRegistryService" class="net.shibboleth.ext.spring.service.ReloadableSpringService" 
+            p:serviceConfigurations-ref="#{
+ 	            getObject('%{idp.service.attribute.registry.resources:shibboleth.AttributeRegistryResources}'.trim()) != null
+                    ? '%{idp.service.attribute.registry.resources:shibboleth.AttributeRegistryResources}'.trim()
+                        : 'shibboleth.DefaultAttributeRegistryResources' }"
+	        p:failFast="%{idp.service.attribute.registry.failFast:%{idp.service.failFast:false}}"
+	        p:reloadCheckDelay="%{idp.service.attribute.registry.checkInterval:PT0S}"
+	        p:beanPostProcessors-ref="shibboleth.IdentifiableBeanPostProcessor"
+	        p:beanFactoryPostProcessors-ref="shibboleth.PropertySourcesPlaceholderConfigurer">
+	    <constructor-arg name="claz" value="net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry" />
+	    <constructor-arg name="strategy">
+	       <bean class="net.shibboleth.idp.attribute.transcoding.spring.impl.AttributeRegistryServiceStrategy"
+	           p:namingRegistry="#{'%{idp.service.attribute.registry.namingRegistry:DefaultNamingRegistry}'.trim()}" />
+	    </constructor-arg>
+    </bean>
+    <util:list id ="shibboleth.DefaultAttributeRegistryResources">
+        <value>%{idp.home}/conf/attribute-registry.xml</value>
+        <value>%{idp.home}/system/conf/attribute-registry-system.xml</value>
+    </util:list>
+
     <bean id="shibboleth.NameIdentifierGenerationService" class="net.shibboleth.ext.spring.service.ReloadableSpringService" 
         c:claz="net.shibboleth.idp.saml.nameid.NameIdentifierGenerationService"
         p:serviceConfigurations-ref="#{'%{idp.service.nameidGeneration.resources:shibboleth.NameIdentifierGenerationResources}'.trim()}"
@@ -76,7 +96,7 @@
     </bean>
 
     <bean id="shibboleth.MetadataResolverService" class="net.shibboleth.ext.spring.service.ReloadableSpringService"
-            depends-on="shibboleth.AttributeResolverService" 
+            depends-on="shibboleth.AttributeRegistryService" 
             p:serviceConfigurations-ref="#{'%{idp.service.metadata.resources:shibboleth.MetadataResolverResources}'.trim()}"
             p:failFast="%{idp.service.metadata.failFast:%{idp.service.failFast:false}}"
             p:reloadCheckDelay="%{idp.service.metadata.checkInterval:PT0S}"
@@ -89,7 +109,7 @@
             <list>
                 <ref bean="shibboleth.IdentifiableBeanPostProcessor" />
                 <bean class="net.shibboleth.idp.profile.spring.relyingparty.metadata.impl.NodeProcessingAttachingBeanPostProcessor"
-                    c:service-ref="shibboleth.AttributeResolverService" />
+                    c:service-ref="shibboleth.AttributeRegistryService" />
             </list>
         </property>
     </bean>
@@ -159,14 +179,6 @@
         class="net.shibboleth.utilities.java.support.security.DelegatingAccessControlService"
         c:acService-ref="shibboleth.ReloadableAccessControlService" />
 
-    <bean id="shibboleth.SAML1AttributeDesignatorsMapperService"
-        class="net.shibboleth.idp.saml.attribute.mapping.impl.SAML1AttributeDesignatorsMapperService"
-        c:resolverService-ref="shibboleth.AttributeResolverService" />
-
-    <bean id="shibboleth.SAML2AttributesMapperService"
-        class="net.shibboleth.idp.saml.attribute.mapping.impl.SAML2AttributesMapperService"
-        c:resolverService-ref="shibboleth.AttributeResolverService" />
-
     <bean id="shibboleth.CASServiceRegistry"
           class="net.shibboleth.idp.cas.service.impl.ReloadingServiceRegistry"
           c:delegate-ref="shibboleth.ReloadableCASServiceRegistry" />
diff --git a/idp-conf/src/main/resources/system/flows/admin/status-beans.xml b/idp-conf/src/main/resources/system/flows/admin/status-beans.xml
index 3428e17..75da1bf 100644
--- a/idp-conf/src/main/resources/system/flows/admin/status-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/admin/status-beans.xml
@@ -32,6 +32,7 @@
         <ref bean="shibboleth.MetadataResolverService" />
         <ref bean="shibboleth.RelyingPartyResolverService" />
         <ref bean="shibboleth.NameIdentifierGenerationService" />
+        <ref bean="shibboleth.AttributeRegistryService" />
         <ref bean="shibboleth.AttributeResolverService" />
         <ref bean="shibboleth.AttributeFilterService" />
         <ref bean="shibboleth.ReloadableCASServiceRegistry" />
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml
index 1bedd54..71bcfd2 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml
@@ -87,7 +87,7 @@
 
     <bean id="MapRequestedAttributesInAttributeConsumingService" scope="prototype"
             class="net.shibboleth.idp.saml.saml2.profile.impl.MapRequestedAttributesInAttributeConsumingService"
-            p:resolverService-ref="shibboleth.AttributeResolverService" />
+            p:transcoderRegistry-ref="shibboleth.AttributeRegistryService" />
 
     <bean id="InitializeRelyingPartyContexFromSAMLPeer"
         class="net.shibboleth.idp.saml.profile.impl.InitializeRelyingPartyContextFromSAMLPeer" scope="prototype" />
@@ -239,14 +239,6 @@
         p:maskFailures="%{idp.service.attribute.filter.maskFailures:true}"
         p:metadataResolver-ref="shibboleth.MetadataResolver" />
 
-    <bean id="FilterByQueriedAttributeDesignators"
-        class="net.shibboleth.idp.saml.saml1.profile.impl.FilterByQueriedAttributeDesignators" scope="prototype"
-        c:mapper-ref="shibboleth.SAML1AttributeDesignatorsMapperService" />
-
-    <bean id="FilterByQueriedAttributes"
-        class="net.shibboleth.idp.saml.saml2.profile.impl.FilterByQueriedAttributes" scope="prototype"
-        c:mapper-ref="shibboleth.SAML2AttributesMapperService" />
-
     <bean id="PopulatePostAuthnInterceptContext"
             class="net.shibboleth.idp.profile.interceptor.impl.PopulateProfileInterceptorContext" scope="prototype"
             p:availableFlows-ref="shibboleth.AvailableInterceptFlows">
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml1/attribute-query-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml1/attribute-query-beans.xml
index 4cf0537..974d005 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml1/attribute-query-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml1/attribute-query-beans.xml
@@ -41,6 +41,10 @@
         class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
         c:_0-ref="shibboleth.OutgoingSOAPBindings" />
 
+    <bean id="FilterByQueriedAttributeDesignators"
+        class="net.shibboleth.idp.saml.saml1.profile.impl.FilterByQueriedAttributeDesignators" scope="prototype"
+        p:transcoderRegistry-ref="shibboleth.AttributeRegistryService" />
+
     <bean id="CopyNameIdentifierFromRequest"
             class="org.opensaml.saml.saml1.profile.impl.CopyNameIdentifierFromRequest" scope="prototype" />
 
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml1/common-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml1/common-beans.xml
index 9e74e4e..b184b1c 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml1/common-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml1/common-beans.xml
@@ -48,7 +48,8 @@
     </bean>
 
     <bean id="AddAttributeStatementToAssertion"
-            class="net.shibboleth.idp.saml.saml1.profile.impl.AddAttributeStatementToAssertion" scope="prototype">
+            class="net.shibboleth.idp.saml.saml1.profile.impl.AddAttributeStatementToAssertion" scope="prototype"
+            p:transcoderRegistry-ref="shibboleth.AttributeRegistryService">
         <property name="activationCondition">
             <bean class="net.shibboleth.idp.saml.profile.config.logic.IncludeAttributeStatementPredicate" />
         </property>
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml2/attribute-query-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml2/attribute-query-beans.xml
index 9fe154e..27bd70d 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml2/attribute-query-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml2/attribute-query-beans.xml
@@ -41,6 +41,10 @@
         class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
         c:_0-ref="shibboleth.OutgoingSOAPBindings" />
 
+    <bean id="FilterByQueriedAttributes"
+        class="net.shibboleth.idp.saml.saml2.profile.impl.FilterByQueriedAttributes" scope="prototype"
+        p:transcoderRegistry-ref="shibboleth.AttributeRegistryService" />
+
     <bean id="CopyNameIDFromRequest"
             class="org.opensaml.saml.saml2.profile.impl.CopyNameIDFromRequest" scope="prototype" />
 
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml2/common-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml2/common-beans.xml
index 2272ff0..4ba739c 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml2/common-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml2/common-beans.xml
@@ -62,7 +62,8 @@
     </bean>
 
     <bean id="AddAttributeStatementToAssertion"
-            class="net.shibboleth.idp.saml.saml2.profile.impl.AddAttributeStatementToAssertion" scope="prototype">
+            class="net.shibboleth.idp.saml.saml2.profile.impl.AddAttributeStatementToAssertion" scope="prototype"
+            p:transcoderRegistry-ref="shibboleth.AttributeRegistryService">
         <property name="activationCondition">
             <bean class="net.shibboleth.idp.saml.profile.config.logic.IncludeAttributeStatementPredicate" />
         </property>
diff --git a/idp-conf/src/test/java/net/shibboleth/idp/test/flows/mapper/MappingTest.java b/idp-conf/src/test/java/net/shibboleth/idp/test/flows/mapper/MappingTest.java
index d26af09..4c0c74e 100644
--- a/idp-conf/src/test/java/net/shibboleth/idp/test/flows/mapper/MappingTest.java
+++ b/idp-conf/src/test/java/net/shibboleth/idp/test/flows/mapper/MappingTest.java
@@ -28,7 +28,7 @@ import org.testng.annotations.Test;
  */
 @ContextConfiguration({"classpath:/mapping/configs/override.xml", })
 public class MappingTest extends AbstractFlowTest {
-    @Test public void testResolveAndFilter() {
+    @Test(enabled=true) public void testResolveAndFilter() {
 
         final FlowExecutionResult result = flowExecutor.launchExecution("resolveAndFilter", null, externalContext);
 
diff --git a/idp-conf/src/test/java/net/shibboleth/idp/test/flows/mapper/TestAfterFilter.java b/idp-conf/src/test/java/net/shibboleth/idp/test/flows/mapper/TestAfterFilter.java
index b6db71f..4bd9ed5 100644
--- a/idp-conf/src/test/java/net/shibboleth/idp/test/flows/mapper/TestAfterFilter.java
+++ b/idp-conf/src/test/java/net/shibboleth/idp/test/flows/mapper/TestAfterFilter.java
@@ -39,13 +39,13 @@ public class TestAfterFilter extends AbstractProfileAction {
         
         AttributeContext ac = rpc.getSubcontext(AttributeContext.class);
         
-        Assert.assertNotNull(ac.getIdPAttributes().get("IdPRA1"));
-        Assert.assertFalse(ac.getIdPAttributes().get("IdPRA1").getValues().isEmpty());
+        Assert.assertNull(ac.getIdPAttributes().get("IdPNotOK"));
         Assert.assertNull(ac.getIdPAttributes().get("IdPEA1"));
         
         Assert.assertNotNull(ac.getIdPAttributes().get("IdPOK"));
         Assert.assertFalse(ac.getIdPAttributes().get("IdPOK").getValues().isEmpty());
-        Assert.assertNull(ac.getIdPAttributes().get("IdPNotOK"));
-                
+
+        Assert.assertNotNull(ac.getIdPAttributes().get("IdPRA1"));
+        Assert.assertFalse(ac.getIdPAttributes().get("IdPRA1").getValues().isEmpty());
     }
 }
diff --git a/idp-conf/src/test/java/net/shibboleth/idp/test/flows/saml1/SAML1AttributeQueryFlowTest.java b/idp-conf/src/test/java/net/shibboleth/idp/test/flows/saml1/SAML1AttributeQueryFlowTest.java
index a1b83a4..92d8bec 100644
--- a/idp-conf/src/test/java/net/shibboleth/idp/test/flows/saml1/SAML1AttributeQueryFlowTest.java
+++ b/idp-conf/src/test/java/net/shibboleth/idp/test/flows/saml1/SAML1AttributeQueryFlowTest.java
@@ -161,7 +161,7 @@ public class SAML1AttributeQueryFlowTest extends AbstractSAML1FlowTest {
             designator.setAttributeNamespace(SAMLConstants.SAML1_ATTR_NAMESPACE_URI);
             designator.setAttributeName("urn:mace:dir:attribute-def:mail");
             attributeQuery.getAttributeQuery().getAttributeDesignators().add(designator);
-    
+
             designator = designatorBuilder.buildObject();
             designator.setAttributeNamespace(SAMLConstants.SAML1_ATTR_NAMESPACE_URI);
             designator.setAttributeName("urn:mace:dir:attribute-def:foo");
diff --git a/idp-conf/src/test/java/net/shibboleth/idp/test/flows/saml2/SAML2AttributeQueryFlowTest.java b/idp-conf/src/test/java/net/shibboleth/idp/test/flows/saml2/SAML2AttributeQueryFlowTest.java
index e16930d..b994561 100644
--- a/idp-conf/src/test/java/net/shibboleth/idp/test/flows/saml2/SAML2AttributeQueryFlowTest.java
+++ b/idp-conf/src/test/java/net/shibboleth/idp/test/flows/saml2/SAML2AttributeQueryFlowTest.java
@@ -170,7 +170,7 @@ public class SAML2AttributeQueryFlowTest extends AbstractSAML2FlowTest {
             XSAny value = valueBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
             value.setTextContent("jdoe at example.org");
             designator.getAttributeValues().add(value);
-    
+
             designator = designatorBuilder.buildObject();
             designator.setNameFormat(Attribute.URI_REFERENCE);
             designator.setName("urn:mace:dir:attribute-def:foo");
diff --git a/idp-conf/src/test/resources/mapping/configs/attribute-filter.xml b/idp-conf/src/test/resources/mapping/configs/attribute-filter.xml
index c690913..0d9edc7 100644
--- a/idp-conf/src/test/resources/mapping/configs/attribute-filter.xml
+++ b/idp-conf/src/test/resources/mapping/configs/attribute-filter.xml
@@ -28,7 +28,7 @@
     </AttributeFilterPolicy>
 	
 
-    <!--  Release is EA map -->
+    <!--  Release if EA map -->
 	<AttributeFilterPolicy id="EAOKFILTER">
 		<PolicyRequirementRule id="prrEA1" xsi:type="EntityAttributeExactMatch"
             attributeName="http://macedir.org/entity-category"
diff --git a/idp-conf/src/test/resources/mapping/configs/attribute-resolver.xml b/idp-conf/src/test/resources/mapping/configs/attribute-resolver.xml
index ec1cdf5..6acdcfd 100644
--- a/idp-conf/src/test/resources/mapping/configs/attribute-resolver.xml
+++ b/idp-conf/src/test/resources/mapping/configs/attribute-resolver.xml
@@ -17,7 +17,6 @@ Part 1 of a two part attribute resolver.
         <AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1" friendlyName="eduPersonAffiliation" />
     </AttributeDefinition>
 
-
     <DataConnector id="staticAttributes" xsi:type="Static">
         <Attribute id="ra_1">
             <Value>RA1</Value>
@@ -46,5 +45,4 @@ Part 1 of a two part attribute resolver.
             name="http://example.org/result"
             nameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" friendlyName="OKOUT" />
     </AttributeDefinition>
-    
 </AttributeResolver>
diff --git a/idp-conf/src/test/resources/mapping/configs/override.xml b/idp-conf/src/test/resources/mapping/configs/override.xml
index aa47df8..2c0a499 100644
--- a/idp-conf/src/test/resources/mapping/configs/override.xml
+++ b/idp-conf/src/test/resources/mapping/configs/override.xml
@@ -18,4 +18,9 @@
         <value>classpath:/mapping/configs/attribute-filter2.xml</value>
 	</util:list>
 
+	<util:list id ="shibboleth.AttributeRegistryResources">
+        <value>classpath:/system/conf/attribute-registry-system.xml</value>
+        <value>classpath:/mapping/configs/attribute-resolver.xml</value>
+        <value>classpath:/mapping/configs/attribute-resolver2.xml</value>
+    </util:list>
 </beans>
\ No newline at end of file
diff --git a/idp-consent-api/.project b/idp-consent-api/.project
index 62a8236..b2139c1 100644
--- a/idp-consent-api/.project
+++ b/idp-consent-api/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-consent-impl/.project b/idp-consent-impl/.project
index 2201166..31861b6 100644
--- a/idp-consent-impl/.project
+++ b/idp-consent-impl/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-consent-impl/src/main/java/net/shibboleth/idp/consent/logic/impl/IsAttributeRequiredPredicate.java b/idp-consent-impl/src/main/java/net/shibboleth/idp/consent/logic/impl/IsAttributeRequiredPredicate.java
index d64bf06..a23ae41 100644
--- a/idp-consent-impl/src/main/java/net/shibboleth/idp/consent/logic/impl/IsAttributeRequiredPredicate.java
+++ b/idp-consent-impl/src/main/java/net/shibboleth/idp/consent/logic/impl/IsAttributeRequiredPredicate.java
@@ -40,7 +40,7 @@ import com.google.common.collect.Multimap;
 
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.IdPRequestedAttribute;
-import net.shibboleth.idp.saml.attribute.mapping.AttributesMapContainer;
+import net.shibboleth.idp.saml.attribute.transcoding.AttributesMapContainer;
 import net.shibboleth.utilities.java.support.logic.Predicate;
 
 /**
diff --git a/idp-core/.project b/idp-core/.project
index d914eaf..df54311 100644
--- a/idp-core/.project
+++ b/idp-core/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-installer/.project b/idp-installer/.project
index e3f990e..bba0631 100644
--- a/idp-installer/.project
+++ b/idp-installer/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-profile-api/.project b/idp-profile-api/.project
index 818e804..6a29367 100644
--- a/idp-profile-api/.project
+++ b/idp-profile-api/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-profile-impl/.project b/idp-profile-impl/.project
index 8f46927..4a93d08 100644
--- a/idp-profile-impl/.project
+++ b/idp-profile-impl/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-profile-spring/.project b/idp-profile-spring/.project
index 585e593..a586093 100644
--- a/idp-profile-spring/.project
+++ b/idp-profile-spring/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-profile-spring/src/main/java/net/shibboleth/idp/profile/spring/relyingparty/metadata/impl/NodeProcessingAttachingBeanPostProcessor.java b/idp-profile-spring/src/main/java/net/shibboleth/idp/profile/spring/relyingparty/metadata/impl/NodeProcessingAttachingBeanPostProcessor.java
index 4610805..79be3bb 100644
--- a/idp-profile-spring/src/main/java/net/shibboleth/idp/profile/spring/relyingparty/metadata/impl/NodeProcessingAttachingBeanPostProcessor.java
+++ b/idp-profile-spring/src/main/java/net/shibboleth/idp/profile/spring/relyingparty/metadata/impl/NodeProcessingAttachingBeanPostProcessor.java
@@ -33,7 +33,7 @@ import org.opensaml.saml.metadata.resolver.filter.impl.NodeProcessingMetadataFil
 import org.springframework.beans.factory.BeanCreationException;
 import org.springframework.beans.factory.config.BeanPostProcessor;
 
-import net.shibboleth.idp.attribute.resolver.AttributeResolver;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
 import net.shibboleth.idp.saml.metadata.RelyingPartyMetadataProvider;
 import net.shibboleth.idp.saml.metadata.impl.AttributeMappingNodeProcessor;
 import net.shibboleth.idp.saml.security.impl.KeyAuthorityNodeProcessor;
@@ -52,8 +52,8 @@ import net.shibboleth.utilities.java.support.service.ReloadableService;
  */
 public class NodeProcessingAttachingBeanPostProcessor implements BeanPostProcessor {
 
-    /** The attribute resolver we use to map attributes. */
-    @Nullable private final ReloadableService<AttributeResolver> atributeResolverService;
+    /** The registry of decoding rules. */
+    @Nullable private final ReloadableService<AttributeTranscoderRegistry> transcoderRegistry;
 
     /**
      * Constructor.
@@ -61,8 +61,8 @@ public class NodeProcessingAttachingBeanPostProcessor implements BeanPostProcess
      * @param service the attribute resolver we use to map attributes
      */
     public NodeProcessingAttachingBeanPostProcessor(
-            @Nullable @ParameterName(name="service") final ReloadableService<AttributeResolver> service) {
-        atributeResolverService = service;
+            @Nullable @ParameterName(name="service") final ReloadableService<AttributeTranscoderRegistry> service) {
+        transcoderRegistry = service;
     }
 
     // Checkstyle: CyclomaticComplexity OFF
@@ -96,8 +96,8 @@ public class NodeProcessingAttachingBeanPostProcessor implements BeanPostProcess
             final NodeProcessingMetadataFilter filterToAttach = new NodeProcessingMetadataFilter();
             final List<MetadataNodeProcessor> processors = new ArrayList<>(
                     Arrays.asList(new EntitiesDescriptorNameProcessor(), new KeyAuthorityNodeProcessor()));
-            if (null != atributeResolverService) {
-                processors.add(new AttributeMappingNodeProcessor(atributeResolverService));
+            if (null != transcoderRegistry) {
+                processors.add(new AttributeMappingNodeProcessor(transcoderRegistry));
             }
             filterToAttach.setNodeProcessors(processors);
             try {
diff --git a/idp-saml-api/.project b/idp-saml-api/.project
index dc7374d..4fdf337 100644
--- a/idp-saml-api/.project
+++ b/idp-saml-api/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-saml-api/pom.xml b/idp-saml-api/pom.xml
index 525ed29..3b02eba 100644
--- a/idp-saml-api/pom.xml
+++ b/idp-saml-api/pom.xml
@@ -76,7 +76,6 @@
             <groupId>${opensaml.groupId}</groupId>
             <artifactId>opensaml-xmlsec-api</artifactId>
         </dependency>
-
         <dependency>
             <groupId>${spring.groupId}</groupId>
             <artifactId>spring-beans</artifactId>
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAML1AttributeEncoder.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAML1AttributeEncoder.java
deleted file mode 100644
index aa80232..0000000
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAML1AttributeEncoder.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding;
-
-import java.util.List;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeDesignatorMapper;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.logic.ConstraintViolationException;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
-
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
-import org.opensaml.saml.common.SAMLObjectBuilder;
-import org.opensaml.saml.common.xml.SAMLConstants;
-import org.opensaml.saml.saml1.core.Attribute;
-
-import com.google.common.base.Objects;
-
-/**
- * Base class for encoders that produce a SAML 1 {@link Attribute}.
- * 
- * @param <EncodedType> the type of data that can be encoded by the encoder
- */
-public abstract class AbstractSAML1AttributeEncoder<EncodedType extends IdPAttributeValue>
-        extends AbstractSAMLAttributeEncoder<Attribute, EncodedType>
-        implements SAML1AttributeEncoder<EncodedType>, AttributeDesignatorMapperProcessor<IdPAttribute> {
-
-    /** Builder used to construct {@link Attribute} objects. */
-    @Nonnull private final SAMLObjectBuilder<Attribute> attributeBuilder;
-    
-    /** The namespace in which the attribute name is interpreted. */
-    @NonnullAfterInit private String namespace;
-    
-    /** Constructor. */
-    public AbstractSAML1AttributeEncoder() {
-        attributeBuilder =
-                (SAMLObjectBuilder<Attribute>) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(
-                        Attribute.TYPE_NAME);
-        if (attributeBuilder == null) {
-            throw new ConstraintViolationException("SAML 1 Attribute builder is unavailable");
-        }
-        namespace = net.shibboleth.idp.saml.xml.SAMLConstants.SAML1_ATTR_NAMESPACE_URI;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @Nonnull @NotEmpty public final String getProtocol() {
-        return SAMLConstants.SAML11P_NS;
-    }
-
-    /** {@inheritDoc} */
-    @Override @NonnullAfterInit public String getNamespace() {
-        return namespace;
-    }
-
-    /**
-     * Set the namespace in which the attribute name is interpreted.
-     * 
-     * @param attributeNamespace namespace in which the attribute name is interpreted
-     */
-    public void setNamespace(@Nonnull @NotEmpty final String attributeNamespace) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        
-        namespace = Constraint.isNotNull(StringSupport.trimOrNull(attributeNamespace),
-                "Attribute namespace cannot be null or empty");
-    }
-    
-    /** {@inheritDoc} */
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-
-        if (namespace == null) {
-            throw new ComponentInitializationException("Attribute namespace cannot be null or empty");
-        }
-    }
-    
-    /** {@inheritDoc} */
-    @Override
-    @Nonnull protected Attribute buildAttribute(@Nonnull final IdPAttribute idpAttribute,
-            @Nonnull @NonnullElements final List<XMLObject> attributeValues) throws AttributeEncodingException {
-
-        final Attribute samlAttribute = attributeBuilder.buildObject();
-        samlAttribute.setAttributeName(getName());
-        samlAttribute.setAttributeNamespace(getNamespace());
-        samlAttribute.getAttributeValues().addAll(attributeValues);
-
-        return samlAttribute;
-    }
-    
-    /** {@inheritDoc} */
-    @Override
-    @Nonnull public void populateAttributeMapper(
-            @Nonnull final AbstractSAMLAttributeDesignatorMapper<IdPAttribute> mapper) {
-        mapper.setAttributeNamespace(getNamespace());
-        mapper.setId(getMapperId());
-        mapper.setSAMLName(getName());
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean equals(final Object obj) {
-
-        if (!super.equals(obj)) {
-            return false;
-        }
-        
-        if (!(obj instanceof AbstractSAML1AttributeEncoder)) {
-            return false;
-        }
-
-        final AbstractSAML1AttributeEncoder other = (AbstractSAML1AttributeEncoder) obj;
-        return java.util.Objects.equals(getNamespace(), other.getNamespace());
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(super.hashCode(), getNamespace());
-    }
-
-    /**
-     * Generate an Id suitable for the mapper.
-     * 
-     * @return a suitable Id for the mapper
-     */
-    @Nonnull @NotEmpty private String getMapperId() {
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-        return "MapperForAttribute" + getName();
-    }
-    
-}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAML2AttributeEncoder.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAML2AttributeEncoder.java
deleted file mode 100644
index af37398..0000000
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAML2AttributeEncoder.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding;
-
-import java.util.List;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.ConstraintViolationException;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
-
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
-import org.opensaml.saml.common.SAMLObjectBuilder;
-import org.opensaml.saml.common.xml.SAMLConstants;
-import org.opensaml.saml.saml2.core.Attribute;
-
-import com.google.common.base.Objects;
-
-/**
- * Base class for encoders that produce a SAML 2 {@link Attribute}.
- * 
- * @param <EncodedType> the type of data that can be encoded by the encoder
- */
-public abstract class AbstractSAML2AttributeEncoder<EncodedType extends IdPAttributeValue> extends
-        AbstractSAMLAttributeEncoder<Attribute, EncodedType> implements SAML2AttributeEncoder<EncodedType> {
-
-    /** Builder used to construct {@link Attribute} objects. */
-    @Nonnull private final SAMLObjectBuilder<Attribute> attributeBuilder;
-
-    /** A friendly, human readable, name for the attribute. */
-    @Nullable private String friendlyName;
-
-    /** The format of the attribute name. */
-    @Nullable private String format;
-
-    /** Constructor. */
-    public AbstractSAML2AttributeEncoder() {
-        attributeBuilder =
-                (SAMLObjectBuilder<Attribute>) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(
-                        Attribute.TYPE_NAME);
-        if (attributeBuilder == null) {
-            throw new ConstraintViolationException("SAML 2 Attribute builder is unavailable");
-        }
-        setNameFormat(Attribute.URI_REFERENCE);
-    }
-
-    /** {@inheritDoc} */
-    @Override @Nonnull public final String getProtocol() {
-        return SAMLConstants.SAML20P_NS;
-    }
-
-    /**
-     * Get the friendly, human readable, name for the attribute.
-     * 
-     * @return friendly, human readable, name for the attribute
-     */
-    @Nullable public final String getFriendlyName() {
-        return friendlyName;
-    }
-
-    /**
-     * Set the friendly, human readable, name for the attribute.
-     * 
-     * @param attributeFriendlyName friendly, human readable, name for the attribute
-     */
-    public void setFriendlyName(@Nullable final String attributeFriendlyName) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
-        friendlyName = StringSupport.trimOrNull(attributeFriendlyName);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @Nullable public String getNameFormat() {
-        return format;
-    }
-
-    /**
-     * Get the name format, or the SAML constants for "unspecified", if not set.
-     * 
-     * @return the effective name format regardless of nulls
-     */
-    @Nonnull @NotEmpty public String getEffectiveNameFormat() {
-        return format != null ? format : Attribute.UNSPECIFIED;
-    }
-
-    /**
-     * Set the format of the attribute name.
-     * 
-     * @param nameFormat format in which the attribute name is interpreted
-     */
-    public void setNameFormat(@Nullable final String nameFormat) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
-        format = StringSupport.trimOrNull(nameFormat);
-    }
-
-    /** {@inheritDoc} */
-    @Override @Nonnull protected Attribute buildAttribute(@Nonnull final IdPAttribute idpAttribute,
-            @Nonnull @NonnullElements final List<XMLObject> attributeValues) throws AttributeEncodingException {
-
-        final Attribute samlAttribute = attributeBuilder.buildObject();
-        samlAttribute.setName(getName());
-        samlAttribute.setFriendlyName(getFriendlyName());
-        samlAttribute.setNameFormat(getNameFormat());
-        samlAttribute.getAttributeValues().addAll(attributeValues);
-
-        return samlAttribute;
-    }
-
-    /**
-     * Generate an Id suitable for the mapper.
-     * 
-     * @return a suitable Id for the mapper
-     */
-    @Nonnull protected String getMapperId() {
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-        if (null != getFriendlyName()) {
-            return "MapperFor" + getFriendlyName();
-        }
-        return "MapperForAttribute" + getName();
-    }
-
-    /** {@inheritDoc} */
-    @Override public boolean equals(final Object obj) {
-
-        if (!super.equals(obj)) {
-            return false;
-        }
-
-        if (!(obj instanceof AbstractSAML2AttributeEncoder)) {
-            return false;
-        }
-
-        final AbstractSAML2AttributeEncoder other = (AbstractSAML2AttributeEncoder) obj;
-        return java.util.Objects.equals(getEffectiveNameFormat(), other.getEffectiveNameFormat());
-    }
-
-    /** {@inheritDoc} */
-    @Override public int hashCode() {
-        return Objects.hashCode(super.hashCode(), getEffectiveNameFormat());
-    }
-}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAMLAttributeEncoder.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAMLAttributeEncoder.java
deleted file mode 100644
index 1e7f36b..0000000
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAMLAttributeEncoder.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Predicate;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.AttributeEncoder;
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.component.UnmodifiableComponent;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
-
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.saml.common.SAMLObject;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Predicates;
-
-/**
- * Base class for encoders that produce SAML attributes.
- * 
- * @param <AttributeType> type of attribute produced
- * @param <EncodedType> the type of data that can be encoded by the encoder
- */
-public abstract class AbstractSAMLAttributeEncoder<AttributeType extends SAMLObject,
-            EncodedType extends IdPAttributeValue> extends AbstractInitializableComponent
-                implements AttributeEncoder<AttributeType>, UnmodifiableComponent {
-
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractSAMLAttributeEncoder.class);
-
-    /** Condition for use of this encoder. */
-    @Nonnull private Predicate<ProfileRequestContext> activationCondition;
-    
-    /** The name of the attribute. */
-    @NonnullAfterInit private String name;
-
-    /** Whether to encode with xsi:type or not. */
-    private boolean encodeType;
-    
-    /** Constructor. */
-    public AbstractSAMLAttributeEncoder() {
-        activationCondition = Predicates.alwaysTrue();
-        encodeType = true;
-    }
-    
-    /** {@inheritDoc} */
-    @Override
-    @Nonnull public Predicate<ProfileRequestContext> getActivationCondition() {
-        return activationCondition;
-    }
-    
-    /**
-     * Set the activation condition for this encoder.
-     * 
-     * @param condition condition to set
-     */
-    public void setActivationCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        
-        activationCondition = Constraint.isNotNull(condition, "Activation condition cannot be null");
-    }
-    
-    /**
-     * Get the name of the attribute.
-     * 
-     * @return name of the attribute
-     */
-    @NonnullAfterInit public final String getName() {
-        return name;
-    }
-
-    /**
-     * Set the name of the attribute.
-     * 
-     * @param attributeName name of the attribute
-     */
-    public void setName(@Nonnull @NotEmpty final String attributeName) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        
-        name = Constraint.isNotNull(StringSupport.trimOrNull(attributeName),
-                "Attribute name cannot be null or empty");
-    }
-    
-    /**
-     * Get whether to encode type information.
-     * 
-     * <p>Defaults to 'true'</p>
-     * 
-     * @return true iff type information should be encoded
-     */
-    public boolean encodeType() {
-        return encodeType;
-    }
-    
-    /**
-     * Set whether to encode type information.
-     * 
-     * @param flag flag to set
-     */
-    public void setEncodeType(final boolean flag) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        
-        encodeType = flag;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-
-        if (name == null) {
-            throw new ComponentInitializationException("Attribute name cannot be null or empty");
-        }
-    }
-    
-    /** {@inheritDoc} */
-    @Override
-    @Nonnull public AttributeType encode(@Nonnull final IdPAttribute attribute) throws AttributeEncodingException {
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-        Constraint.isNotNull(attribute, "Attribute to encode cannot be null");
-        final String attributeId = attribute.getId();
-        log.debug("Beginning to encode attribute {}", attributeId);
-
-        if (attribute.getValues().isEmpty()) {
-            throw new AttributeEncodingException("Unable to encode " + attributeId
-                    + " attribute.  It does not contain any values");
-        }
-
-        final List<XMLObject> samlAttributeValues = new ArrayList<>();
-
-        EncodedType attributeValue;
-        XMLObject samlAttributeValue;
-        for (final IdPAttributeValue o : attribute.getValues()) {
-            if (o == null) {
-                // filtered out upstream leave in test for sanity
-                log.debug("Skipping null value of attribute {}", attributeId);
-                continue;
-            }
-
-            if (!canEncodeValue(attribute, o)) {
-                log.warn("Skipping value of attribute '{}'; Type {} cannot be encoded by this encoder ({}).",
-                        attributeId, o.getClass().getName(), this.getClass().getName());
-                continue;
-            }
-
-            attributeValue = (EncodedType) o;
-            samlAttributeValue = encodeValue(attribute, attributeValue);
-            if (samlAttributeValue == null) {
-                log.debug("Skipping empty value for attribute {}", attributeId);
-            } else {
-                samlAttributeValues.add(samlAttributeValue);
-            }
-        }
-
-        if (samlAttributeValues.isEmpty()) {
-            throw new AttributeEncodingException("Attribute " + attributeId + " did not contain any encodeable values");
-        }
-
-        log.debug("Completed encoding {} values for attribute {}", samlAttributeValues.size(), attributeId);
-        return buildAttribute(attribute, samlAttributeValues);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean equals(final Object obj) {
-        if (obj == null) {
-            return false;
-        }
-
-        if (obj == this) {
-            return true;
-        }
-
-        if (!(obj instanceof AbstractSAMLAttributeEncoder)) {
-            return false;
-        }
-
-        final AbstractSAMLAttributeEncoder other = (AbstractSAMLAttributeEncoder) obj;
-        return java.util.Objects.equals(getName(), other.getName())
-                && java.util.Objects.equals(getProtocol(), other.getProtocol());
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(getName(), getProtocol());
-    }
-
-    /**
-     * Checks if the given value can be handled by the encoder. In many cases this is simply a check to see if the given
-     * object is of the right type.
-     * 
-     * @param idpAttribute the attribute being encoded, never null
-     * @param value the value to check, never null
-     * 
-     * @return true if the encoder can encoder this value, false if not
-     */
-    protected abstract boolean canEncodeValue(@Nonnull final IdPAttribute idpAttribute,
-            @Nonnull final IdPAttributeValue value);
-
-    /**
-     * Encodes an attribute value in to a SAML attribute value element.
-     * 
-     * @param idpAttribute the attribute being encoded, never null
-     * @param value the value to encoder, never null
-     * 
-     * @return the attribute value or null if the resulting attribute value would be empty
-     * 
-     * @throws AttributeEncodingException thrown if there is a problem encoding the attribute value
-     */
-    @Nullable protected abstract XMLObject encodeValue(@Nonnull final IdPAttribute idpAttribute,
-            @Nonnull final EncodedType value) throws AttributeEncodingException;
-
-    /**
-     * Builds a SAML attribute element from the given attribute values.
-     * 
-     * @param idpAttribute the attribute being encoded, never null
-     * @param attributeValues the encoded values for the attribute, never null or containing null elements
-     * 
-     * @return the SAML attribute element
-     * 
-     * @throws AttributeEncodingException thrown if there is a problem constructing the SAML attribute
-     */
-    @Nonnull protected abstract AttributeType buildAttribute(@Nonnull final IdPAttribute idpAttribute,
-            @Nonnull @NonnullElements final List<XMLObject> attributeValues) throws AttributeEncodingException;
-}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/AttributeDesignatorMapperProcessor.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/AttributeDesignatorMapperProcessor.java
deleted file mode 100644
index cc420b9..0000000
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/AttributeDesignatorMapperProcessor.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeDesignatorMapper;
-
-/**
- * An interface which is implemented by encoders which encode SAML 1 attributes and which can be reverse mapped.
- * 
- * @param <I> The IdP Type
- */
-public interface AttributeDesignatorMapperProcessor<I extends IdPAttribute> {
-
-    /**
-     * This populates the attribute mapper to go from a SAML AttributeDesignator to an IdP Attribute (or
-     * derivative) such that applying the output of the mapper to the encoder will produce the name attribute
-     * (and vice versa).
-     * 
-     * <p>The populated mapper <em>must not</em> have any associated (IdP) Attribute names and <em>must</em> implement
-     * {@link Object#equals(Object)} and {@link Object#hashCode()}.</p>
-     * 
-     * @param mapper the mapper.
-     */
-    @Nonnull void populateAttributeMapper(@Nonnull final AbstractSAMLAttributeDesignatorMapper<I> mapper);
-
-}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/AttributeMapperProcessor.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/AttributeMapperProcessor.java
deleted file mode 100644
index c676c88..0000000
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/AttributeMapperProcessor.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeMapper;
-
-import org.opensaml.saml.saml2.core.Attribute;
-
-/**
- * An interface which is implemented by encoders which encode attributes and which can be reverse mapped.
- * 
- * @param <S> The SAML Type
- * @param <I> The IdP Type
- */
-public interface AttributeMapperProcessor<S extends Attribute,I extends IdPAttribute> {
-
-    /**
-     * This populates the attribute mapper to go from a SAML Attribute (or derivative) to an IdP Attribute (or
-     * derivative) such that applying the output of the mapper to the encoder will produce the name attribute
-     * (and vice versa).
-     * 
-     * <p>The populated mapper <em>must not</em> have any associated (IdP) Attribute names and <em>must</em> implement
-     * {@link Object#equals(Object)} and {@link Object#hashCode()}.</p>
-     * 
-     * @param mapper the mapper.
-     */
-    @Nonnull void populateAttributeMapper(@Nonnull final AbstractSAMLAttributeMapper<S, I> mapper);
-
-}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/SAML1AttributeEncoder.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/SAML1AttributeEncoder.java
deleted file mode 100644
index bcb202f..0000000
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/SAML1AttributeEncoder.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding;
-
-import net.shibboleth.idp.attribute.AttributeEncoder;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
-
-import org.opensaml.saml.saml1.core.Attribute;
-
-
-/**
- * Interface for encoders that produce a SAML 1 {@link Attribute}.
- * 
- * @param <EncodedType> the type of data that can be encoded by the encoder
- */
-public interface SAML1AttributeEncoder<EncodedType extends IdPAttributeValue> extends AttributeEncoder<Attribute> {
-
-    /**
-     * Get the encoded Name of the attribute.
-     * 
-     * @return name of the attribute
-     */
-    @NonnullAfterInit String getName();
- 
-    /**
-     * Get the encoded Namespace of the attribute.
-     * 
-     * @return namespace of the attribute
-     */
-    @NonnullAfterInit String getNamespace();
-
-}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AbstractSAMLAttributeDesignatorMapper.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AbstractSAMLAttributeDesignatorMapper.java
deleted file mode 100644
index 0e0495f..0000000
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AbstractSAMLAttributeDesignatorMapper.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.annotation.constraint.NullableElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
-import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
-
-import org.opensaml.saml.saml1.core.AttributeDesignator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableList;
-
-/**
- * Basis of all classes which map SAML1 {@link AttributeDesignator} into an IdP {@link IdPAttribute}.
- * 
- * @param <OutType> the output (IdP Attribute) type
- */
-public abstract class AbstractSAMLAttributeDesignatorMapper<OutType extends IdPAttribute> extends
-        AbstractIdentifiableInitializableComponent implements AttributeMapper<AttributeDesignator, OutType> {
-
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractSAMLAttributeDesignatorMapper.class);
-
-    /** The internal names to generate. */
-    @Nonnull @NonnullElements private List<String> attributeIds = Collections.emptyList();
-
-    /** The attribute namespace. */
-    @Nullable private String attributeNamespace;
-
-    /** the (SAML) attribute name. */
-    @NonnullAfterInit private String theSAMLName;
-
-    /** The String used to prefix log message. */
-    @Nullable private String logPrefix;
-
-    /**
-     * Set the list of internal identifiers.
-     * 
-     * @param theIds the list
-     */
-    public void setAttributeIds(@Nullable @NullableElements final List<String> theIds) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        if (null == theIds) {
-            return;
-        }
-
-        attributeIds = ImmutableList.copyOf(StringSupport.normalizeStringCollection(theIds));
-    }
-
-    /**
-     * Get the list of internal identifiers.
-     * 
-     * @return the identifiers
-     */
-    @Nonnull @NonnullElements @Unmodifiable public List<String> getAttributeIds() {
-        return attributeIds;
-    }
-
-    /**
-     * Set the SAML attribute name.
-     * 
-     * @param name the name
-     */
-    public void setSAMLName(@Nullable final String name) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        theSAMLName = StringSupport.trimOrNull(name);
-    }
-
-    /**
-     * Get the SAML attribute name.
-     * 
-     * @return the name
-     */
-    @NonnullAfterInit public String getSAMLName() {
-        return theSAMLName;
-    }
-
-    /**
-     * Set the (optional) attribute namespace.
-     * 
-     * @param ns the namespace to set
-     */
-    public void setAttributeNamespace(@Nullable final String ns) {
-        attributeNamespace = StringSupport.trimOrNull(ns);
-    }
-
-    /**
-     * Get the (optional) attribute namespace.
-     * 
-     * @return the namespace
-     */
-    @Nullable public String getAttributeNamespace() {
-        return attributeNamespace;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-        if (null == theSAMLName) {
-            throw new ComponentInitializationException(getLogPrefix() + " SAML name not present");
-        }
-        if (attributeIds.isEmpty()) {
-            throw new ComponentInitializationException(getLogPrefix()
-                    + " At least one attribute Id should be provided");
-        }
-        logPrefix = null;
-    }
-
-    /**
-     * Compare whether the name and namespace given "match" ours. Used in checking both against input attributes and as
-     * part of equality checking.
-     * 
-     * @param otherSAMLName the name to compare against
-     * @param otherSAMLNamespace the bamespace to compare against
-     * @return whether there is a match.
-     */
-    protected boolean matches(@Nonnull @NotEmpty final String otherSAMLName,
-            @Nullable final String otherSAMLNamespace) {
-        if (!otherSAMLName.equals(theSAMLName)) {
-            log.debug("{} SAML attribute name {} does not match {}", getLogPrefix(), otherSAMLName, getId());
-            return false;
-        } else if (!java.util.Objects.equals(otherSAMLNamespace, getAttributeNamespace())) {
-            log.debug("{} SAML attribute namespace {} does not match {}", getLogPrefix(), otherSAMLNamespace,
-                    getAttributeNamespace());
-            return false;
-        }
-        
-        return true;
-    }
-
-    /**
-     * Determines if the input matches the provided parameterisation.
-     * 
-     * @param designator the designator to consider
-     * @return whether it matches.
-     */
-    protected boolean attributeMatches(@Nonnull final AttributeDesignator designator) {
-        return matches(designator.getAttributeName(), designator.getAttributeNamespace());
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @Nonnull @NonnullElements public Map<String,OutType> mapAttribute(@Nonnull final AttributeDesignator prototype) {
-
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-
-        if (!attributeMatches(prototype)) {
-            return Collections.emptyMap();
-        }
-
-        final Map<String,OutType> output = new HashMap<>();
-
-        log.debug("{} attribute id {} and aliases {} will be created", getLogPrefix(), getId(), getAttributeIds());
-
-        OutType out = newAttribute(prototype, getId());
-
-        for (final String id : attributeIds) {
-            out = newAttribute(prototype, getId());
-            output.put(id, out);
-        }
-        return output;
-    }
-
-    /**
-     * return a string which is to be prepended to all log messages.
-     * 
-     * @return "<type> Attribute Mapper '<mapper ID>' :"
-     */
-    @Nonnull @NotEmpty protected String getLogPrefix() {
-        // local cache of cached entry to allow unsynchronised clearing.
-        String prefix = logPrefix;
-        if (null == prefix) {
-            final StringBuilder builder = new StringBuilder();
-
-            builder.append("Attribute Mapper '").append(getId()).append("':");
-            prefix = builder.toString();
-            if (null == logPrefix) {
-                logPrefix = prefix;
-            }
-        }
-        return prefix;
-    }
-
-    /**
-     * {@inheritDoc}. The identity is not part of equality of hash
-     */
-    @Override
-    public boolean equals(final Object obj) {
-        if (obj == null) {
-            return false;
-        }
-
-        if (obj == this) {
-            return true;
-        }
-
-        if (obj instanceof AbstractSAMLAttributeDesignatorMapper) {
-            final AbstractSAMLAttributeDesignatorMapper other = (AbstractSAMLAttributeDesignatorMapper) obj;
-            return matches(other.getSAMLName(), other.getAttributeNamespace())
-                    && java.util.Objects.equals(getAttributeIds(), other.getAttributeIds());
-        }
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}. The identity is not part of equality of hash
-     */
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(attributeNamespace, theSAMLName, attributeIds);
-    }
-
-    /**
-     * Function to summon up the output type based on an ID and an object of the input type. Typically the input type
-     * will be inspected for extra parameterisation.
-     * 
-     * @param input the input value to inspect.
-     * @param id the identifier of the new attribute.
-     * @return an output, suitable set up with per object information.
-     */
-    @Nonnull protected abstract OutType newAttribute(@Nonnull final AttributeDesignator input,
-            @Nonnull @NotEmpty final String id);
-
-}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AbstractSAMLAttributeDesignatorsMapper.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AbstractSAMLAttributeDesignatorsMapper.java
deleted file mode 100644
index cccd896..0000000
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AbstractSAMLAttributeDesignatorsMapper.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.saml.saml1.core.AttributeDesignator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Supplier;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
-
-import net.shibboleth.idp.attribute.AttributeEncoder;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.resolver.AttributeResolver;
-import net.shibboleth.idp.saml.attribute.encoding.AttributeDesignatorMapperProcessor;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.collection.Pair;
-import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-
-/**
- * The class contains the mechanics to go from a list of {@link AttributeDesignator}s to a {@link Multimap} of
- * {@link String},{@link IdPAttribute} (or derived, or null). The representation as a {@link Multimap} is useful for
- * filtering situations and is exploited by the AttributeInMetadata filter.
- * 
- * @param <OutType> some sort of representation of an IdP attribute
- */
-public abstract class AbstractSAMLAttributeDesignatorsMapper<OutType extends IdPAttribute>
-        extends AbstractIdentifiableInitializableComponent implements AttributesMapper<AttributeDesignator,OutType> {
-
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractSAMLAttributeDesignatorsMapper.class);
-
-    /** The mappers we can apply. */
-    @Nonnull @NonnullElements private Collection<AttributeMapper<AttributeDesignator,OutType>> mappers;
-
-    /** The String used to prefix log message. */
-    @Nullable private String logPrefix;
-
-    /** Default Constructor. */
-    public AbstractSAMLAttributeDesignatorsMapper() {
-        mappers = Collections.emptyList();
-    }
-    
-    /**
-     * Constructor to create the mapping from an existing resolver.
-     * 
-     * <p>This code inverts the {@link AttributeEncoder} (internal attribute -> SAML Attributes) into
-     * {@link AttributeMapper} (SAML [AttributeDesignators] -> internal [Requested] Attributes). <br/>
-     * to generate the {@link AbstractSAMLAttributeDesignatorMapper} (with no
-     * {@link AbstractSAMLAttributeDesignatorMapper#getAttributeIds()}. These are accumulated into a {@link Multimap},
-     * where the key is the {@link AbstractSAMLAttributeDesignatorMapper} and the values are the (IdP) attribute names.
-     * The collection of {@link AttributeMapper}s can then be extracted from the map, and the appropriate internal names
-     * added (these being the value of the {@link Multimap})</p>
-     * 
-     * @param resolver the resolver
-     * @param id the ID
-     * @param mapperFactory factory to generate new mappers of the correct type.
-     */
-    public AbstractSAMLAttributeDesignatorsMapper(@Nonnull final AttributeResolver resolver,
-            @Nonnull @NotEmpty final String id,
-            @Nonnull final Supplier<AbstractSAMLAttributeDesignatorMapper<OutType>> mapperFactory) {
-
-        setId(id); 
-        mappers = Collections.emptyList();
-        
-        final Multimap<AbstractSAMLAttributeDesignatorMapper<OutType>,String> theMappers = HashMultimap.create();
-
-        for (final Pair<String, AttributeEncoder<?>> pair : resolver.getAllEncoders()) {
-            if (pair.getSecond() instanceof AttributeDesignatorMapperProcessor) {
-                // There is an appropriate reverse mapper.
-                final AttributeDesignatorMapperProcessor factory =
-                        (AttributeDesignatorMapperProcessor) pair.getSecond();
-                final AbstractSAMLAttributeDesignatorMapper<OutType> mapper = mapperFactory.get();
-                factory.populateAttributeMapper(mapper);
-
-                theMappers.put(mapper, pair.getFirst());
-            }
-        }
-        
-        mappers = new ArrayList<>(theMappers.values().size());
-
-        for (final Entry<AbstractSAMLAttributeDesignatorMapper<OutType>,Collection<String>> entry
-                : theMappers.asMap().entrySet()) {
-
-            final AbstractSAMLAttributeDesignatorMapper<OutType> mapper = entry.getKey();
-            mapper.setAttributeIds(new ArrayList<>(entry.getValue()));
-            mappers.add(mapper);
-        }
-    }
-
-    /**
-     * Get the mappers.
-     * 
-     * @return Returns the mappers.
-     */
-    @Nonnull @NonnullElements public Collection<AttributeMapper<AttributeDesignator,OutType>> getMappers() {
-        return mappers;
-    }
-
-    /**
-     * Set the attribute mappers into the lookup map.
-     * 
-     * @param theMappers The mappers to set.
-     */
-    public void setMappers(@Nonnull final Collection<AttributeMapper<AttributeDesignator,OutType>> theMappers) {
-        mappers = Constraint.isNotNull(theMappers, "Mappers list cannot be null");
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @Nonnull @NonnullElements public Multimap<String,OutType> mapAttributes(
-            @Nonnull @NonnullElements final List<AttributeDesignator> prototypes) {
-
-        final Multimap<String,OutType> result = ArrayListMultimap.create();
-
-        for (final AttributeDesignator prototype : prototypes) {
-            for (final AttributeMapper<AttributeDesignator,OutType> mapper : mappers) {
-
-                final Map<String,OutType> mappedAttributes = mapper.mapAttribute(prototype);
-
-                log.debug("{} SAML attribute '{}' mapped to {} attributes by mapper '{}'", getLogPrefix(),
-                        prototype.getAttributeName(), mappedAttributes.size(), mapper.getId());
-
-                for (final Entry<String,OutType> entry : mappedAttributes.entrySet()) {
-                    result.put(entry.getKey(), entry.getValue());
-                }
-            }
-        }
-        return result;
-    }
-
-    /** {@inheritDoc} */
-    @Override protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-        
-        logPrefix = null;
-        for (final AttributeMapper mapper : mappers) {
-            ComponentSupport.initialize(mapper);
-        }
-    }
-
-    /**
-     * Return a string which is to be prepended to all log messages.
-     * 
-     * @return "Attribute Mappers '<ID>' :"
-     */
-    @Nonnull @NotEmpty private String getLogPrefix() {
-        String s = logPrefix;
-        if (null == s) {
-            s = new StringBuilder("Attribute Mappers : '").append(getId()).append("':").toString();
-            logPrefix = s;
-        }
-        return s;
-    }
-    
-}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AbstractSAMLAttributeMapper.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AbstractSAMLAttributeMapper.java
deleted file mode 100644
index 217de97..0000000
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AbstractSAMLAttributeMapper.java
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.annotation.constraint.NullableElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
-import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
-
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.saml.saml2.core.Attribute;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableList;
-
-/**
- * Basis of all classes which map SAML2 {@link Attribute} into an IdP {@link IdPAttribute}.
- * 
- * @param <InType> the input (SAML2 attribute) type
- * @param <OutType> the output (IdP Attribute) type
- */
-public abstract class AbstractSAMLAttributeMapper<InType extends Attribute, OutType extends IdPAttribute> extends
-        AbstractIdentifiableInitializableComponent implements AttributeMapper<InType, OutType> {
-
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractSAMLAttributeMapper.class);
-
-    /** The internal names to generate. */
-    @Nonnull @NonnullElements private List<String> attributeIds = Collections.emptyList();
-
-    /** The attribute format. */
-    @Nullable private String attributeFormat;
-
-    /** the (SAML) attribute name. */
-    @NonnullAfterInit private String theSAMLName;
-
-    /** The String used to prefix log message. */
-    @Nullable private String logPrefix;
-
-    /** The value mapper. */
-    @NonnullAfterInit private AbstractSAMLAttributeValueMapper valueMapper;
-
-    /**
-     * Set the list of internal identifiers.
-     * 
-     * @param theIds the list
-     */
-    public void setAttributeIds(@Nullable @NullableElements final List<String> theIds) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        if (null == theIds) {
-            return;
-        }
-
-        attributeIds = ImmutableList.copyOf(StringSupport.normalizeStringCollection(theIds));
-    }
-
-    /**
-     * Get the list of internal identifiers.
-     * 
-     * @return the identifiers
-     */
-    @Nonnull @NonnullElements @Unmodifiable public List<String> getAttributeIds() {
-        return attributeIds;
-    }
-
-    /**
-     * Set the SAML attribute name.
-     * 
-     * @param name the name
-     */
-    public void setSAMLName(@Nullable final String name) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        theSAMLName = StringSupport.trimOrNull(name);
-    }
-
-    /**
-     * Get the SAML attribute name.
-     * 
-     * @return the name
-     */
-    @NonnullAfterInit public String getSAMLName() {
-        return theSAMLName;
-    }
-
-    /**
-     * Get the class which converts types.
-     * 
-     * @param mapper The valueMapper to set.
-     */
-    public void setValueMapper(@Nullable final AbstractSAMLAttributeValueMapper mapper) {
-        valueMapper = mapper;
-    }
-
-    /**
-     * Get the class which converts types.
-     * 
-     * @return Returns the valueMapper.
-     */
-    @NonnullAfterInit public AbstractSAMLAttributeValueMapper getValueMapper() {
-        return valueMapper;
-    }
-
-    /**
-     * Set the (optional) attribute format.
-     * 
-     * @param format the format
-     */
-    public void setAttributeFormat(@Nullable final String format) {
-        attributeFormat = StringSupport.trimOrNull(format);
-    }
-
-    /**
-     * Get the (optional) attribute format.
-     * 
-     * @return the format
-     */
-    @Nullable public String getAttributeFormat() {
-        return attributeFormat;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-        if (null == theSAMLName) {
-            throw new ComponentInitializationException(getLogPrefix() + " SAML name not present");
-        }
-        if (null == valueMapper) {
-            throw new ComponentInitializationException(getLogPrefix() + " No value mapper present");
-        }
-        if (attributeIds.isEmpty()) {
-            throw new ComponentInitializationException(getLogPrefix()
-                    + " At least one attribute Id should be provided");
-        }
-        logPrefix = null;
-        valueMapper.setLogPrefix(getLogPrefix());
-    }
-
-    /**
-     * Compare whether the name and format given "match" ours. Used in checking both against input attributes and as
-     * part of equality checking. For the sake of comparison null is considered to be the same as
-     * {@link Attribute#UNSPECIFIED}
-     * 
-     * @param otherSAMLName the name to compare against
-     * @param otherSAMLFormat the format to compare against
-     * @return whether there is a match.
-     */
-    protected boolean matches(@Nonnull @NotEmpty final String otherSAMLName, @Nullable final String otherSAMLFormat) {
-        if (!otherSAMLName.equals(theSAMLName)) {
-            log.debug("{} SAML attribute name {} does not match {}", getLogPrefix(), otherSAMLName, getId());
-            return false;
-        }
-
-        String format = otherSAMLFormat;
-        if (format != null && Attribute.UNSPECIFIED.equals(format)) {
-            format = null;
-        }
-
-        if (getAttributeFormat() != null && format != null && !getAttributeFormat().equals(format)
-                && !Attribute.UNSPECIFIED.equals(getAttributeFormat())) {
-            log.debug("{} SAML name format {} does not match {}", getLogPrefix(), format, getAttributeFormat());
-            return false;
-        }
-        return true;
-
-    }
-
-    /**
-     * Determines if the attribute matches the provided parameterisation.
-     * 
-     * @param attribute the attribute to consider
-     * @return whether it matches.
-     */
-    protected boolean attributeMatches(@Nonnull final InType attribute) {
-        return matches(attribute.getName(), attribute.getNameFormat());
-    }
-
-    /** {@inheritDoc} */
-    @Override @Nonnull @NonnullElements public Map<String,OutType> mapAttribute(@Nonnull final InType prototype) {
-
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-
-        if (!attributeMatches(prototype)) {
-            return Collections.emptyMap();
-        }
-
-        final List<XMLObject> inputValues = prototype.getAttributeValues();
-        final List<IdPAttributeValue<?>> outputValues = getValueMapper().decodeValues(inputValues);
-
-        if (!inputValues.isEmpty() && outputValues.isEmpty()) {
-            log.warn("{} Attribute '{}' value conversion yielded no suitable values", getLogPrefix(),
-                    prototype.getName());
-            return Collections.emptyMap();
-        }
-
-        final Map<String,OutType> output = new HashMap<>(inputValues.size());
-
-        log.debug("{} attribute id {} and aliases {} will be created", getLogPrefix(), getId(), getAttributeIds());
-
-        OutType out = newAttribute(prototype, getId());
-        out.setValues(outputValues);
-
-        for (final String id : attributeIds) {
-            out = newAttribute(prototype, getId());
-            out.setValues(outputValues);
-            output.put(id, out);
-        }
-        return output;
-    }
-
-    /**
-     * return a string which is to be prepended to all log messages.
-     * 
-     * @return "<type> Attribute Mapper '<mapper ID>' :"
-     */
-    @Nonnull @NotEmpty protected String getLogPrefix() {
-        // local cache of cached entry to allow unsynchronised clearing.
-        String prefix = logPrefix;
-        if (null == prefix) {
-            final StringBuilder builder = new StringBuilder();
-            if (null != getValueMapper()) {
-                builder.append(getValueMapper().getAttributeTypeName()).append(" ");
-            }
-
-            builder.append("Attribute Mapper '").append(getId()).append("':");
-            prefix = builder.toString();
-            if (null == logPrefix) {
-                logPrefix = prefix;
-            }
-        }
-        return prefix;
-    }
-
-    /**
-     * {@inheritDoc}. The identity is not part of equality of hash
-     */
-    @Override
-    public boolean equals(final Object obj) {
-        if (obj == null) {
-            return false;
-        }
-
-        if (obj == this) {
-            return true;
-        }
-
-        if (obj instanceof AbstractSAMLAttributeMapper) {
-            final AbstractSAMLAttributeMapper other = (AbstractSAMLAttributeMapper) obj;
-            return matches(other.getSAMLName(), other.getAttributeFormat())
-                    && java.util.Objects.equals(getAttributeIds(), other.getAttributeIds());
-        }
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}. The identity is not part of equality of hash
-     */
-    @Override
-    public int hashCode() {
-        String myFormat = getAttributeFormat();
-        if (null == myFormat) {
-            myFormat = Attribute.UNSPECIFIED;
-        }
-        return Objects.hashCode(myFormat, theSAMLName, attributeIds);
-    }
-
-    /**
-     * Function to summon up the output type based on an ID and an object of the input type. Typically the input type
-     * will be inspected for extra parameterisation.
-     * 
-     * @param input the input value to inspect.
-     * @param id the identifier of the new attribute.
-     * @return an output, suitable set up with per object information.
-     */
-    @Nonnull protected abstract OutType newAttribute(@Nonnull final InType input, @Nonnull @NotEmpty final String id);
-
-}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AbstractSAMLAttributeValueMapper.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AbstractSAMLAttributeValueMapper.java
deleted file mode 100644
index 1f1e78f..0000000
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AbstractSAMLAttributeValueMapper.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping;
-
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
-import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.xml.DOMTypeSupport;
-
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.schema.XSAny;
-import org.opensaml.core.xml.schema.XSBase64Binary;
-import org.opensaml.core.xml.schema.XSBoolean;
-import org.opensaml.core.xml.schema.XSDateTime;
-import org.opensaml.core.xml.schema.XSInteger;
-import org.opensaml.core.xml.schema.XSString;
-import org.opensaml.core.xml.schema.XSURI;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Objects;
-
-/**
- * The base of the classes that map SAML2 attribute values into IdP attribute values.
- */
-public abstract class AbstractSAMLAttributeValueMapper extends AbstractInitializableComponent {
-
-    /** logger. */
-    private final Logger log = LoggerFactory.getLogger(AbstractSAMLAttributeValueMapper.class);
-
-    /** The String used to prefix log message. */
-    private String logPrefix;
-
-    /**
-     * Convert from a list of the SAML Objects (usually a SAML2 AttributeValue) Value into the IDP Attributes.
-     * 
-     * @param inputs the list of SAML Attributes
-     * @return a list of IdP Attributes
-     */
-    @Nonnull @Unmodifiable public List<IdPAttributeValue<?>>
-            decodeValues(@Nonnull @NonnullElements final List<XMLObject> inputs) {
-        final List<IdPAttributeValue<?>> outputs = new ArrayList<>(inputs.size());
-
-        for (final XMLObject input : inputs) {
-            final IdPAttributeValue output = decodeValue(input);
-            if (null != output) {
-                outputs.add(output);
-            }
-        }
-        return Collections.unmodifiableList(outputs);
-
-    }
-
-    /**
-     * Function to return the string contents if this is the correct type.
-     * 
-     * @param object The object to inspect.
-     * @return Its contents suitably decoded. Returns null if we could not decode.
-     */
-    // Checkstyle: CyclomaticComplexity OFF
-    @Nullable protected String getStringValue(@Nonnull final XMLObject object) {
-        String retVal = null;
-
-        if (object instanceof XSString) {
-
-            retVal = ((XSString) object).getValue();
-
-        } else if (object instanceof XSURI) {
-
-            retVal = ((XSURI) object).getValue();
-
-        } else if (object instanceof XSBoolean) {
-
-            retVal = ((XSBoolean) object).getValue().getValue() ? "1" : "0";
-
-        } else if (object instanceof XSInteger) {
-
-            retVal = ((XSInteger) object).getValue().toString();
-
-        } else if (object instanceof XSDateTime) {
-
-            final Instant dt = ((XSDateTime) object).getValue();
-            if (dt != null) {
-                retVal = DOMTypeSupport.instantToString(dt);
-            } else {
-                retVal = null;
-            }
-
-        } else if (object instanceof XSBase64Binary) {
-
-            retVal = ((XSBase64Binary) object).getValue();
-
-        } else if (object instanceof XSAny) {
-
-            final XSAny wc = (XSAny) object;
-            if (wc.getUnknownAttributes().isEmpty() && wc.getUnknownXMLObjects().isEmpty()) {
-                retVal = wc.getTextContent();
-            } else {
-                retVal = null;
-            }
-        }
-
-        if (null == retVal) {
-            log.info("{} value of type {} could not be converted", getLogPrefix(), object.getClass().toString());
-        }
-        return retVal;
-    }
-
-    // Checkstyle: CyclomaticComplexity ON
-
-    /**
-     * Return a string which is to be prepended to all log messages. This is set by the enclosing AttributeMapper.
-     * 
-     * @return Returns the logPrefix.
-     */
-    @NonnullAfterInit public String getLogPrefix() {
-        return logPrefix;
-    }
-
-    /**
-     * Set the log prefix to use.
-     * 
-     * @param prefix The logPrefix to set.
-     */
-    public void setLogPrefix(@Nonnull final String prefix) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        logPrefix = Constraint.isNotNull(prefix, "prefix can not be null or empty");
-    }
-
-    /** {@inheritDoc} */
-    protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-        if (null == logPrefix) {
-            throw new ComponentInitializationException("No log prefix set");
-        }
-    }
-
-    /** {@inheritDoc} */
-    public boolean equals(final Object obj) {
-        if (obj == null) {
-            return false;
-        }
-
-        if (obj == this) {
-            return true;
-        }
-
-        if (obj instanceof AbstractSAMLAttributeValueMapper) {
-            final AbstractSAMLAttributeValueMapper other = (AbstractSAMLAttributeValueMapper) obj;
-
-            return getClass().equals(other.getClass());
-        }
-        return false;
-    }
-
-    /** {@inheritDoc} */
-    public int hashCode() {
-        return Objects.hashCode(getClass());
-    }
-
-    /**
-     * Function to decode a single {@link XMLObject} into an {@link IdPAttributeValue}.
-     * 
-     * @param object the object to decode
-     * @return the returned final {@link IdPAttributeValue} or null if decoding failed
-     */
-    @Nullable protected abstract IdPAttributeValue decodeValue(@Nonnull final XMLObject object);
-
-    /**
-     * Return the output type (for logging).
-     * 
-     * @return the output type.
-     */
-    @Nonnull protected abstract String getAttributeTypeName();
-
-}
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AbstractSAMLAttributesMapper.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AbstractSAMLAttributesMapper.java
deleted file mode 100644
index 61e88bb..0000000
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AbstractSAMLAttributesMapper.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.saml.saml2.core.Attribute;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Supplier;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
-
-import net.shibboleth.idp.attribute.AttributeEncoder;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.resolver.AttributeResolver;
-import net.shibboleth.idp.saml.attribute.encoding.AttributeMapperProcessor;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.collection.Pair;
-import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-
-/**
- * The class contains the mechanics to go from a list of {@link Attribute}s (or derived) to a {@link Multimap} of
- * {@link String},{@link IdPAttribute} (or derived, or null). The representation as a {@link Multimap} is useful for
- * filtering situations and is exploited by AttributeInMetadata filter.
- * 
- * @param <InType> the type which is to be inspected and mapped
- * @param <OutType> some sort of representation of an IdP attribute
- */
-
-public abstract class AbstractSAMLAttributesMapper<InType extends Attribute, OutType extends IdPAttribute> extends
-        AbstractIdentifiableInitializableComponent implements AttributesMapper<InType, OutType> {
-
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractSAMLAttributesMapper.class);
-
-    /** The mappers we can apply. */
-    @Nonnull @NonnullElements private Collection<AttributeMapper<InType,OutType>> mappers = Collections.EMPTY_LIST;
-
-    /** The String used to prefix log message. */
-    @Nullable private String logPrefix;
-
-    /** Default Constructor. */
-    public AbstractSAMLAttributesMapper() {
-        
-    }
-    
-    /**
-     * Constructor to create the mapping from an existing resolver. <br/>
-     * This code inverts the {@link AttributeEncoder} (internal attribute -> SAML Attributes) into
-     * {@link AttributeMapper} (SAML [RequestedAttributes] -> internal [Requested] Attributes). <br/>
-     * to generate the {@link AbstractSAMLAttributeMapper} (with no
-     * {@link AbstractSAMLAttributeMapper#getAttributeIds()}. These are accumulated into a {@link Multimap}, where the
-     * key is the {@link AbstractSAMLAttributeMapper} and the values are the (IdP) attribute names. The collection of
-     * {@link AttributeMapper}s can then be extracted from the map, and the appropriate internal names added (these
-     * being the value of the {@link Multimap})
-     * 
-     * @param resolver The resolver
-     * @param id The it
-     * @param mapperFactory A factory to generate new mappers of the correct type.
-     */
-    public AbstractSAMLAttributesMapper(@Nonnull final AttributeResolver resolver, @Nonnull @NotEmpty final String id,
-            @Nonnull final Supplier<AbstractSAMLAttributeMapper<InType,OutType>> mapperFactory) {
-
-        setId(id); 
-
-        final Multimap<AbstractSAMLAttributeMapper<InType,OutType>,String> theMappers;
-
-        theMappers = HashMultimap.create();
-
-        for (final Pair<String, AttributeEncoder<?>> pair : resolver.getAllEncoders()) {
-            if (pair.getSecond() instanceof AttributeMapperProcessor) {
-                // There is an appropriate reverse mapper
-                final AttributeMapperProcessor factory = (AttributeMapperProcessor) pair.getSecond();
-                final AbstractSAMLAttributeMapper<InType,OutType> mapper = mapperFactory.get();
-                factory.populateAttributeMapper(mapper);
-                theMappers.put(mapper, pair.getFirst());
-            }
-        }
-
-        mappers = new ArrayList<>(theMappers.values().size());
-
-        for (final Entry<AbstractSAMLAttributeMapper<InType,OutType>,Collection<String>> entry : theMappers.asMap()
-                .entrySet()) {
-
-            final AbstractSAMLAttributeMapper<InType, OutType> mapper = entry.getKey();
-            mapper.setAttributeIds(new ArrayList<>(entry.getValue()));
-            mappers.add(mapper);
-        }
-    }
-
-    /**
-     * Get the mappers.
-     * 
-     * @return Returns the mappers.
-     */
-    @Nonnull @NonnullElements public Collection<AttributeMapper<InType,OutType>> getMappers() {
-        return mappers;
-    }
-
-    /**
-     * Set the attribute mappers into the lookup map.
-     * 
-     * @param theMappers The mappers to set.
-     */
-    public void setMappers(@Nonnull final Collection<AttributeMapper<InType,OutType>> theMappers) {
-        mappers = Constraint.isNotNull(theMappers, "mappers list must be non null");
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @Nonnull @NonnullElements public Multimap<String,OutType> mapAttributes(
-            @Nonnull @NonnullElements final List<InType> prototypes) {
-
-        final Multimap<String,OutType> result = ArrayListMultimap.create();
-
-        for (final InType prototype : prototypes) {
-            for (final AttributeMapper<InType,OutType> mapper : mappers) {
-
-                final Map<String,OutType> mappedAttributes;
-                try {
-                    mappedAttributes = mapper.mapAttribute(prototype); 
-                } catch (final Exception e) {
-                    log.error("{} Attempt to map SAML attribute '{}' with mapper '{}' failed: ", getLogPrefix(),
-                            prototype.getName(),mapper.getId(), e);
-                    throw e;
-                }
-
-                log.debug("{} SAML attribute '{}' mapped to {} attributes by mapper '{}'", getLogPrefix(),
-                        prototype.getName(), mappedAttributes.size(), mapper.getId());
-
-                for (final Entry<String,OutType> entry : mappedAttributes.entrySet()) {
-                    result.put(entry.getKey(), entry.getValue());
-                }
-            }
-        }
-        return result;
-    }
-
-    /** {@inheritDoc} */
-    @Override protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-        logPrefix = null;
-        for (final AttributeMapper mapper : mappers) {
-            ComponentSupport.initialize(mapper);
-        }
-    }
-
-    /**
-     * Return a string which is to be prepended to all log messages.
-     * 
-     * @return "Attribute Mappers '<ID>' :"
-     */
-    @Nonnull @NotEmpty private String getLogPrefix() {
-        String s = logPrefix;
-        if (null == s) {
-            s = new StringBuilder("Attribute Mappers : '").append(getId()).append("':").toString();
-            logPrefix = s;
-        }
-        return s;
-    }
-}
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AttributeMapper.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AttributeMapper.java
deleted file mode 100644
index 45ee2a3..0000000
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AttributeMapper.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping;
-
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.component.IdentifiedComponent;
-
-/**
- * This attribute defines the mechanism to go from something into an (IdP) {@link IdPAttribute}. <br/>
- * Implementations of this interface will be paired with implementations of {@link AttributesMapper}.
- * 
- * @param <InType> the type which is to be inspected and mapped
- * @param <OutType> some sort of representation of an IdP attribute
- */
-public interface AttributeMapper<InType,OutType extends IdPAttribute> extends IdentifiedComponent{
-
-    /**
-     * Map the SAML attribute to the required output type.
-     * 
-     * <p>If the input contains values to be decoded, failure to decode any values results in a failure
-     * to map the input at all.</p>
-     * 
-     * @param input the SAML attribute
-     * @return the appropriate map of names to output type
-     * 
-     */
-    @Nonnull @NonnullElements Map<String,OutType> mapAttribute(@Nonnull final InType input);
-
-}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AttributesMapper.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AttributesMapper.java
deleted file mode 100644
index e3cabc0..0000000
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AttributesMapper.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping;
-
-import java.util.List;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-
-import com.google.common.collect.Multimap;
-
-/**
- * This attribute defines the mechanism to go from a list of objects into a collection (IdP) {@link IdPAttribute}, the
- * representation (a {@link Multimap}) being such as is useful to attribute filtering. <br/>
- * Implementations of this interface will be paired with implementations of {@link AttributeMapper}.
- * 
- * @param <InType> the type which is to be inspected and mapped
- * @param <OutType> some sort of representation of an IdP attribute
- */
-public interface AttributesMapper<InType,OutType extends IdPAttribute> {
-
-    /**
-     * Map the input objects into IdP attributes.
-     * 
-     * @param prototypes the SAML attributes
-     * 
-     * @return a map from IdP AttributeId to RequestedAttributes
-     */
-    @Nonnull @NonnullElements Multimap<String,OutType> mapAttributes(
-            @Nonnull @NonnullElements final List<InType> prototypes);
-    
-}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAML1AttributeTranscoder.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAML1AttributeTranscoder.java
new file mode 100644
index 0000000..0171c0f
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAML1AttributeTranscoder.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.attribute.AttributeDecodingException;
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.IdPRequestedAttribute;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.saml.xml.SAMLConstants;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml1.core.Attribute;
+import org.opensaml.saml.saml1.core.AttributeDesignator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Strings;
+
+/**
+ * Base class for transcoders that operate on a SAML 1 {@link Attribute} or {@link AttributeDesignator}.
+ * 
+ * @param <EncodedType> the type of data that can be handled by the transcoder
+ */
+public abstract class AbstractSAML1AttributeTranscoder<EncodedType extends IdPAttributeValue>
+        extends AbstractSAMLAttributeTranscoder<AttributeDesignator,EncodedType>
+        implements SAML1AttributeTranscoder<EncodedType> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractSAML1AttributeTranscoder.class);
+    
+    /** Builder used to construct {@link Attribute} objects. */
+    @Nonnull private final SAMLObjectBuilder<Attribute> attributeBuilder;
+
+    /** Builder used to construct {@link AttributeDesignator} objects. */
+    @Nonnull private final SAMLObjectBuilder<AttributeDesignator> designatorBuilder;
+    
+    /** Constructor. */
+    public AbstractSAML1AttributeTranscoder() {
+        attributeBuilder = (SAMLObjectBuilder<Attribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<Attribute>getBuilderOrThrow(
+                        Attribute.TYPE_NAME);
+        designatorBuilder = (SAMLObjectBuilder<AttributeDesignator>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<AttributeDesignator>getBuilderOrThrow(
+                        AttributeDesignator.TYPE_NAME);
+    }
+
+    /** {@inheritDoc} */
+    @Nonnull public Class<AttributeDesignator> getEncodedType() {
+        return AttributeDesignator.class;
+    }
+    
+    /** {@inheritDoc} */
+    @Nullable public String getEncodedName(@Nonnull final TranscodingRule rule) {
+        
+        try {
+            // SAML 1 naming should be based on only what needs to be available from the properties alone.
+            return new NamingFunction().apply(buildAttribute(null, null, AttributeDesignator.class, rule,
+                    Collections.emptyList()));
+        } catch (final AttributeEncodingException e) {
+            return null;
+        }
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull protected AttributeDesignator buildAttribute(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nullable final IdPAttribute attribute, @Nonnull final Class<? extends AttributeDesignator> to,
+            @Nonnull final TranscodingRule rule, @Nonnull @NonnullElements final List<XMLObject> attributeValues)
+                    throws AttributeEncodingException {
+
+        if (attribute != null && !attribute.getValues().isEmpty() && attributeValues.isEmpty()) {
+            throw new AttributeEncodingException("Failed to encode any values for attribute " + attribute.getId());
+        }
+
+        final String name = rule.get(PROP_NAME, String.class);
+        if (Strings.isNullOrEmpty(name)) {
+            throw new AttributeEncodingException("Required transcoder property 'name' not found");
+        }
+
+        final AttributeDesignator samlAttribute;
+        
+        if (to.equals(Attribute.class)) {
+            if (attributeValues.isEmpty()) {
+                throw new AttributeEncodingException("Unable to encode a SAML 1 Attribute with no values");
+            }
+            
+            samlAttribute = attributeBuilder.buildObject();
+            ((Attribute) samlAttribute).getAttributeValues().addAll(attributeValues);
+        } else if (to.equals(AttributeDesignator.class)) {
+            samlAttribute = designatorBuilder.buildObject();
+            if (!attributeValues.isEmpty()) {
+                log.warn("Lossy conversion to AttributeDesignator");
+            }
+        } else {
+            throw new AttributeEncodingException("Unsupported target object type: " + to.getName());
+        }
+
+        samlAttribute.setAttributeName(name);
+        samlAttribute.setAttributeNamespace(
+                rule.getOrDefault(PROP_NAMESPACE, String.class, SAMLConstants.SAML1_ATTR_NAMESPACE_URI));
+        
+        return samlAttribute;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull protected IdPAttribute buildIdPAttribute(
+            @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final AttributeDesignator attribute,
+            @Nonnull final TranscodingRule rule,
+            @Nonnull @NonnullElements final List<IdPAttributeValue<?>> attributeValues)
+                    throws AttributeDecodingException {
+        
+        final String id = rule.get(AttributeTranscoderRegistry.PROP_ID, String.class);
+        if (Strings.isNullOrEmpty(id)) {
+            throw new AttributeDecodingException("Required transcoder property 'id' not found");
+        }
+        
+        final IdPAttribute idpAttribute;
+        if (attribute instanceof Attribute) {
+            if (!((Attribute) attribute).getAttributeValues().isEmpty() && attributeValues.isEmpty()) {
+                throw new AttributeDecodingException("Failed to decode any values for attribute "
+                        + attribute.getAttributeName());
+            }
+            idpAttribute = new IdPAttribute(id);
+        } else {
+            idpAttribute = new IdPRequestedAttribute(id);
+        }
+        
+        idpAttribute.setValues(attributeValues);
+        
+        return idpAttribute;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull protected Iterable<XMLObject> getValues(@Nonnull final AttributeDesignator input) {
+        return input instanceof Attribute ? ((Attribute) input).getAttributeValues() : Collections.emptyList();
+    }
+
+    /** A function to produce a "canonical" name for a SAML 1 {@link AttributeDesignator} for transcoding rules. */
+    public static class NamingFunction implements Function<AttributeDesignator,String> {
+
+        /** {@inheritDoc} */
+        @Nullable public String apply(@Nullable final AttributeDesignator input) {
+            
+            if (input == null || input.getAttributeName() == null || input.getAttributeNamespace() == null) {
+                return null;
+            }
+            
+            final StringBuilder builder = new StringBuilder();
+            builder.append("SAML1:{").append(input.getAttributeNamespace()).append('}')
+                .append(input.getAttributeName());
+            return builder.toString();
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAML2AttributeTranscoder.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAML2AttributeTranscoder.java
new file mode 100644
index 0000000..52e2b8d
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAML2AttributeTranscoder.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.attribute.AttributeDecodingException;
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.IdPRequestedAttribute;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.metadata.RequestedAttribute;
+
+import com.google.common.base.Strings;
+
+/**
+ * Base class for transcoders that operate on a SAML 2 {@link Attribute} or {@link RequestedAttribute}.
+ * 
+ * @param <EncodedType> the type of data that can be handled by the transcoder
+ */
+public abstract class AbstractSAML2AttributeTranscoder<EncodedType extends IdPAttributeValue> extends
+        AbstractSAMLAttributeTranscoder<Attribute,EncodedType> implements SAML2AttributeTranscoder<EncodedType> {
+    
+    /** Builder used to construct {@link Attribute} objects. */
+    @Nonnull private final SAMLObjectBuilder<Attribute> attributeBuilder;
+
+    /** Builder used to construct {@link RequestedAttribute} objects. */
+    @Nonnull private final SAMLObjectBuilder<RequestedAttribute> reqAttributeBuilder;
+    
+    /** Constructor. */
+    public AbstractSAML2AttributeTranscoder() {
+        attributeBuilder = (SAMLObjectBuilder<Attribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<Attribute>getBuilderOrThrow(
+                        Attribute.TYPE_NAME);
+        reqAttributeBuilder = (SAMLObjectBuilder<RequestedAttribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<RequestedAttribute>getBuilderOrThrow(
+                        RequestedAttribute.TYPE_NAME);
+    }
+
+    /** {@inheritDoc} */
+    @Nonnull public Class<Attribute> getEncodedType() {
+        return Attribute.class;
+    }
+    
+    /** {@inheritDoc} */
+    @Nullable public String getEncodedName(@Nonnull final TranscodingRule rule) {
+        
+        try {
+            // SAML 2 naming should be based on only what needs to be available from the properties alone.
+            return new NamingFunction().apply(buildAttribute(null, null, Attribute.class, rule,
+                    Collections.emptyList()));
+        } catch (final AttributeEncodingException e) {
+            return null;
+        }
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull protected Attribute buildAttribute(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nullable final IdPAttribute attribute, @Nonnull final Class<? extends Attribute> to,
+            @Nonnull final TranscodingRule rule, @Nonnull @NonnullElements final List<XMLObject> attributeValues)
+                    throws AttributeEncodingException {
+
+        if (attribute != null && !attribute.getValues().isEmpty() && attributeValues.isEmpty()) {
+            throw new AttributeEncodingException("Failed to encode any values for attribute " + attribute.getId());
+        }
+        
+        final String name = rule.get(PROP_NAME, String.class);
+        if (Strings.isNullOrEmpty(name)) {
+            throw new AttributeEncodingException("Required transcoder property 'name' not found");
+        }
+
+        final Attribute samlAttribute;
+        
+        if (to.equals(Attribute.class)) {
+            samlAttribute = attributeBuilder.buildObject();
+        } else if (to.equals(RequestedAttribute.class)) {
+            samlAttribute = reqAttributeBuilder.buildObject();
+            if (attribute instanceof IdPRequestedAttribute) {
+                ((RequestedAttribute) samlAttribute).setIsRequired(((IdPRequestedAttribute) attribute).getIsRequired());
+            }
+        } else {
+            throw new AttributeEncodingException("Unsupported target object type: " + to.getName());
+        }
+
+        samlAttribute.setName(name);
+        samlAttribute.setNameFormat(rule.getOrDefault(PROP_NAME_FORMAT, String.class, Attribute.URI_REFERENCE));
+        samlAttribute.getAttributeValues().addAll(attributeValues);
+        
+        final String friendlyName = rule.getOrDefault(PROP_FRIENDLY_NAME, String.class,
+                attribute != null ? attribute.getId() : "");
+        if (!friendlyName.isBlank()) {
+            samlAttribute.setFriendlyName(friendlyName);
+        }
+        
+        return samlAttribute;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull protected IdPAttribute buildIdPAttribute(
+            @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final Attribute attribute,
+            @Nonnull final TranscodingRule rule,
+            @Nonnull @NonnullElements final List<IdPAttributeValue<?>> attributeValues)
+                    throws AttributeDecodingException {
+        
+        if (!attribute.getAttributeValues().isEmpty() && attributeValues.isEmpty()) {
+            throw new AttributeDecodingException("Failed to decode any values for attribute " + attribute.getName());
+        }
+        
+        final String id = rule.get(AttributeTranscoderRegistry.PROP_ID, String.class);
+        if (Strings.isNullOrEmpty(id)) {
+            throw new AttributeDecodingException("Required transcoder property 'id' not found");
+        }
+        
+        final IdPAttribute idpAttribute;
+        if (attribute instanceof RequestedAttribute) {
+            idpAttribute = new IdPRequestedAttribute(id);
+            ((IdPRequestedAttribute) idpAttribute).setRequired(((RequestedAttribute) attribute).isRequired());
+        } else {
+            idpAttribute = new IdPAttribute(id);
+        }
+        
+        idpAttribute.setValues(attributeValues);
+        
+        return idpAttribute;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull protected Iterable<XMLObject> getValues(@Nonnull final Attribute input) {
+        return input.getAttributeValues();
+    }
+
+    /**
+     * A function to produce a "canonical" name for a SAML 2.0 {@link Attribute} for transcoding rules.
+     */
+    public static class NamingFunction implements Function<Attribute,String> {
+
+        /** {@inheritDoc} */
+        @Nullable public String apply(@Nullable final Attribute input) {
+            
+            if (input == null || input.getName() == null) {
+                return null;
+            }
+            
+            String format = input.getNameFormat();
+            if (format == null) {
+                format = Attribute.UNSPECIFIED;
+            }
+            
+            final StringBuilder builder = new StringBuilder();
+            builder.append("SAML2:{").append(format).append('}').append(input.getName());
+            return builder.toString();
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAMLAttributeTranscoder.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAMLAttributeTranscoder.java
new file mode 100644
index 0000000..df99f1b
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AbstractSAMLAttributeTranscoder.java
@@ -0,0 +1,285 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.attribute.AttributeDecodingException;
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AbstractAttributeTranscoder;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.saml.xmlobject.ScopedValue;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.xml.DOMTypeSupport;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.core.xml.schema.XSBase64Binary;
+import org.opensaml.core.xml.schema.XSBoolean;
+import org.opensaml.core.xml.schema.XSDateTime;
+import org.opensaml.core.xml.schema.XSInteger;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.core.xml.schema.XSURI;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.common.SAMLObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Base class for transcoders that support SAML attributes.
+ * 
+ * @param <AttributeType> type of object produced
+ * @param <EncodedType> the type of data that can be handled by the transcoder
+ */
+public abstract class AbstractSAMLAttributeTranscoder<AttributeType extends SAMLObject,
+        EncodedType extends IdPAttributeValue> extends AbstractAttributeTranscoder<AttributeType>
+        implements SAMLAttributeTranscoder<AttributeType> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractSAMLAttributeTranscoder.class);
+        
+    /** {@inheritDoc} */
+    @Override
+    @Nullable public AttributeType doEncode(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final IdPAttribute attribute, @Nonnull final Class<? extends AttributeType> to,
+            @Nonnull final TranscodingRule rule) throws AttributeEncodingException {
+
+        final String attributeId = attribute.getId();
+
+        log.debug("Beginning to encode attribute {}", attributeId);
+
+        final List<XMLObject> samlAttributeValues = new ArrayList<>();
+
+        for (final IdPAttributeValue o : attribute.getValues()) {
+            if (o == null) {
+                // filtered out upstream leave in test for sanity
+                log.debug("Skipping null value of attribute {}", attributeId);
+                continue;
+            }
+
+            if (!canEncodeValue(attribute, o)) {
+                log.warn("Skipping value of attribute '{}'; Type {} cannot be encoded by this encoder ({}).",
+                        attributeId, o.getClass().getSimpleName(), this.getClass().getSimpleName());
+                continue;
+            }
+
+            final EncodedType attributeValue = (EncodedType) o;
+            final XMLObject samlAttributeValue =
+                    encodeValue(profileRequestContext, attribute, rule, attributeValue);
+            if (samlAttributeValue == null) {
+                log.debug("Skipping null value for attribute {}", attributeId);
+            } else {
+                samlAttributeValues.add(samlAttributeValue);
+            }
+        }
+        
+        log.debug("Encoded {} values for attribute {}", samlAttributeValues.size(), attributeId);
+        return buildAttribute(profileRequestContext, attribute, to, rule, samlAttributeValues);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable public IdPAttribute doDecode(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final AttributeType input, @Nonnull final TranscodingRule rule)
+                    throws AttributeDecodingException {
+
+        final String attributeName = getEncodedName(rule);
+        
+        log.debug("Beginning to decode attribute {}", attributeName);
+
+        final List<IdPAttributeValue<?>> idpAttributeValues = new ArrayList<>();
+        final Iterable<XMLObject> samlAttributeValues = getValues(input);
+
+        for (final XMLObject o : samlAttributeValues) {
+            if (o == null) {
+                // filtered out upstream leave in test for sanity
+                log.debug("Skipping null value of attribute {}", attributeName);
+                continue;
+            }
+
+            final IdPAttributeValue<?> idpAttributeValue = decodeValue(profileRequestContext, input, rule, o);
+            if (idpAttributeValue == null) {
+                log.debug("Unable to decode value of attribute {}", attributeName);
+            } else {
+                idpAttributeValues.add(idpAttributeValue);
+            }
+        }
+
+        if (!idpAttributeValues.isEmpty()) {
+            log.debug("Decoded {} values for attribute {}", idpAttributeValues.size(), attributeName);
+        }
+        return buildIdPAttribute(profileRequestContext, input, rule, idpAttributeValues);
+    }
+
+    /**
+     * Function to return an XML object in string form.
+     * 
+     * @param object object to decode
+     * 
+     * @return decoded string, or null
+     */
+// Checkstyle: CyclomaticComplexity OFF
+    @Nullable protected String getStringValue(@Nonnull final XMLObject object) {
+        String retVal = null;
+
+        if (object instanceof XSString) {
+
+            retVal = ((XSString) object).getValue();
+
+        } else if (object instanceof XSURI) {
+
+            retVal = ((XSURI) object).getValue();
+
+        } else if (object instanceof XSBoolean) {
+
+            retVal = ((XSBoolean) object).getValue().getValue() ? "1" : "0";
+
+        } else if (object instanceof XSInteger) {
+
+            retVal = ((XSInteger) object).getValue().toString();
+
+        } else if (object instanceof XSDateTime) {
+
+            final Instant dt = ((XSDateTime) object).getValue();
+            if (dt != null) {
+                retVal = DOMTypeSupport.instantToString(dt);
+            } else {
+                retVal = null;
+            }
+
+        } else if (object instanceof XSBase64Binary) {
+
+            retVal = ((XSBase64Binary) object).getValue();
+            
+        } else if (object instanceof ScopedValue) {
+            
+            retVal = ((ScopedValue) object).getValue();
+
+        } else if (object instanceof XSAny) {
+
+            final XSAny wc = (XSAny) object;
+            if (wc.getUnknownAttributes().isEmpty() && wc.getUnknownXMLObjects().isEmpty()) {
+                retVal = wc.getTextContent();
+            } else {
+                retVal = null;
+            }
+        }
+
+        if (null == retVal) {
+            log.info("Value of type {} could not be converted", object.getClass().getSimpleName());
+        }
+        return retVal;
+    }
+// Checkstyle: CyclomaticComplexity ON
+
+    /**
+     * Checks if the given value can be handled by the transcoder.
+     * 
+     * <p>In many cases this is simply a check to see if the given object is of the right type.</p>
+     * 
+     * @param idpAttribute the attribute being encoded, never null
+     * @param value the value to check, never null
+     * 
+     * @return true if the transcoder can encode this value, false if not
+     */
+    protected abstract boolean canEncodeValue(@Nonnull final IdPAttribute idpAttribute,
+            @Nonnull final IdPAttributeValue value);
+    
+    /**
+     * Builds a SAML attribute element from the given attribute values.
+     * 
+     * @param profileRequestContext current profile request
+     * @param attribute the attribute being encoded
+     * @param to target type to create
+     * @param rule properties to control encoding
+     * @param attributeValues the encoded values for the attribute
+     * 
+     * @return the SAML attribute object
+     * 
+     * @throws AttributeEncodingException thrown if there is a problem constructing the SAML attribute
+     */
+    @Nonnull protected abstract AttributeType buildAttribute(
+            @Nullable final ProfileRequestContext profileRequestContext, @Nullable final IdPAttribute attribute,
+            @Nonnull final Class<? extends AttributeType> to, @Nonnull final TranscodingRule rule,
+            @Nonnull @NonnullElements final List<XMLObject> attributeValues) throws AttributeEncodingException;
+
+    /**
+     * Encodes an attribute value into a SAML AttributeValue element.
+     * 
+     * @param profileRequestContext current profile request
+     * @param attribute the attribute being encoded
+     * @param rule properties to control encoding
+     * @param value the value to encode
+     * 
+     * @return the attribute value or null if the resulting attribute value would be empty
+     * 
+     * @throws AttributeEncodingException thrown if there is a problem encoding the attribute value
+     */
+    @Nullable protected abstract XMLObject encodeValue(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final IdPAttribute attribute, @Nonnull final TranscodingRule rule,
+            @Nonnull final EncodedType value) throws AttributeEncodingException;
+
+    /**
+     * Builds an {@link IdPAttribute} from the given values.
+     * 
+     * @param profileRequestContext current profile request
+     * @param attribute the attribute being decoded
+     * @param rule properties to control decoding
+     * @param attributeValues the decoded values for the attribute
+     * 
+     * @return the IdPAttribute object
+     * 
+     * @throws AttributeDecodingException thrown if there is a problem constructing the IdPAttribute
+     */
+    @Nonnull protected abstract IdPAttribute buildIdPAttribute(
+            @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final AttributeType attribute,
+            @Nonnull final TranscodingRule rule,
+            @Nonnull @NonnullElements final List<IdPAttributeValue<?>> attributeValues)
+                    throws AttributeDecodingException;
+    
+    /**
+     * Returns the values to decode from the concrete input object.
+     * 
+     * @param input input object
+     * 
+     * @return values to decode
+     */
+    @Nonnull protected abstract Iterable<XMLObject> getValues(@Nonnull final AttributeType input);
+    
+    /**
+     * Function to decode a single {@link XMLObject} into an {@link IdPAttributeValue}.
+     * 
+     * @param profileRequestContext current profile request
+     * @param attribute the attribute being decoded
+     * @param rule properties to control decoding
+     * @param value the value to decode
+     * 
+     * @return the returned final {@link IdPAttributeValue} or null if decoding failed
+     */
+    @Nullable protected abstract IdPAttributeValue<?> decodeValue(
+            @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final AttributeType attribute,
+            @Nonnull final TranscodingRule rule, @Nullable final XMLObject value);
+
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AttributesMapContainer.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AttributesMapContainer.java
similarity index 74%
rename from idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AttributesMapContainer.java
rename to idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AttributesMapContainer.java
index 3c7bb17..d27a1df 100644
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/mapping/AttributesMapContainer.java
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/AttributesMapContainer.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.saml.attribute.mapping;
+package net.shibboleth.idp.saml.attribute.transcoding;
 
 import net.shibboleth.idp.attribute.IdPAttribute;
 
@@ -23,28 +23,28 @@ import com.google.common.base.Supplier;
 import com.google.common.collect.Multimap;
 
 /**
- * Container for reverse mapped attributes. This gives us a distinguished class to look for in the
+ * Container for decoded attributes. This gives us a distinguished class to look for in the
  * {@link org.opensaml.core.xml.XMLObject#getObjectMetadata()}.
  * 
- * @param <OutType> The type of attribute we are mapping to.
+ * @param <OutType> The type of object decoded
  */
-public class AttributesMapContainer<OutType extends IdPAttribute> implements Supplier<Multimap<String, OutType>> {
+public class AttributesMapContainer<OutType extends IdPAttribute> implements Supplier<Multimap<String,OutType>> {
 
     /** The map we are encapsulating.*/
-    private final Multimap<String, OutType> providedValue;
+    private final Multimap<String,OutType> providedValue;
 
     /**
      * Constructor.
      * 
      * @param value the value to return.
      */
-    public AttributesMapContainer(final Multimap<String, OutType> value) {
+    public AttributesMapContainer(final Multimap<String,OutType> value) {
         providedValue = value;
     }
 
     /** {@inheritDoc} */
-    @Override public Multimap<String, OutType> get() {
+    @Override public Multimap<String,OutType> get() {
         return providedValue;
     }
 
-}
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML1AttributeDesignatorMapper.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAML1AttributeTranscoder.java
similarity index 61%
rename from idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML1AttributeDesignatorMapper.java
rename to idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAML1AttributeTranscoder.java
index 990e0ce..0749595 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML1AttributeDesignatorMapper.java
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAML1AttributeTranscoder.java
@@ -15,24 +15,25 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.saml.attribute.mapping.impl;
+package net.shibboleth.idp.saml.attribute.transcoding;
 
 import javax.annotation.Nonnull;
 
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeDesignatorMapper;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 
+import org.opensaml.saml.saml1.core.Attribute;
 import org.opensaml.saml.saml1.core.AttributeDesignator;
 
-/** A class that maps a SAML1 {@link AttributeDesignator} into an IdP {@link IdPAttribute}. */
-public class SAML1AttributeDesignatorMapper extends AbstractSAMLAttributeDesignatorMapper<IdPAttribute> {
-    
-    /** {@inheritDoc} */
-    @Override
-    @Nonnull protected IdPAttribute newAttribute(@Nonnull final AttributeDesignator input,
-            @Nonnull @NotEmpty final String id) {
-        return new IdPAttribute(id);
-    }
-    
+/**
+ * Marker interface for transcoders that operate on a SAML 1 {@link Attribute} or {@link AttributeDesignator}.
+ * 
+ * @param <EncodedType> the type of data that can be handled by the transcoder
+ */
+public interface SAML1AttributeTranscoder<EncodedType extends IdPAttributeValue> extends
+        SAMLAttributeTranscoder<AttributeDesignator> {
+
+    /** The namespace of the attribute name. */
+    @Nonnull @NotEmpty static final String PROP_NAMESPACE = "namespace";
+
 }
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/SAML2AttributeEncoder.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAML2AttributeTranscoder.java
similarity index 57%
rename from idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/SAML2AttributeEncoder.java
rename to idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAML2AttributeTranscoder.java
index 98fdc4c..c36df8d 100644
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/SAML2AttributeEncoder.java
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAML2AttributeTranscoder.java
@@ -15,33 +15,28 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.saml.attribute.encoding;
+package net.shibboleth.idp.saml.attribute.transcoding;
+
+import javax.annotation.Nonnull;
 
-import net.shibboleth.idp.attribute.AttributeEncoder;
 import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 
 import org.opensaml.saml.saml2.core.Attribute;
-
+import org.opensaml.saml.saml2.metadata.RequestedAttribute;
 
 /**
- * Interface for encoders that produce a SAML 2 {@link Attribute}.
+ * Marker interface for transcoders that operate on a SAML 2 {@link Attribute} or {@link RequestedAttribute}.
  * 
- * @param <EncodedType> the type of data that can be encoded by the encoder
+ * @param <EncodedType> the type of data that can be handled by the transcoder
  */
-public interface SAML2AttributeEncoder<EncodedType extends IdPAttributeValue> extends AttributeEncoder<Attribute> {
-
-    /**
-     * Get the encoded Name of the attribute.
-     * 
-     * @return name of the attribute
-     */
-    @NonnullAfterInit String getName();
- 
-    /**
-     * Get the encoded NameFormat of the attribute.
-     * 
-     * @return name format of the attribute
-     */
-    @NonnullAfterInit String getNameFormat();
+public interface SAML2AttributeTranscoder<EncodedType extends IdPAttributeValue> extends
+        SAMLAttributeTranscoder<Attribute> {
+
+    /** A friendly, human readable, name for the attribute. */
+    @Nonnull @NotEmpty static final String PROP_FRIENDLY_NAME = "friendlyName";
+
+    /** The format of the attribute name. */
+    @Nonnull @NotEmpty static final String PROP_NAME_FORMAT = "nameFormat";
+
 }
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML2AttributeMapper.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAMLAttributeTranscoder.java
similarity index 60%
rename from idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML2AttributeMapper.java
rename to idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAMLAttributeTranscoder.java
index 3c0f3cc..80a4404 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML2AttributeMapper.java
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAMLAttributeTranscoder.java
@@ -15,23 +15,26 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.saml.attribute.mapping.impl;
+package net.shibboleth.idp.saml.attribute.transcoding;
 
 import javax.annotation.Nonnull;
 
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeMapper;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 
-import org.opensaml.saml.saml2.core.Attribute;
-
-/** A class that maps a SAML2 {@link Attribute} into an IdP {@link IdPAttribute}. */
-public class SAML2AttributeMapper extends AbstractSAMLAttributeMapper<Attribute, IdPAttribute> {
-    
-    /** {@inheritDoc} */
-    @Override
-    @Nonnull protected IdPAttribute newAttribute(@Nonnull final Attribute input, @Nonnull @NotEmpty final String id) {
-        return new IdPAttribute(id);
-    }
-    
+import org.opensaml.saml.common.SAMLObject;
+
+/**
+ * Marker interface for transcoders that support SAML objects.
+ * 
+ * @param <AttributeType> type of object produced
+ */
+public interface SAMLAttributeTranscoder<AttributeType extends SAMLObject> extends AttributeTranscoder<AttributeType> {
+
+    /** The attribute name. */
+    @Nonnull @NotEmpty static final String PROP_NAME = "name";
+
+    /** Whether to encode the xsi:type. */
+    @Nonnull @NotEmpty static final String PROP_ENCODE_TYPE = "encodeType";
+
 }
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/SAMLEncoderSupport.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAMLEncoderSupport.java
similarity index 99%
rename from idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/SAMLEncoderSupport.java
rename to idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAMLEncoderSupport.java
index b14649a..d299388 100644
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/SAMLEncoderSupport.java
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAMLEncoderSupport.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.saml.attribute.encoding;
+package net.shibboleth.idp.saml.attribute.transcoding;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/package-info.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/package-info.java
similarity index 83%
rename from idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/package-info.java
rename to idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/package-info.java
index 69b6f2e..4c9f4e9 100644
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/encoding/package-info.java
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/package-info.java
@@ -16,7 +16,7 @@
  */
 
 /** 
- * Base classes for encoding {@link net.shibboleth.idp.attribute.IdPAttribute} objects into SAML message components. 
+ * Base classes for SAML {@link net.shibboleth.idp.attribute.transcoding.AttributeTranscoder} classes.
  */
 
-package net.shibboleth.idp.saml.attribute.encoding;
\ No newline at end of file
+package net.shibboleth.idp.saml.attribute.transcoding;
\ No newline at end of file
diff --git a/idp-saml-api/src/test/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAML1AttributeEncoderTest.java b/idp-saml-api/src/test/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAML1AttributeEncoderTest.java
deleted file mode 100644
index 80e283f..0000000
--- a/idp-saml-api/src/test/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAML1AttributeEncoderTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.xml.namespace.QName;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-
-import org.opensaml.core.OpenSAMLInitBaseTestCase;
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.XMLObjectBuilder;
-import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
-import org.opensaml.core.xml.schema.XSString;
-import org.opensaml.saml.saml1.core.Attribute;
-import org.testng.Assert;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-/**
- * Test for {@link AbstractSAML1AttributeEncoder}.
- */
-public class AbstractSAML1AttributeEncoderTest extends OpenSAMLInitBaseTestCase {
-
-    private XMLObjectBuilder<XSString> theBuilder;
-
-    private QName theQName = new QName("LocalQNAME");
-
-    private final String MY_NAME = "myName";
-
-    private final String MY_NAMESPACE = "myNameSpace";
-
-    private final String ATTRIBUTE_ID = "attrID";
-
-    private final String ATTRIBUTE_VALUE_1 = "attrValOne";
-
-    private final String ATTRIBUTE_VALUE_2 = "attrValueTwo";
-    
-    @BeforeClass public void initTest() throws ComponentInitializationException {
-        theBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSString>getBuilderOrThrow(XSString.TYPE_NAME);
-
-    }
-
-    @Test public void abstractSAML1AttributeEncoder() throws ComponentInitializationException, AttributeEncodingException {
-        AbstractSAML1AttributeEncoder encoder = new AbstractSAMLAttributeEncoderTest.mockEncoder(theBuilder, theQName);
-        encoder.setName(MY_NAME);
-        encoder.setNamespace(MY_NAMESPACE);
-        encoder.initialize();
-        // Use literal here to catch things being edited by mistake
-        Assert.assertEquals(encoder.getProtocol(), "urn:oasis:names:tc:SAML:1.1:protocol");
-
-                
-        IdPAttribute attr = new IdPAttribute(ATTRIBUTE_ID);
-        attr.setValues(Arrays.asList(new StringAttributeValue(ATTRIBUTE_VALUE_1),
-                new StringAttributeValue(ATTRIBUTE_VALUE_2)));
-        
-        XMLObject obj = encoder.encode(attr);
-        
-        Assert.assertTrue(obj instanceof Attribute);
-        
-        List<XMLObject> result = ((Attribute) obj).getAttributeValues();
-        
-        Assert.assertEquals(result.size(), 2);
-        Set<String> resultSet = new HashSet<>(2); 
-        for (XMLObject o: result) {
-            Assert.assertTrue(o instanceof XSString);
-            resultSet.add(((XSString) o).getValue());
-        }
-        Assert.assertTrue(resultSet.contains(ATTRIBUTE_VALUE_1));
-        Assert.assertTrue(resultSet.contains(ATTRIBUTE_VALUE_2));
-    }
-}
diff --git a/idp-saml-api/src/test/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAML2AttributeEncoderTest.java b/idp-saml-api/src/test/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAML2AttributeEncoderTest.java
deleted file mode 100644
index d36bee4..0000000
--- a/idp-saml-api/src/test/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAML2AttributeEncoderTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.xml.namespace.QName;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.ByteAttributeValue;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.UnmodifiableComponentException;
-
-import org.opensaml.core.OpenSAMLInitBaseTestCase;
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.XMLObjectBuilder;
-import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
-import org.opensaml.core.xml.schema.XSString;
-import org.opensaml.saml.saml2.core.Attribute;
-import org.testng.Assert;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-/**
- * Test for {@link AbstractSAML2AttributeEncoder}.
- */
-public class AbstractSAML2AttributeEncoderTest extends OpenSAMLInitBaseTestCase {
-
-    private XMLObjectBuilder<XSString> theBuilder;
-
-    private QName theQName = new QName("LocalQNAME");
-
-    private final String MY_NAME = "myName";
-
-    private final String MY_NAMESPACE = "myNameSpace";
-
-    private final String ATTRIBUTE_ID = "attrID";
-
-    private final String ATTRIBUTE_VALUE_1 = "attrValOne";
-
-    private final String ATTRIBUTE_VALUE_2 = "attrValueTwo";
-
-    private final String FRIENDLY_NAME = "friendly";
-    
-    @BeforeClass public void initTest() throws ComponentInitializationException {
-        theBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSString>getBuilderOrThrow(XSString.TYPE_NAME);
-    }
-
-    @Test public void abstractSAML2AttributeEncoder() throws ComponentInitializationException, AttributeEncodingException {
-        AbstractSAML2AttributeEncoder encoder = new mockEncoder();
-        
-        encoder.setName(MY_NAME);
-        encoder.setNameFormat(MY_NAMESPACE);
-        encoder.setFriendlyName(FRIENDLY_NAME);
-        encoder.initialize();
-        // Use literal here to catch things being edited by mistake
-        Assert.assertEquals(encoder.getProtocol(), "urn:oasis:names:tc:SAML:2.0:protocol");
-
-                
-        IdPAttribute attr = new IdPAttribute(ATTRIBUTE_ID);
-        attr.setValues(Arrays.asList(new StringAttributeValue(ATTRIBUTE_VALUE_1),
-                new StringAttributeValue(ATTRIBUTE_VALUE_2)));
-        
-        XMLObject obj = encoder.encode(attr);
-        
-        Assert.assertTrue(obj instanceof Attribute);
-        
-        List<XMLObject> result = ((Attribute) obj).getAttributeValues();
-        
-        Assert.assertEquals(result.size(), 2);
-        Set<String> resultSet = new HashSet<>(2); 
-        for (XMLObject o: result) {
-            Assert.assertTrue(o instanceof XSString);
-            resultSet.add(((XSString) o).getValue());
-        }
-        Assert.assertTrue(resultSet.contains(ATTRIBUTE_VALUE_1));
-        Assert.assertTrue(resultSet.contains(ATTRIBUTE_VALUE_2));
-    }
-    
-    @Test public void friendlyName() throws ComponentInitializationException {
-        AbstractSAML2AttributeEncoder encoder = new mockEncoder();
-        Assert.assertNull(encoder.getFriendlyName());
-        encoder.setName(MY_NAME);
-        encoder.setNameFormat(MY_NAMESPACE);
-        encoder.initialize();
-        
-        encoder = new mockEncoder();
-        Assert.assertNull(encoder.getFriendlyName());
-        encoder.setName(MY_NAME);
-        encoder.setFriendlyName(FRIENDLY_NAME);
-        encoder.setNameFormat(MY_NAMESPACE);
-        Assert.assertEquals(encoder.getFriendlyName(), FRIENDLY_NAME);
-        encoder.initialize();
-        Assert.assertEquals(encoder.getFriendlyName(), FRIENDLY_NAME);
-        try {
-            encoder.setFriendlyName(FRIENDLY_NAME);
-            Assert.fail();
-        } catch (UnmodifiableComponentException ex) {
-            //OK
-        }
-    }
-    
-    protected class mockEncoder extends AbstractSAML2AttributeEncoder {
-        
-        /** {@inheritDoc} */
-        protected boolean canEncodeValue(IdPAttribute attribute, IdPAttributeValue value) {
-            return ! (value instanceof ByteAttributeValue);
-        }
-
-        /** {@inheritDoc} */
-        protected XMLObject encodeValue(IdPAttribute attribute, IdPAttributeValue value) throws AttributeEncodingException {
-            if (!(value instanceof StringAttributeValue)) {
-                return null;
-            }
-            XSString result = theBuilder.buildObject(theQName);
-            result.setValue((String) value.getValue());
-            return result;
-        }
-        
-    }
-
-}
diff --git a/idp-saml-api/src/test/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAMLAttributeEncoderTest.java b/idp-saml-api/src/test/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAMLAttributeEncoderTest.java
deleted file mode 100644
index 68ffd84..0000000
--- a/idp-saml-api/src/test/java/net/shibboleth/idp/saml/attribute/encoding/AbstractSAMLAttributeEncoderTest.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.xml.namespace.QName;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.ByteAttributeValue;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.UninitializedComponentException;
-import net.shibboleth.utilities.java.support.component.UnmodifiableComponentException;
-import net.shibboleth.utilities.java.support.logic.ConstraintViolationException;
-
-import org.opensaml.core.OpenSAMLInitBaseTestCase;
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.XMLObjectBuilder;
-import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
-import org.opensaml.core.xml.schema.XSString;
-import org.opensaml.saml.saml1.core.Attribute;
-import org.testng.Assert;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-/**
- *
- */
-public class AbstractSAMLAttributeEncoderTest extends OpenSAMLInitBaseTestCase {
-    
-    private XMLObjectBuilder<XSString> theBuilder;
-    private QName theQName = new QName("LocalQNAME");
-    private final String MY_NAME = "myName";
-    private final String MY_NAMESPACE = "myNameSpace";
-    private final String ATTRIBUTE_ID = "attrID";
-    private final String ATTRIBUTE_VALUE_1 = "attrValOne";
-    private final String ATTRIBUTE_VALUE_2 = "attrValeTwo";
-    
-    @BeforeClass public void initTest() throws ComponentInitializationException {
-        theBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSString>getBuilderOrThrow(XSString.TYPE_NAME);
-    }
-    
-    @Test public void initializeAndSetters() throws AttributeEncodingException, ComponentInitializationException {
-        AbstractSAML1AttributeEncoder encoder = new mockEncoder(theBuilder, theQName);
-        
-        Assert.assertNull(encoder.getName());
-        Assert.assertEquals(encoder.getNamespace(), "urn:mace:shibboleth:1.0:attributeNamespace:uri");
-        
-        try {
-            encoder.setName("");
-            Assert.fail();
-        } catch (ConstraintViolationException e) {
-            
-        }
-        
-        encoder = new mockEncoder(theBuilder, theQName);
-        try {
-            encoder.setNamespace("");
-            Assert.fail();
-        } catch (ConstraintViolationException e) {
-            
-        }
-        
-        encoder = new mockEncoder(theBuilder, theQName);
-        encoder.setNamespace(MY_NAMESPACE);
-        encoder.setName(MY_NAME);
-        
-        try {
-            encoder.encode(new IdPAttribute(ATTRIBUTE_ID));
-            Assert.fail();
-        } catch (UninitializedComponentException ex) {
-            // OK
-        }
-        
-        encoder.initialize();
-        try {
-            encoder.setName(" ");
-            Assert.fail();
-        } catch (UnmodifiableComponentException ex) {
-            //
-        }
-        
-        try {
-            encoder.setNamespace(" ");
-            Assert.fail();
-        } catch (UnmodifiableComponentException ex) {
-            //
-        }
-
-    }
-
-    @Test public void encode() throws AttributeEncodingException, ComponentInitializationException {
-        AbstractSAML1AttributeEncoder encoder = new mockEncoder(theBuilder, theQName);
-        encoder.setNamespace(MY_NAMESPACE);
-        encoder.setName(MY_NAME);
-        encoder.initialize();
-        IdPAttribute attr = new IdPAttribute(ATTRIBUTE_ID);
-        
-        try {
-            encoder.encode(attr);
-        } catch (AttributeEncodingException e) {
-            // OK
-        }
-        
-        final int[] intArray = {1, 2, 3, 4};
-        final List<IdPAttributeValue<?>> values = new ArrayList<>(
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}),
-                        null,
-                        new IdPAttributeValue<Object>() {
-                            @Override
-                            public Object getValue() {
-                                return intArray;
-                            }
-
-                            @Override
-                            public String getDisplayValue() {
-                                return intArray.toString();
-                            }
-                        }
-                        ));
-        attr.setValues(values);
-        try {
-            encoder.encode(attr);
-        } catch (AttributeEncodingException e) {
-            // OK
-        }
-        values.add(new StringAttributeValue(ATTRIBUTE_VALUE_1));
-        values.add(new StringAttributeValue(ATTRIBUTE_VALUE_2));
-        attr.setValues(values);
-        
-        List<XMLObject> result = ((Attribute) encoder.encode(attr)).getAttributeValues();
-        
-        Assert.assertEquals(result.size(), 2);
-        Set<String> resultSet = new HashSet<>(2); 
-        for (XMLObject o: result) {
-            Assert.assertTrue(o instanceof XSString);
-            resultSet.add(((XSString) o).getValue());
-        }
-        Assert.assertTrue(resultSet.contains(ATTRIBUTE_VALUE_1));
-        Assert.assertTrue(resultSet.contains(ATTRIBUTE_VALUE_2));
-
-    }
-    
-    @Test public void equalsHash() {
-        mockEncoder enc1 = new mockEncoder(theBuilder, theQName);
-        Assert.assertEquals(enc1, enc1);
-        Assert.assertNotSame(enc1, null);
-        Assert.assertNotSame(enc1, this);
-
-        mockEncoder enc2 = new mockEncoder(theBuilder, theQName);
-        
-        Assert.assertEquals(enc1, enc2);
-        Assert.assertEquals(enc1.hashCode(), enc2.hashCode());
-        enc1.setName(MY_NAME);
-        enc1.setNamespace(MY_NAMESPACE);
-        enc2.setName(MY_NAME);
-        enc2.setNamespace(MY_NAMESPACE);
-        Assert.assertEquals(enc1, enc2);
-        Assert.assertEquals(enc1.hashCode(), enc2.hashCode());
-        enc2.setName(MY_NAME + MY_NAME);
-        Assert.assertNotSame(enc1,  enc2);
-        Assert.assertNotSame(enc1.hashCode(),  enc2.hashCode());
-        enc2.setName(MY_NAME);
-        enc2.setNamespace(MY_NAME);
-        Assert.assertNotSame(enc1,  enc2);
-        Assert.assertNotSame(enc1.hashCode(),  enc2.hashCode());        
-    }
- 
-    protected static class mockEncoder extends AbstractSAML1AttributeEncoder {
-        
-        @Nonnull private final XMLObjectBuilder<XSString> builder;
-        @Nonnull private final QName myQName;
-
-        /**
-         * Constructor.
-         *
-         * @param theBuilder
-         * @param theQName
-         */
-        public mockEncoder(@Nonnull final XMLObjectBuilder<XSString> theBuilder, @Nonnull final QName theQName) {
-            builder = theBuilder;
-            myQName = theQName;
-        }
-
-        /** {@inheritDoc} */
-        @Override
-        protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute,
-                @Nonnull final IdPAttributeValue value) {
-            return ! (value instanceof ByteAttributeValue);
-        }
-
-        /** {@inheritDoc} */
-        @Override
-        @Nullable protected XMLObject encodeValue(@Nonnull final IdPAttribute attribute,
-                @Nonnull final IdPAttributeValue value) throws AttributeEncodingException {
-            if (!(value instanceof StringAttributeValue)) {
-                return null;
-            }
-            XSString result = builder.buildObject(myQName);
-            result.setValue((String) value.getValue());
-            return result;
-        }
-        
-    }
-}
diff --git a/idp-saml-api/src/test/java/net/shibboleth/idp/saml/attribute/encoding/SAMLEncoderSupportTest.java b/idp-saml-api/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/SAMLEncoderSupportTest.java
similarity index 99%
rename from idp-saml-api/src/test/java/net/shibboleth/idp/saml/attribute/encoding/SAMLEncoderSupportTest.java
rename to idp-saml-api/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/SAMLEncoderSupportTest.java
index 9a9cb82..759e563 100644
--- a/idp-saml-api/src/test/java/net/shibboleth/idp/saml/attribute/encoding/SAMLEncoderSupportTest.java
+++ b/idp-saml-api/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/SAMLEncoderSupportTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.saml.attribute.encoding;
+package net.shibboleth.idp.saml.attribute.transcoding;
 
 import java.util.List;
 
diff --git a/idp-saml-impl/.project b/idp-saml-impl/.project
index 1b5d236..a9a4b04 100644
--- a/idp-saml-impl/.project
+++ b/idp-saml-impl/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1ByteAttributeEncoder.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1ByteAttributeEncoder.java
deleted file mode 100644
index 3e53911..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1ByteAttributeEncoder.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding.impl;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.ByteAttributeValue;
-import net.shibboleth.idp.saml.attribute.encoding.AbstractSAML1AttributeEncoder;
-import net.shibboleth.idp.saml.attribute.encoding.SAMLEncoderSupport;
-
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.saml.saml1.core.AttributeValue;
-
-/**
- * {@link net.shibboleth.idp.attribute.AttributeEncoder} that produces SAML 1 attributes from an
- * {@link IdPAttribute} that contains <code>byte[]</code> values.
- */
-public class SAML1ByteAttributeEncoder extends AbstractSAML1AttributeEncoder<ByteAttributeValue> {
-
-    /** {@inheritDoc} */
-    @Override
-    protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute, @Nonnull final IdPAttributeValue value) {
-        return value instanceof ByteAttributeValue;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @Nullable protected XMLObject encodeValue(@Nonnull final IdPAttribute attribute,
-            @Nonnull final ByteAttributeValue value)  throws AttributeEncodingException {
-        return SAMLEncoderSupport.encodeByteArrayValue(attribute,
-                AttributeValue.DEFAULT_ELEMENT_NAME, value.getValue(), encodeType());
-    }
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1ScopedStringAttributeEncoder.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1ScopedStringAttributeEncoder.java
deleted file mode 100644
index 8912aa1..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1ScopedStringAttributeEncoder.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding.impl;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
-import net.shibboleth.idp.saml.attribute.encoding.AbstractSAML1AttributeEncoder;
-import net.shibboleth.idp.saml.attribute.encoding.SAMLEncoderSupport;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
-
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.saml.saml1.core.AttributeValue;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * {@link net.shibboleth.idp.attribute.AttributeEncoder} that produces SAML 1 attributes from an
- * {@link net.shibboleth.idp.attribute.IdPAttribute} that contains scoped string values.
- */
-public class SAML1ScopedStringAttributeEncoder extends AbstractSAML1AttributeEncoder<ScopedStringAttributeValue> {
-
-    /** The log. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML1ScopedStringAttributeEncoder.class);
-
-    /** Type of scoping to use. */
-    @Nullable private String scopeType;
-
-    /** Delimiter used for "inline" scopeType. */
-    @Nullable private String scopeDelimiter;
-
-    /** Attribute name used for "attribute" scopeType. */
-    @Nullable private String scopeAttributeName;
-
-    /**
-     * Constructor.
-     *
-     */
-    public SAML1ScopedStringAttributeEncoder() {
-        scopeDelimiter = "@";
-        scopeAttributeName =  "Scope";
-        scopeType = "attribute";
-        
-        // Turn off type encoding, because we default to a non-built-in type that consumers won't
-        // have the schema for.
-        setEncodeType(false);
-    }
-    
-    /**
-     * Get the name of the non-inline scope-carrying attribute.
-     * 
-     * @return the name of the scope-carrying attribute
-     */
-    @Nullable public String getScopeAttributeName() {
-        return scopeAttributeName;
-    }
-
-    /**
-     * Get the scope delimiter.
-     * 
-     * @return the scope delimiter
-     */
-    @Nullable public String getScopeDelimiter() {
-        return scopeDelimiter;
-    }
-
-    /**
-     * Get the scope syntax type.
-     * 
-     * @return the type of scope syntax to use
-     */
-    @Nullable public String getScopeType() {
-        return scopeType;
-    }
-
-    /**
-     * Set the name of the non-inline scope-carrying attribute.
-     * 
-     * @param newScopeAttribute the name to set
-     */
-    public void setScopeAttributeName(@Nullable final String newScopeAttribute) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        
-        scopeAttributeName = StringSupport.trimOrNull(newScopeAttribute);
-    }
-
-    /**
-     * Set the scope delimiter.
-     * 
-     * @param newScopeDelimiter delimiter to set
-     */
-    public void setScopeDelimiter(@Nullable final String newScopeDelimiter) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        
-        scopeDelimiter = StringSupport.trimOrNull(newScopeDelimiter);
-    }
-
-    /**
-     * Set the scope syntax type.
-     * 
-     * @param newScopeType the scope syntax type
-     */
-    public void setScopeType(@Nullable final String newScopeType) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        
-        scopeType = StringSupport.trimOrNull(newScopeType);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-
-        if (null == getScopeType()) {
-            log.debug("Scope type not set, assuming \"attribute\"");
-            scopeType = "attribute";
-        }
-
-        if ("attribute".equals(getScopeType())) {
-            if (null == getScopeAttributeName()) {
-                throw new ComponentInitializationException(
-                        "Encoder of type \"attribute\" must specify a scope attribute name");
-            }
-        } else if ("inline".equals(getScopeType())) {
-            if (null == getScopeDelimiter()) {
-                throw new ComponentInitializationException("Encoder of type \"inline\" must specify a scope delimiter");
-            }
-        } else {
-            throw new ComponentInitializationException("Encoder scope type must be set to \"inline\" or \"attribute\"");
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute, @Nonnull final IdPAttributeValue value) {
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-        
-        return value instanceof ScopedStringAttributeValue;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @Nullable protected XMLObject encodeValue(@Nonnull final IdPAttribute attribute,
-            @Nonnull final ScopedStringAttributeValue value) throws AttributeEncodingException {
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-        
-        if ("attribute".equals(getScopeType())) {
-            return SAMLEncoderSupport.encodeScopedStringValueAttribute(attribute,
-                    AttributeValue.DEFAULT_ELEMENT_NAME, value, getScopeAttributeName(), encodeType());
-        } else {
-            return SAMLEncoderSupport.encodeScopedStringValueInline(attribute,
-                    AttributeValue.DEFAULT_ELEMENT_NAME, value, getScopeDelimiter(), encodeType());
-        }
-    }
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1XMLObjectAttributeEncoder.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1XMLObjectAttributeEncoder.java
deleted file mode 100644
index e1ce07d..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1XMLObjectAttributeEncoder.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding.impl;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.XMLObjectAttributeValue;
-import net.shibboleth.idp.saml.attribute.encoding.AbstractSAML1AttributeEncoder;
-import net.shibboleth.idp.saml.attribute.encoding.SAMLEncoderSupport;
-
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.saml.saml1.core.AttributeValue;
-
-/**
- * {@link net.shibboleth.idp.attribute.AttributeEncoder} that produces SAML 1 attributes from an
- * {@link net.shibboleth.idp.attribute.IdPAttribute} that contains {@link XMLObjectAttributeValue} values.
- */
-public class SAML1XMLObjectAttributeEncoder extends AbstractSAML1AttributeEncoder<XMLObjectAttributeValue> {
-
-    /** {@inheritDoc} */
-    @Override
-    protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute, @Nonnull final IdPAttributeValue value) {
-        return value instanceof XMLObjectAttributeValue;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @Nullable protected XMLObject encodeValue(@Nonnull final IdPAttribute attribute,
-            @Nonnull final XMLObjectAttributeValue value) throws AttributeEncodingException {
-        return SAMLEncoderSupport.encodeXMLObjectValue(attribute,
-                AttributeValue.DEFAULT_ELEMENT_NAME, value.getValue());
-    }
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2ByteAttributeEncoder.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2ByteAttributeEncoder.java
deleted file mode 100644
index 1350f84..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2ByteAttributeEncoder.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding.impl;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.ByteAttributeValue;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.IdPRequestedAttribute;
-import net.shibboleth.idp.saml.attribute.encoding.AbstractSAML2AttributeEncoder;
-import net.shibboleth.idp.saml.attribute.encoding.AttributeMapperProcessor;
-import net.shibboleth.idp.saml.attribute.encoding.SAMLEncoderSupport;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeMapper;
-import net.shibboleth.idp.saml.attribute.mapping.impl.ByteAttributeValueMapper;
-
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.saml.saml2.core.AttributeValue;
-import org.opensaml.saml.saml2.metadata.RequestedAttribute;
-
-/**
- * {@link net.shibboleth.idp.attribute.AttributeEncoder} that produces a SAML 2 Attribute from an {@link IdPAttribute}
- * that contains <code>byte[]</code> values.
- */
-public class SAML2ByteAttributeEncoder extends AbstractSAML2AttributeEncoder<ByteAttributeValue> implements
-        AttributeMapperProcessor<RequestedAttribute, IdPRequestedAttribute> {
-
-    /** {@inheritDoc} */
-    @Override protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute,
-            @Nonnull final IdPAttributeValue value) {
-        return value instanceof ByteAttributeValue;
-    }
-
-    /** {@inheritDoc} */
-    @Override @Nullable protected XMLObject encodeValue(@Nonnull final IdPAttribute attribute,
-            @Nonnull final ByteAttributeValue value) throws AttributeEncodingException {
-        return SAMLEncoderSupport.encodeByteArrayValue(attribute,
-                AttributeValue.DEFAULT_ELEMENT_NAME, value.getValue(), encodeType());
-    }
-
-    /** {@inheritDoc} */
-    @Override @Nonnull public void populateAttributeMapper(final
-            AbstractSAMLAttributeMapper<RequestedAttribute, IdPRequestedAttribute> mapper) {
-        mapper.setAttributeFormat(getNameFormat());
-        mapper.setId(getMapperId());
-        mapper.setSAMLName(getName());
-        mapper.setValueMapper(new ByteAttributeValueMapper());
-    }
-
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2ScopedStringAttributeEncoder.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2ScopedStringAttributeEncoder.java
deleted file mode 100644
index 8156190..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2ScopedStringAttributeEncoder.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding.impl;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.IdPRequestedAttribute;
-import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
-import net.shibboleth.idp.saml.attribute.encoding.AbstractSAML2AttributeEncoder;
-import net.shibboleth.idp.saml.attribute.encoding.AttributeMapperProcessor;
-import net.shibboleth.idp.saml.attribute.encoding.SAMLEncoderSupport;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeMapper;
-import net.shibboleth.idp.saml.attribute.mapping.impl.ScopedStringAttributeValueMapper;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
-
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.saml.saml2.core.AttributeValue;
-import org.opensaml.saml.saml2.metadata.RequestedAttribute;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * {@link net.shibboleth.idp.attribute.AttributeEncoder} that produces a SAML 2 Attribute from an
- * {@link net.shibboleth.idp.attribute.IdPAttribute} that contains scoped string values.
- */
-public class SAML2ScopedStringAttributeEncoder extends AbstractSAML2AttributeEncoder<ScopedStringAttributeValue>
-        implements AttributeMapperProcessor<RequestedAttribute, IdPRequestedAttribute> {
-
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML2ScopedStringAttributeEncoder.class);
-
-    /** Type of scoping to use. */
-    @Nullable private String scopeType;
-
-    /** Delimiter used for "inline" scopeType. */
-    @Nullable private String scopeDelimiter;
-
-    /** Attribute name used for "attribute" scopeType. */
-    @Nullable private String scopeAttributeName;
-    
-    /**
-     * Constructor.
-     *
-     */
-    public SAML2ScopedStringAttributeEncoder() {
-        scopeDelimiter = "@";
-        scopeAttributeName = "Scope";
-        scopeType = "inline";
-    }
-
-    /**
-     * Get the name of the non-inline scope-carrying attribute.
-     * 
-     * @return the name of the scope-carrying attribute
-     */
-    @Nullable public String getScopeAttributeName() {
-        return scopeAttributeName;
-    }
-
-    /**
-     * Get the scope delimiter.
-     * 
-     * @return the scope delimiter
-     */
-    @Nullable public String getScopeDelimiter() {
-        return scopeDelimiter;
-    }
-
-    /**
-     * Get the scope syntax type.
-     * 
-     * @return the type of scope syntax to use
-     */
-    @Nullable public String getScopeType() {
-        return scopeType;
-    }
-
-    /**
-     * Set the name of the non-inline scope-carrying attribute.
-     * 
-     * @param newScopeAttribute the name to set
-     */
-    public void setScopeAttributeName(@Nullable final String newScopeAttribute) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
-        scopeAttributeName = StringSupport.trimOrNull(newScopeAttribute);
-    }
-
-    /**
-     * Set the scope delimiter.
-     * 
-     * @param newScopeDelimiter delimiter to set
-     */
-    public void setScopeDelimiter(@Nullable final String newScopeDelimiter) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
-        scopeDelimiter = StringSupport.trimOrNull(newScopeDelimiter);
-    }
-
-    /**
-     * Set the scope syntax type.
-     * 
-     * @param newScopeType the scope syntax type
-     */
-    public void setScopeType(@Nullable final String newScopeType) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
-        scopeType = StringSupport.trimOrNull(newScopeType);
-    }
-
-    /**
-     * Ensures that the values we will need are all there.
-     * 
-     * {@inheritDoc}
-     */
-    @Override protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-
-        if (null == getScopeType()) {
-            log.debug("Scope type not set, assuming \"inline\"");
-            scopeType = "inline";
-        }
-
-        if ("attribute".equals(getScopeType())) {
-            if (null == getScopeAttributeName()) {
-                throw new ComponentInitializationException(
-                        "Encoder of type \"attribute\" must specify a scope attribute name");
-            }
-        } else if ("inline".equals(getScopeType())) {
-            if (null == getScopeDelimiter()) {
-                throw new ComponentInitializationException("Encoder of type \"inline\" must specify a scope delimiter");
-            }
-        } else {
-            throw new ComponentInitializationException("Encoder scope type must be set to \"inline\" or \"attribute\"");
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute,
-            @Nonnull final IdPAttributeValue value) {
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-
-        return value instanceof ScopedStringAttributeValue;
-    }
-
-    /** {@inheritDoc} */
-    @Override @Nullable protected XMLObject encodeValue(@Nonnull final IdPAttribute attribute,
-            @Nonnull final ScopedStringAttributeValue value) throws AttributeEncodingException {
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-
-        if ("attribute".equals(getScopeType())) {
-            return SAMLEncoderSupport.encodeScopedStringValueAttribute(attribute,
-                    AttributeValue.DEFAULT_ELEMENT_NAME, value, getScopeAttributeName(), encodeType());
-        } else {
-            return SAMLEncoderSupport.encodeScopedStringValueInline(
-                    attribute, AttributeValue.DEFAULT_ELEMENT_NAME, value, getScopeDelimiter(), encodeType());
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override @Nonnull public void populateAttributeMapper(
-            final AbstractSAMLAttributeMapper<RequestedAttribute, IdPRequestedAttribute> mapper) {
-        mapper.setAttributeFormat(getNameFormat());
-        mapper.setId(getMapperId());
-        mapper.setSAMLName(getName());
-        final ScopedStringAttributeValueMapper foo = new ScopedStringAttributeValueMapper();
-        foo.setDelimiter(getScopeDelimiter());
-        mapper.setValueMapper(foo);
-    }
-
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2XMLObjectAttributeEncoder.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2XMLObjectAttributeEncoder.java
deleted file mode 100644
index 097a035..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2XMLObjectAttributeEncoder.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding.impl;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.IdPRequestedAttribute;
-import net.shibboleth.idp.attribute.XMLObjectAttributeValue;
-import net.shibboleth.idp.saml.attribute.encoding.AbstractSAML2AttributeEncoder;
-import net.shibboleth.idp.saml.attribute.encoding.AttributeMapperProcessor;
-import net.shibboleth.idp.saml.attribute.encoding.SAMLEncoderSupport;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeMapper;
-import net.shibboleth.idp.saml.attribute.mapping.impl.XMLObjectAttributeValueMapper;
-
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.saml.saml2.core.AttributeValue;
-import org.opensaml.saml.saml2.metadata.RequestedAttribute;
-
-/**
- * {@link net.shibboleth.idp.attribute.AttributeEncoder} that produces a SAML 2 Attribute from an {@link IdPAttribute}
- * that contains {@link XMLObject} values.
- */
-public class SAML2XMLObjectAttributeEncoder extends AbstractSAML2AttributeEncoder<XMLObjectAttributeValue> implements
-        AttributeMapperProcessor<RequestedAttribute, IdPRequestedAttribute> {
-
-    /** {@inheritDoc} */
-    @Override protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute,
-            @Nonnull final IdPAttributeValue value) {
-        return value instanceof XMLObjectAttributeValue;
-    }
-
-    /** {@inheritDoc} */
-    @Override @Nullable protected XMLObject encodeValue(@Nonnull final IdPAttribute attribute,
-            @Nonnull final XMLObjectAttributeValue value) throws AttributeEncodingException {
-        return SAMLEncoderSupport
-                .encodeXMLObjectValue(attribute, AttributeValue.DEFAULT_ELEMENT_NAME, value.getValue());
-    }
-
-    /** {@inheritDoc} */
-    @Override @Nonnull public void populateAttributeMapper(
-            final AbstractSAMLAttributeMapper<RequestedAttribute, IdPRequestedAttribute> mapper) {
-
-        mapper.setAttributeFormat(getNameFormat());
-        mapper.setId(getMapperId());
-        mapper.setSAMLName(getName());
-        mapper.setValueMapper(new XMLObjectAttributeValueMapper());
-
-    }
-
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/ByteAttributeValueMapper.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/ByteAttributeValueMapper.java
deleted file mode 100644
index 55fd0aa..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/ByteAttributeValueMapper.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping.impl;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.ByteAttributeValue;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeValueMapper;
-import net.shibboleth.utilities.java.support.codec.Base64Support;
-
-import org.opensaml.core.xml.XMLObject;
-
-/**
- * Mapping extract a {@link ByteAttributeValue} from an AttributeValue.
- */
-public class ByteAttributeValueMapper extends AbstractSAMLAttributeValueMapper {
-
-    /** {@inheritDoc} */
-    @Nullable protected IdPAttributeValue<?> decodeValue(@Nonnull final XMLObject object) {
-        final String value = getStringValue(object);
-        if (null == value) {
-            return null;
-        }
-        final byte[] decoded = Base64Support.decode(value);
-        if (null == decoded) {
-            return null;
-        }
-        return ByteAttributeValue.valueOf(decoded);
-    }
-
-    /** {@inheritDoc} */
-    @Nonnull protected String getAttributeTypeName() {
-        return "Base64Encoded";
-    }
-}
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/RequestedAttributeMapper.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/RequestedAttributeMapper.java
deleted file mode 100644
index e6227f3..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/RequestedAttributeMapper.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping.impl;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.attribute.IdPRequestedAttribute;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeMapper;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-
-import org.opensaml.saml.saml2.metadata.RequestedAttribute;
-
-/** A class that maps a SAML2 {@link RequestedAttribute} into an IdP {@link IdPRequestedAttribute}. */
-public class RequestedAttributeMapper extends AbstractSAMLAttributeMapper<RequestedAttribute, IdPRequestedAttribute> {
-    
-    /** {@inheritDoc} */
-    @Override
-    @Nonnull protected IdPRequestedAttribute newAttribute(@Nonnull final RequestedAttribute input,
-            @Nonnull @NotEmpty final String id) {
-        final IdPRequestedAttribute output = new IdPRequestedAttribute(id);
-        output.setRequired(input.isRequired());
-        return output;
-    }
-    
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/RequestedAttributesMapper.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/RequestedAttributesMapper.java
deleted file mode 100644
index d5affc5..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/RequestedAttributesMapper.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping.impl;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.attribute.IdPRequestedAttribute;
-import net.shibboleth.idp.attribute.resolver.AttributeResolver;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeMapper;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributesMapper;
-
-import org.opensaml.saml.saml2.metadata.RequestedAttribute;
-
-import com.google.common.base.Supplier;
-
-/**
- * This class conceptually represents the content of a attribute-map file, hence it describes (and then does) the
- * mappings from a {@link java.util.List} of SAML2 {@link RequestedAttribute} into a
- * {@link com.google.common.collect.Multimap} going from (SAML2) attributeId to {@link IdPRequestedAttribute}s.
- * 
- */
-public class RequestedAttributesMapper extends AbstractSAMLAttributesMapper<RequestedAttribute, IdPRequestedAttribute> {
-    
-    /** Default constructor. */
-    public RequestedAttributesMapper() {
-        
-    }
-
-    /**
-     * Generate a specific mapper to go from {@link RequestedAttribute} to {@link IdPRequestedAttribute} by inverting
-     * the function of the mappers in the profiled {@link AttributeResolver}.
-     * 
-     * @param resolver resolver to invert
-     */
-    public RequestedAttributesMapper(@Nonnull final AttributeResolver resolver) {
-        super(resolver, "RequestedMapper<" + resolver.getId() + ">",
-                new Supplier<AbstractSAMLAttributeMapper<RequestedAttribute, IdPRequestedAttribute>>() {
-
-                    @Override public RequestedAttributeMapper get() {
-                        return new RequestedAttributeMapper();
-                    }
-                });
-    }
-
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML1AttributeDesignatorsMapper.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML1AttributeDesignatorsMapper.java
deleted file mode 100644
index bb3348a..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML1AttributeDesignatorsMapper.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping.impl;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.resolver.AttributeResolver;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeDesignatorMapper;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeDesignatorsMapper;
-
-import com.google.common.base.Supplier;
-
-/**
- * This class conceptually represents the content of a attribute-map file, hence it describes (and then does) the
- * mappings from a {@link java.util.List} of SAML1 {@link org.opensaml.saml.saml1.core.AttributeDesignator} into a
- * {@link com.google.common.collect.Multimap} going from (SAML1) attributeId to {@link IdPAttribute}s.
- * 
- */
-public class SAML1AttributeDesignatorsMapper extends AbstractSAMLAttributeDesignatorsMapper<IdPAttribute> {
-
-    /** Default constructor. */
-    public SAML1AttributeDesignatorsMapper() {
-        
-    }
-
-    /**
-     * Generate a specific mapper to go from {@link org.opensaml.saml.saml1.core.AttributeDesignator} to
-     * {@link IdPAttribute} by inverting the function of the mappers in the profiled {@link AttributeResolver}.
-     * 
-     * @param resolver resolver to invert
-     */
-    public SAML1AttributeDesignatorsMapper(@Nonnull final AttributeResolver resolver) {
-        super(resolver, "Mapper<" + resolver.getId() + ">",
-                new Supplier<AbstractSAMLAttributeDesignatorMapper<IdPAttribute>>() {
-
-            @Override public SAML1AttributeDesignatorMapper get() {
-                return new SAML1AttributeDesignatorMapper();
-            }
-        });
-    }
-
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML1AttributeDesignatorsMapperService.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML1AttributeDesignatorsMapperService.java
deleted file mode 100644
index cc44d99..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML1AttributeDesignatorsMapperService.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping.impl;
-
-import java.time.Instant;
-import java.util.List;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.opensaml.saml.saml1.core.AttributeDesignator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Multimap;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.resolver.AttributeResolver;
-import net.shibboleth.idp.saml.attribute.mapping.AttributesMapper;
-import net.shibboleth.utilities.java.support.annotation.ParameterName;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.service.ReloadableService;
-import net.shibboleth.utilities.java.support.service.ServiceableComponent;
-
-/**
- * An implementation of {@link SAML1AttributeDesignatorsMapper} for use by objects that
- * can't just store a constructed mapper themselves, such as action beans.
- */
- at ThreadSafe
-public class SAML1AttributeDesignatorsMapperService implements AttributesMapper<AttributeDesignator,IdPAttribute> {
-
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML1AttributeDesignatorsMapperService.class);
-
-    /** Service used to get the resolver used to fetch attributes. */
-    @Nonnull private final ReloadableService<AttributeResolver> attributeResolverService;
-
-    /** Whether the last invocation of {@link ReloadableService#reload()}
-     * on {@link #attributeResolverService} failed. This limits the noise in log file. */
-    @Nonnull private boolean captiveServiceReloadFailed;
-
-    /** Cached AttributeMapper. */
-    @Nullable private SAML1AttributeDesignatorsMapper attributesMapper;
-
-    /** Date when the cache was last refreshed. */
-    @Nullable private Instant lastReload;
-
-    /**
-     * Constructor.
-     * 
-     * @param resolverService the service for the attribute resolver we are to derive mapping info from
-     */
-    public SAML1AttributeDesignatorsMapperService(
-            @Nonnull @ParameterName(name="resolverService")
-            final ReloadableService<AttributeResolver> resolverService) {
-        attributeResolverService = Constraint.isNotNull(resolverService, "AttributeResolver cannot be null");
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @Nonnull @NonnullElements public Multimap<String, IdPAttribute> mapAttributes(
-            @Nonnull @NonnullElements final List<AttributeDesignator> prototypes) {
-        
-        final SAML1AttributeDesignatorsMapper mapper = getMapper();
-        if (null == mapper) {
-            log.error("No AttributeMapper available, returning nothing");
-            return ArrayListMultimap.create();
-        }
-        
-        return mapper.mapAttributes(prototypes);
-    }
-
-    /**
-     * Check to see if a reload of the mapper is required and do so, and return whatever should be used.
-     * 
-     * @return the mapper to use
-     */
-    @Nullable private SAML1AttributeDesignatorsMapper getMapper() {
-        
-        if (lastReload != null && lastReload.equals(attributeResolverService.getLastSuccessfulReloadInstant())) {
-            // Nothing has changed since we last reloaded.
-            return attributesMapper;
-        }
-
-        // Reload.
-        ServiceableComponent<AttributeResolver> component = null;
-        SAML1AttributeDesignatorsMapper am = null;
-        try {
-            // Get date before we get the component. That way we'll not leak changes.
-            final Instant when = attributeResolverService.getLastSuccessfulReloadInstant();
-            component = attributeResolverService.getServiceableComponent();
-            if (null == component) {
-                if (!captiveServiceReloadFailed) {
-                    log.error("Invalid AttributeResolver configuration");
-                }
-                captiveServiceReloadFailed = true;
-            } else {
-                final AttributeResolver attributeResolver = component.getComponent();
-                am = new SAML1AttributeDesignatorsMapper(attributeResolver);
-                captiveServiceReloadFailed = false;
-                lastReload = when;
-            }
-        } finally {
-            if (null != component) {
-                component.unpinComponent();
-            }
-        }
-        
-        try {
-            if (null != am) {
-                am.initialize();
-            }
-        } catch (final ComponentInitializationException e) {
-            log.error("Error initializing AttributeMapper", e);
-        }
-        
-        attributesMapper = am;
-        return am;
-    }
-    
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML2AttributesMapper.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML2AttributesMapper.java
deleted file mode 100644
index cbaea518..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML2AttributesMapper.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping.impl;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.resolver.AttributeResolver;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeMapper;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributesMapper;
-
-import org.opensaml.saml.saml2.core.Attribute;
-
-import com.google.common.base.Supplier;
-
-/**
- * This class conceptually represents the content of a attribute-map file, hence it describes (and then does) the
- * mappings from a {@link java.util.List} of SAML2 {@link Attribute} into a {@link com.google.common.collect.Multimap}
- * going from (SAML2) attributeId to {@link IdPAttribute}s.
- * 
- */
-public class SAML2AttributesMapper extends AbstractSAMLAttributesMapper<Attribute, IdPAttribute> {
-
-    /** Default constructor. */
-    public SAML2AttributesMapper() {
-        
-    }
-
-    /**
-     * Generate a specific mapper to go from {@link Attribute} to {@link IdPAttribute} by inverting
-     * the function of the mappers in the profiled {@link AttributeResolver}.
-     * 
-     * @param resolver resolver to invert
-     */
-    public SAML2AttributesMapper(@Nonnull final AttributeResolver resolver) {
-        super(resolver, "Mapper<" + resolver.getId() + ">",
-                new Supplier<AbstractSAMLAttributeMapper<Attribute, IdPAttribute>>() {
-
-            @Override public SAML2AttributeMapper get() {
-                return new SAML2AttributeMapper();
-            }
-        });
-    }
-
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML2AttributesMapperService.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML2AttributesMapperService.java
deleted file mode 100644
index bc30707..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/SAML2AttributesMapperService.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping.impl;
-
-import java.time.Instant;
-import java.util.List;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.opensaml.saml.saml2.core.Attribute;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Multimap;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.resolver.AttributeResolver;
-import net.shibboleth.idp.saml.attribute.mapping.AttributesMapper;
-import net.shibboleth.utilities.java.support.annotation.ParameterName;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.service.ReloadableService;
-import net.shibboleth.utilities.java.support.service.ServiceableComponent;
-
-/**
- * An implementation of {@link SAML2AttributesMapper} for use by objects that can't just store a constructed mapper
- * themselves, such as action beans.
- */
- at ThreadSafe
-public class SAML2AttributesMapperService implements AttributesMapper<Attribute, IdPAttribute> {
-
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML2AttributesMapperService.class);
-
-    /** Service used to get the resolver used to fetch attributes. */
-    @Nonnull private final ReloadableService<AttributeResolver> attributeResolverService;
-
-    /** Whether the last invocation of {@link ReloadableService#reload()}
-     * on {@link #attributeResolverService} failed. This limits the noise in log file. */
-    @Nonnull private boolean captiveServiceReloadFailed;
-
-    /** Cached AttributeMapper. */
-    @Nullable private SAML2AttributesMapper attributesMapper;
-
-    /** Date when the cache was last refreshed. */
-    @Nullable private Instant lastReload;
-
-    /**
-     * Constructor.
-     * 
-     * @param resolverService the service for the attribute resolver we are to derive mapping info from
-     */
-    public SAML2AttributesMapperService(@Nonnull @ParameterName(name="resolverService") 
-                                        final ReloadableService<AttributeResolver> resolverService) {
-        attributeResolverService = Constraint.isNotNull(resolverService, "AttributeResolver cannot be null");
-    }
-
-    /** {@inheritDoc} */
-    @Override @Nonnull @NonnullElements public Multimap<String, IdPAttribute> mapAttributes(
-            @Nonnull @NonnullElements final List<Attribute> prototypes) {
-
-        final SAML2AttributesMapper mapper = getMapper();
-        if (null == mapper) {
-            log.error("No AttributeMapper available, returning nothing");
-            return ArrayListMultimap.create();
-        }
-
-        return mapper.mapAttributes(prototypes);
-    }
-
-    /**
-     * Check to see if a reload of the mapper is required and do so, and return whatever should be used.
-     * 
-     * @return the mapper to use
-     */
-    @Nullable private SAML2AttributesMapper getMapper() {
-
-        if (lastReload != null && lastReload.equals(attributeResolverService.getLastSuccessfulReloadInstant())) {
-            // Nothing has changed since we last reloaded.
-            return attributesMapper;
-        }
-
-        // Reload.
-        ServiceableComponent<AttributeResolver> component = null;
-        SAML2AttributesMapper am = null;
-        try {
-            // Get date before we get the component. That way we'll not leak changes.
-            final Instant when = attributeResolverService.getLastSuccessfulReloadInstant();
-            component = attributeResolverService.getServiceableComponent();
-            if (null == component) {
-                if (!captiveServiceReloadFailed) {
-                    log.error("Invalid AttributeResolver configuration");
-                }
-                captiveServiceReloadFailed = true;
-            } else {
-                final AttributeResolver attributeResolver = component.getComponent();
-                am = new SAML2AttributesMapper(attributeResolver);
-                captiveServiceReloadFailed = false;
-                lastReload = when;
-            }
-        } finally {
-            if (null != component) {
-                component.unpinComponent();
-            }
-        }
-
-        try {
-            if (null != am) {
-                am.initialize();
-            }
-        } catch (final ComponentInitializationException e) {
-            log.error("Error initializing AttributeMapper", e);
-        }
-
-        attributesMapper = am;
-        return am;
-    }
-
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/ScopedStringAttributeValueMapper.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/ScopedStringAttributeValueMapper.java
deleted file mode 100644
index a356831..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/ScopedStringAttributeValueMapper.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping.impl;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeValueMapper;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
-
-import org.opensaml.core.xml.XMLObject;
-
-import com.google.common.base.Objects;
-
-/**
- * Mapping to extract a {@link net.shibboleth.idp.attribute.ScopedStringAttributeValue} from an AttributeValue.
- */
-public class ScopedStringAttributeValueMapper extends AbstractSAMLAttributeValueMapper {
-
-    /** The scope delimiter. */
-    private String delimiter = "@";
-
-    /**
-     * Returns the delimiter.
-     * 
-     * @return the delimiter
-     */
-    @Nonnull public String getDelimiter() {
-        return delimiter;
-    }
-
-    /**
-     * Sets the delimiter.
-     * 
-     * @param delim what to set.
-     */
-    public void setDelimiter(@Nonnull @NotEmpty final String delim) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        final String trimmed = StringSupport.trimOrNull(delim);
-        Constraint.isNotNull(trimmed, "ScopedStringAttributeDecoder: delimiter can not be empty null");
-        delimiter = trimmed;
-    }
-
-    /** {@inheritDoc} */
-    @Nullable protected IdPAttributeValue<?> decodeValue(@Nonnull final XMLObject object) {
-        Constraint.isNotNull(object, "Object supplied to must not be null");
-        final String stringValue = getStringValue(object);
-
-        if (null == stringValue) {
-            return null;
-        }
-
-        final int offset = stringValue.indexOf(getDelimiter());
-
-        if (offset < 0) {
-            return null;
-        }
-
-        return ScopedStringAttributeValue.valueOf(stringValue.substring(0, offset), stringValue.substring(offset
-                + getDelimiter().length()));
-    }
-
-    /** {@inheritDoc} */
-    @Nonnull protected String getAttributeTypeName() {
-        return "Scoped";
-    }
-    
-    /** {@inheritDoc} */
-    public boolean equals(final Object obj) {
-        if (obj == null) {
-            return false;
-        }
-
-        if (obj == this) {
-            return true;
-        }
-        
-        if (obj instanceof ScopedStringAttributeValueMapper) {
-            final ScopedStringAttributeValueMapper other = (ScopedStringAttributeValueMapper) obj;
-            return java.util.Objects.equals(getDelimiter(), other.getDelimiter());
-        }
-        return false;
-    }
-
-    /** {@inheritDoc} */
-    public int hashCode() {
-        return Objects.hashCode(getAttributeTypeName(), getDelimiter());
-    }
-
-}
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/StringAttributeValueMapper.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/StringAttributeValueMapper.java
deleted file mode 100644
index aca6bbd..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/StringAttributeValueMapper.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping.impl;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeValueMapper;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-
-import org.opensaml.core.xml.XMLObject;
-
-/**
- * Mapping extract a {@link org.opensaml.core.xml.schema.XSString} from an {@link IdPAttributeValue}.
- */
-public class StringAttributeValueMapper extends AbstractSAMLAttributeValueMapper {
-
-    /** {@inheritDoc} */
-    @Nullable protected IdPAttributeValue<?> decodeValue(final XMLObject object) {
-        Constraint.isNotNull(object, "Object supplied to must not be null");
-        final String value = getStringValue(object);
-        if (null == value) {
-            return null;
-        }
-        return StringAttributeValue.valueOf(value);
-    }
-
-    /** {@inheritDoc} */
-    @Nonnull protected String getAttributeTypeName() {
-        return "String";
-    }
-}
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/XMLObjectAttributeValueMapper.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/XMLObjectAttributeValueMapper.java
deleted file mode 100644
index 3d59d39..0000000
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/mapping/impl/XMLObjectAttributeValueMapper.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping.impl;
-
-import java.util.List;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.XMLObjectAttributeValue;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeValueMapper;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-
-import org.opensaml.core.xml.XMLObject;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Objects;
-
-/**
- * Decoder for {@link XMLObject} into {@link XMLObjectAttributeValue}.
- */
-public class XMLObjectAttributeValueMapper extends AbstractSAMLAttributeValueMapper {
-
-    /** logger . */
-    private Logger log = LoggerFactory.getLogger(XMLObjectAttributeValueMapper.class);
-    
-    /** Do we treat the entire AttributeValue as the object to be embedded. */
-    private boolean includeAttributeValue;
-    
-    /** Do we include the attribute value, or just its first child?
-     * @return Returns the includeAttributeValue.
-     */
-    public boolean isIncludeAttributeValue() {
-        return includeAttributeValue;
-    }
-
-    /** Sets whether we look at the AttributeValue, or just its child.
-     * @param doInclude The includeAttributeValue to set.
-     */
-    public void setIncludeAttributeValue(final boolean doInclude) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        includeAttributeValue = doInclude;
-    }
-
-    
-    /** {@inheritDoc} */
-    @Nullable protected IdPAttributeValue decodeValue(@Nonnull final XMLObject object)  {
-        Constraint.isNotNull(object, "Object supplied to must not be null");
-        
-        if (includeAttributeValue) {
-            return new XMLObjectAttributeValue(object);
-        }
-        
-        final List<XMLObject> children = object.getOrderedChildren();
-        
-        if (null == children || children.isEmpty()) {
-            log.debug("{} attribute had no XML children, ignored", getLogPrefix());
-            return null;
-        }
-        if (children.size() > 1) {
-            log.debug("{} attribute has more than one child, returning first value only", getLogPrefix());
-        }
-        final XMLObject child = children.get(0);
-        log.debug("{} returning value of type {}", getLogPrefix(), child.getClass().toString());
-        return new XMLObjectAttributeValue(child);
-    }
-
-    /** {@inheritDoc} */
-    @Nonnull protected String getAttributeTypeName() {
-        return "XMLObject";
-    }
-
-    /** {@inheritDoc} */
-    public boolean equals(final Object obj) {
-        if (obj == null) {
-            return false;
-        }
-
-        if (obj == this) {
-            return true;
-        }
-        
-        if (obj instanceof XMLObjectAttributeValueMapper) {
-            final XMLObjectAttributeValueMapper other = (XMLObjectAttributeValueMapper) obj;
-            return isIncludeAttributeValue() == other.isIncludeAttributeValue(); 
-        }
-        return false;
-    }
-
-    /** {@inheritDoc} */
-    public int hashCode() {
-        return Objects.hashCode(getClass(), isIncludeAttributeValue());
-    }
-}
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1ByteAttributeTranscoder.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1ByteAttributeTranscoder.java
new file mode 100644
index 0000000..02f500c
--- /dev/null
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1ByteAttributeTranscoder.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding.impl;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.ByteAttributeValue;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLEncoderSupport;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML1AttributeTranscoder;
+import net.shibboleth.utilities.java.support.codec.Base64Support;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml1.core.AttributeDesignator;
+import org.opensaml.saml.saml1.core.AttributeValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link net.shibboleth.idp.attribute.AttributeTranscoder} that supports {@link Attribute} and
+ * {@link ByteAttributeValue} objects.
+ */
+public class SAML1ByteAttributeTranscoder extends AbstractSAML1AttributeTranscoder<ByteAttributeValue> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML1ByteAttributeTranscoder.class);
+
+    /** {@inheritDoc} */
+    @Override protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute,
+            @Nonnull final IdPAttributeValue value) {
+        return value instanceof ByteAttributeValue;
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable protected XMLObject encodeValue(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final IdPAttribute attribute, @Nonnull final TranscodingRule rule,
+            @Nonnull final ByteAttributeValue value) throws AttributeEncodingException {
+                
+        final Boolean encodeType = rule.getOrDefault(PROP_ENCODE_TYPE, Boolean.class, Boolean.TRUE);
+
+        return SAMLEncoderSupport.encodeByteArrayValue(attribute, AttributeValue.DEFAULT_ELEMENT_NAME, value.getValue(),
+                encodeType);
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable protected IdPAttributeValue<?> decodeValue(
+            @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final AttributeDesignator attribute,
+            @Nonnull final TranscodingRule rule, @Nullable final XMLObject value) {
+        
+        final String s = getStringValue(value);
+        if (null == s) {
+            return null;
+        }
+        
+        final byte[] decoded = Base64Support.decode(s);
+        if (decoded.length == 0) {
+            log.warn("Ignoring non-base64-encoded value");
+            return null;
+        }
+        
+        return ByteAttributeValue.valueOf(decoded);
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1ScopedStringAttributeTranscoder.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1ScopedStringAttributeTranscoder.java
new file mode 100644
index 0000000..4ddefdc
--- /dev/null
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1ScopedStringAttributeTranscoder.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding.impl;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.xml.namespace.QName;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLEncoderSupport;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+
+import org.opensaml.core.xml.AttributeExtensibleXMLObject;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml1.core.AttributeDesignator;
+import org.opensaml.saml.saml1.core.AttributeValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link net.shibboleth.idp.attribute.AttributeTranscoder} that supports {@link AttributeDesignator} and
+ * {@link ScopedStringAttributeValue} objects.
+ */
+public class SAML1ScopedStringAttributeTranscoder extends AbstractSAML1AttributeTranscoder<ScopedStringAttributeValue> {
+
+    /** One of "inline" or "attribute", controlling the style of XML encoding. */
+    @Nonnull @NotEmpty public static final String PROP_SCOPE_TYPE = "scopeType";
+
+    /** Name of XML attribute when scopeType property is "attribute". */
+    @Nonnull @NotEmpty public static final String PROP_SCOPE_ATTR_NAME = "scopeAttributeName";
+
+    /** Scope delimiter when scopeType property is "inline". */
+    @Nonnull @NotEmpty public static final String PROP_SCOPE_DELIMITER = "scopeDelimiter";
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML1ScopedStringAttributeTranscoder.class);
+
+    /** {@inheritDoc} */
+    @Override protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute,
+            @Nonnull final IdPAttributeValue value) {
+        return value instanceof ScopedStringAttributeValue;
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable protected XMLObject encodeValue(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final IdPAttribute attribute, @Nonnull final TranscodingRule rule,
+            @Nonnull final ScopedStringAttributeValue value) throws AttributeEncodingException {
+                
+        final Boolean encodeType = rule.getOrDefault(PROP_ENCODE_TYPE, Boolean.class, Boolean.TRUE);
+
+        final String scopeType = rule.getOrDefault(PROP_SCOPE_TYPE, String.class, "attribute");
+        
+        if ("attribute".equals(scopeType)) {
+            final String scopeAttributeName = rule.getOrDefault(PROP_SCOPE_ATTR_NAME, String.class, "Scope");
+            return SAMLEncoderSupport.encodeScopedStringValueAttribute(attribute,
+                    AttributeValue.DEFAULT_ELEMENT_NAME, value, scopeAttributeName, encodeType);
+        } else if ("inline".equals(scopeType)) {
+            final String scopeDelimiter = rule.getOrDefault(PROP_SCOPE_DELIMITER, String.class, "@");
+            return SAMLEncoderSupport.encodeScopedStringValueInline(
+                    attribute, AttributeValue.DEFAULT_ELEMENT_NAME, value, scopeDelimiter, encodeType);
+        } else {
+            throw new AttributeEncodingException("Invalid scopeType property (must be inline or attribute)");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable protected IdPAttributeValue<?> decodeValue(
+            @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final AttributeDesignator attribute,
+            @Nonnull final TranscodingRule rule, @Nullable final XMLObject value) {
+        
+        if (value == null) {
+            return null;
+        }
+        
+        final String stringValue = getStringValue(value);
+        if (null == stringValue) {
+            return null;
+        }
+
+        final String scopeType = rule.getOrDefault(PROP_SCOPE_TYPE, String.class, "attribute");
+        if ("attribute".equals(scopeType)) {
+            if (value instanceof AttributeExtensibleXMLObject) {
+                final String scopeValue = ((AttributeExtensibleXMLObject) value).getUnknownAttributes().get(
+                        new QName(rule.getOrDefault(PROP_SCOPE_ATTR_NAME, String.class, "Scope")));
+
+                if (scopeValue == null) {
+                    log.warn("Scope not found in designated XML attribute");
+                    return null;
+                }
+                
+                return ScopedStringAttributeValue.valueOf(stringValue, scopeValue);
+            } else {
+                log.warn("Object does not support required interface to access the scope via XML attribute");
+                return null;
+            }
+        } else if ("inline".equals(scopeType)) {
+            final String scopeDelimiter = rule.getOrDefault(PROP_SCOPE_DELIMITER, String.class, "@");
+            final int offset = stringValue.indexOf(scopeDelimiter);
+            if (offset < 0) {
+                log.warn("Ignoring value with no scope delimiter ({})", scopeDelimiter);
+                return null;
+            }
+
+            return ScopedStringAttributeValue.valueOf(stringValue.substring(0, offset), stringValue.substring(offset
+                    + scopeDelimiter.length()));
+            
+        } else {
+            log.error("Invalid scopeType property (must be inline or attribute)");
+            return null;
+        }
+    }
+        
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1StringAttributeEncoder.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1StringAttributeTranscoder.java
similarity index 52%
rename from idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1StringAttributeEncoder.java
rename to idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1StringAttributeTranscoder.java
index 7b80268..3e595ad 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1StringAttributeEncoder.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1StringAttributeTranscoder.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.saml.attribute.encoding.impl;
+package net.shibboleth.idp.saml.attribute.transcoding.impl;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -26,38 +26,54 @@ import net.shibboleth.idp.attribute.IdPAttributeValue;
 import net.shibboleth.idp.attribute.LocalizedStringAttributeValue;
 import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
 import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.idp.saml.attribute.encoding.AbstractSAML1AttributeEncoder;
-import net.shibboleth.idp.saml.attribute.encoding.SAMLEncoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLEncoderSupport;
 
 import org.opensaml.core.xml.XMLObject;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml1.core.AttributeDesignator;
 import org.opensaml.saml.saml1.core.AttributeValue;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * {@link net.shibboleth.idp.attribute.AttributeEncoder} that produces SAML 1 attributes from an
- * {@link net.shibboleth.idp.attribute.IdPAttribute} that contains <code>String</code> values.
+ * {@link net.shibboleth.idp.attribute.AttributeTranscoder} that supports {@link AttributeDesignator} and
+ * {@link StringAttributeValue} objects.
  */
-public class SAML1StringAttributeEncoder extends AbstractSAML1AttributeEncoder<StringAttributeValue> {
+public class SAML1StringAttributeTranscoder extends AbstractSAML1AttributeTranscoder<StringAttributeValue> {
 
     /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML1StringAttributeEncoder.class);
+    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML1StringAttributeTranscoder.class);
 
     /** {@inheritDoc} */
-    @Override
-    protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute, @Nonnull final IdPAttributeValue value) {
+    @Override protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute,
+            @Nonnull final IdPAttributeValue value) {
         return value instanceof StringAttributeValue;
     }
 
     /** {@inheritDoc} */
-    @Override
-    @Nullable protected XMLObject encodeValue(@Nonnull final IdPAttribute attribute,
+    @Override @Nullable protected XMLObject encodeValue(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final IdPAttribute attribute, @Nonnull final TranscodingRule rule,
             @Nonnull final StringAttributeValue value) throws AttributeEncodingException {
+        
         if (value instanceof LocalizedStringAttributeValue || value instanceof ScopedStringAttributeValue) {
             log.warn("Attribute '{}': Lossy encoding of attribute value of type {} to SAML1 String Attribute",
-                    attribute.getId(), value.getClass().getName());
+                    attribute.getId(), value.getClass().getSimpleName());
         }
-        return SAMLEncoderSupport.encodeStringValue(attribute,
-                AttributeValue.DEFAULT_ELEMENT_NAME, value.getValue(), encodeType());
+        
+        final Boolean encodeType = rule.getOrDefault(PROP_ENCODE_TYPE, Boolean.class, Boolean.TRUE);
+        
+        return SAMLEncoderSupport.encodeStringValue(attribute, AttributeValue.DEFAULT_ELEMENT_NAME, value.getValue(),
+                encodeType);
     }
+
+    /** {@inheritDoc} */
+    @Override @Nullable protected IdPAttributeValue<?> decodeValue(
+            @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final AttributeDesignator attribute,
+            @Nonnull final TranscodingRule rule, @Nullable final XMLObject value) {
+        
+        return value != null ? StringAttributeValue.valueOf(getStringValue(value)) : null;
+    }
+    
 }
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1XMLObjectAttributeTranscoder.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1XMLObjectAttributeTranscoder.java
new file mode 100644
index 0000000..761ce51
--- /dev/null
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1XMLObjectAttributeTranscoder.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding.impl;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.XMLObjectAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLEncoderSupport;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml1.core.AttributeDesignator;
+import org.opensaml.saml.saml1.core.AttributeValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link net.shibboleth.idp.attribute.AttributeTranscoder} that supports {@link AttributeDesignator} and
+ * {@link XMLObjectAttributeValue} objects.
+ */
+public class SAML1XMLObjectAttributeTranscoder extends AbstractSAML1AttributeTranscoder<XMLObjectAttributeValue> {
+
+    /** Property indicating whether to decode the AttributeValue element itself, or its child element. */
+    @Nonnull @NotEmpty public static final String PROP_INCLUDE_ATTR_VALUE = "includeAttributeValue";
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML1XMLObjectAttributeTranscoder.class);
+
+    /** {@inheritDoc} */
+    @Override protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute,
+            @Nonnull final IdPAttributeValue value) {
+        return value instanceof XMLObjectAttributeValue;
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable protected XMLObject encodeValue(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final IdPAttribute attribute, @Nonnull final TranscodingRule rule,
+            @Nonnull final XMLObjectAttributeValue value) throws AttributeEncodingException {
+
+        return SAMLEncoderSupport.encodeXMLObjectValue(attribute, AttributeValue.DEFAULT_ELEMENT_NAME,
+                value.getValue());
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable protected IdPAttributeValue<?> decodeValue(
+            @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final AttributeDesignator attribute,
+            @Nonnull final TranscodingRule rule, @Nullable final XMLObject value) {
+
+        if (value == null) {
+            return null;
+        }
+        
+        final Boolean includeAttributeValue = rule.getOrDefault(PROP_INCLUDE_ATTR_VALUE, Boolean.class, Boolean.FALSE);
+        if (includeAttributeValue) {
+            return new XMLObjectAttributeValue(value);
+        }
+        
+        final List<XMLObject> children = value.getOrderedChildren();
+        
+        if (null == children || children.isEmpty()) {
+            log.debug("Ignoring XMLObject with no child elements");
+            return null;
+        }
+        if (children.size() > 1) {
+            log.debug("XMLObject has more than one child, returning first element only");
+        }
+        
+        final XMLObject child = children.get(0);
+        log.debug("Returning value of type {}", child.getClass().getSimpleName());
+        return new XMLObjectAttributeValue(child);
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2ByteAttributeTranscoder.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2ByteAttributeTranscoder.java
new file mode 100644
index 0000000..b58a3d4
--- /dev/null
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2ByteAttributeTranscoder.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding.impl;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.ByteAttributeValue;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLEncoderSupport;
+import net.shibboleth.utilities.java.support.codec.Base64Support;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.AttributeValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link net.shibboleth.idp.attribute.AttributeTranscoder} that supports {@link Attribute} and
+ * {@link ByteAttributeValue} objects.
+ */
+public class SAML2ByteAttributeTranscoder extends AbstractSAML2AttributeTranscoder<ByteAttributeValue> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML2ByteAttributeTranscoder.class);
+
+    /** {@inheritDoc} */
+    @Override protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute,
+            @Nonnull final IdPAttributeValue value) {
+        return value instanceof ByteAttributeValue;
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable protected XMLObject encodeValue(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final IdPAttribute attribute, @Nonnull final TranscodingRule rule,
+            @Nonnull final ByteAttributeValue value) throws AttributeEncodingException {
+                
+        final Boolean encodeType = rule.getOrDefault(PROP_ENCODE_TYPE, Boolean.class, Boolean.TRUE);
+
+        return SAMLEncoderSupport.encodeByteArrayValue(attribute, AttributeValue.DEFAULT_ELEMENT_NAME, value.getValue(),
+                encodeType);
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable protected IdPAttributeValue<?> decodeValue(
+            @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final Attribute attribute,
+            @Nonnull final TranscodingRule rule, @Nullable final XMLObject value) {
+        
+        final String s = getStringValue(value);
+        if (null == s) {
+            return null;
+        }
+        
+        final byte[] decoded = Base64Support.decode(s);
+        if (decoded.length == 0) {
+            log.warn("Ignoring non-base64-encoded value");
+            return null;
+        }
+        
+        return ByteAttributeValue.valueOf(decoded);
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2ScopedStringAttributeTranscoder.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2ScopedStringAttributeTranscoder.java
new file mode 100644
index 0000000..5dbdf9b
--- /dev/null
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2ScopedStringAttributeTranscoder.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding.impl;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.xml.namespace.QName;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLEncoderSupport;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+
+import org.opensaml.core.xml.AttributeExtensibleXMLObject;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.AttributeValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link net.shibboleth.idp.attribute.AttributeTranscoder} that supports {@link Attribute} and
+ * {@link ScopedStringAttributeValue} objects.
+ */
+public class SAML2ScopedStringAttributeTranscoder extends AbstractSAML2AttributeTranscoder<ScopedStringAttributeValue> {
+
+    /** One of "inline" or "attribute", controlling the style of XML encoding. */
+    @Nonnull @NotEmpty public static final String PROP_SCOPE_TYPE = "scopeType";
+
+    /** Name of XML attribute when scopeType property is "attribute". */
+    @Nonnull @NotEmpty public static final String PROP_SCOPE_ATTR_NAME = "scopeAttributeName";
+
+    /** Scope delimiter when scopeType property is "inline". */
+    @Nonnull @NotEmpty public static final String PROP_SCOPE_DELIMITER = "scopeDelimiter";
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML2ScopedStringAttributeTranscoder.class);
+
+    /** {@inheritDoc} */
+    @Override protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute,
+            @Nonnull final IdPAttributeValue value) {
+        return value instanceof ScopedStringAttributeValue;
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable protected XMLObject encodeValue(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final IdPAttribute attribute, @Nonnull final TranscodingRule rule,
+            @Nonnull final ScopedStringAttributeValue value) throws AttributeEncodingException {
+                
+        final Boolean encodeType = rule.getOrDefault(PROP_ENCODE_TYPE, Boolean.class, Boolean.TRUE);
+
+        final String scopeType = rule.getOrDefault(PROP_SCOPE_TYPE, String.class, "inline");
+        
+        if ("attribute".equals(scopeType)) {
+            final String scopeAttributeName = rule.getOrDefault(PROP_SCOPE_ATTR_NAME, String.class, "Scope");
+            return SAMLEncoderSupport.encodeScopedStringValueAttribute(attribute,
+                    AttributeValue.DEFAULT_ELEMENT_NAME, value, scopeAttributeName, encodeType);
+        } else if ("inline".equals(scopeType)) {
+            final String scopeDelimiter = rule.getOrDefault(PROP_SCOPE_DELIMITER, String.class, "@");
+            return SAMLEncoderSupport.encodeScopedStringValueInline(
+                    attribute, AttributeValue.DEFAULT_ELEMENT_NAME, value, scopeDelimiter, encodeType);
+        } else {
+            throw new AttributeEncodingException("Invalid scopeType property (must be inline or attribute)");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable protected IdPAttributeValue<?> decodeValue(
+            @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final Attribute attribute,
+            @Nonnull final TranscodingRule rule, @Nullable final XMLObject value) {
+        
+        if (value == null) {
+            return null;
+        }
+        
+        final String stringValue = getStringValue(value);
+        if (null == stringValue) {
+            return null;
+        }
+
+        final String scopeType = rule.getOrDefault(PROP_SCOPE_TYPE, String.class, "inline");
+        if ("attribute".equals(scopeType)) {
+            
+            if (value instanceof AttributeExtensibleXMLObject) {
+                final String scopeValue = ((AttributeExtensibleXMLObject) value).getUnknownAttributes().get(
+                        new QName(null, rule.getOrDefault(PROP_SCOPE_ATTR_NAME, String.class, "Scope")));
+                if (scopeValue == null) {
+                    log.warn("Scope not found in designated XML attribute");
+                    return null;
+                }
+                
+                return ScopedStringAttributeValue.valueOf(stringValue, scopeValue);
+                
+            } else {
+                log.warn("Object does not support required interface to access the scope via XML attribute");
+                return null;
+            }
+        } else if ("inline".equals(scopeType)) {
+            final String scopeDelimiter = rule.getOrDefault(PROP_SCOPE_DELIMITER, String.class, "@");
+            final int offset = stringValue.indexOf(scopeDelimiter);
+            if (offset < 0) {
+                log.warn("Ignoring value with no scope delimiter ({})", scopeDelimiter);
+                return null;
+            }
+
+            return ScopedStringAttributeValue.valueOf(stringValue.substring(0, offset), stringValue.substring(offset
+                    + scopeDelimiter.length()));
+            
+        } else {
+            log.error("Invalid scopeType property (must be inline or attribute)");
+            return null;
+        }
+    }
+        
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2StringAttributeEncoder.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeTranscoder.java
similarity index 56%
rename from idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2StringAttributeEncoder.java
rename to idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeTranscoder.java
index d317a93..64d95b8 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2StringAttributeEncoder.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeTranscoder.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.saml.attribute.encoding.impl;
+package net.shibboleth.idp.saml.attribute.transcoding.impl;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -23,31 +23,28 @@ import javax.annotation.Nullable;
 import net.shibboleth.idp.attribute.AttributeEncodingException;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.IdPRequestedAttribute;
 import net.shibboleth.idp.attribute.LocalizedStringAttributeValue;
 import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
 import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.idp.saml.attribute.encoding.AbstractSAML2AttributeEncoder;
-import net.shibboleth.idp.saml.attribute.encoding.AttributeMapperProcessor;
-import net.shibboleth.idp.saml.attribute.encoding.SAMLEncoderSupport;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeMapper;
-import net.shibboleth.idp.saml.attribute.mapping.impl.StringAttributeValueMapper;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLEncoderSupport;
 
 import org.opensaml.core.xml.XMLObject;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.Attribute;
 import org.opensaml.saml.saml2.core.AttributeValue;
-import org.opensaml.saml.saml2.metadata.RequestedAttribute;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * {@link net.shibboleth.idp.attribute.AttributeEncoder} that produces a SAML 2 Attribute from an {@link IdPAttribute}
- * that contains <code>String</code> values.
+ * {@link net.shibboleth.idp.attribute.AttributeTranscoder} that supports {@link Attribute} and
+ * {@link StringAttributeValue} objects.
  */
-public class SAML2StringAttributeEncoder extends AbstractSAML2AttributeEncoder<StringAttributeValue> implements
-        AttributeMapperProcessor<RequestedAttribute, IdPRequestedAttribute> {
+public class SAML2StringAttributeTranscoder extends AbstractSAML2AttributeTranscoder<StringAttributeValue> {
 
     /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML2StringAttributeEncoder.class);
+    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML2StringAttributeTranscoder.class);
 
     /** {@inheritDoc} */
     @Override protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute,
@@ -56,22 +53,27 @@ public class SAML2StringAttributeEncoder extends AbstractSAML2AttributeEncoder<S
     }
 
     /** {@inheritDoc} */
-    @Override @Nullable protected XMLObject encodeValue(@Nonnull final IdPAttribute attribute,
+    @Override @Nullable protected XMLObject encodeValue(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final IdPAttribute attribute, @Nonnull final TranscodingRule rule,
             @Nonnull final StringAttributeValue value) throws AttributeEncodingException {
+        
         if (value instanceof LocalizedStringAttributeValue || value instanceof ScopedStringAttributeValue) {
             log.warn("Attribute '{}': Lossy encoding of attribute value of type {} to SAML2 String Attribute",
-                    attribute.getId(), value.getClass().getName());
+                    attribute.getId(), value.getClass().getSimpleName());
         }
-        return SAMLEncoderSupport.encodeStringValue(attribute,
-                AttributeValue.DEFAULT_ELEMENT_NAME, value.getValue(), encodeType());
+        
+        final Boolean encodeType = rule.getOrDefault(PROP_ENCODE_TYPE, Boolean.class, Boolean.TRUE);
+        
+        return SAMLEncoderSupport.encodeStringValue(attribute, AttributeValue.DEFAULT_ELEMENT_NAME, value.getValue(),
+                encodeType);
     }
 
     /** {@inheritDoc} */
-    @Override @Nonnull public void populateAttributeMapper(
-            final AbstractSAMLAttributeMapper<RequestedAttribute, IdPRequestedAttribute> mapper) {
-        mapper.setAttributeFormat(getNameFormat());
-        mapper.setId(getMapperId());
-        mapper.setSAMLName(getName());
-        mapper.setValueMapper(new StringAttributeValueMapper());
+    @Override @Nullable protected IdPAttributeValue<?> decodeValue(
+            @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final Attribute attribute,
+            @Nonnull final TranscodingRule rule, @Nullable final XMLObject value) {
+        
+        return value != null ? StringAttributeValue.valueOf(getStringValue(value)) : null;
     }
+    
 }
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2XMLObjectAttributeTranscoder.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2XMLObjectAttributeTranscoder.java
new file mode 100644
index 0000000..6d9ee65
--- /dev/null
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2XMLObjectAttributeTranscoder.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding.impl;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.XMLObjectAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLEncoderSupport;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.AttributeValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link net.shibboleth.idp.attribute.AttributeTranscoder} that supports {@link Attribute} and
+ * {@link XMLObjectAttributeValue} objects.
+ */
+public class SAML2XMLObjectAttributeTranscoder extends AbstractSAML2AttributeTranscoder<XMLObjectAttributeValue> {
+
+    /** Property indicating whether to decode the AttributeValue element itself, or its child element. */
+    @Nonnull @NotEmpty public static final String PROP_INCLUDE_ATTR_VALUE = "includeAttributeValue";
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML2XMLObjectAttributeTranscoder.class);
+
+    /** {@inheritDoc} */
+    @Override protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute,
+            @Nonnull final IdPAttributeValue value) {
+        return value instanceof XMLObjectAttributeValue;
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable protected XMLObject encodeValue(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final IdPAttribute attribute, @Nonnull final TranscodingRule rule,
+            @Nonnull final XMLObjectAttributeValue value) throws AttributeEncodingException {
+
+        return SAMLEncoderSupport.encodeXMLObjectValue(attribute, AttributeValue.DEFAULT_ELEMENT_NAME,
+                value.getValue());
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable protected IdPAttributeValue<?> decodeValue(
+            @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final Attribute attribute,
+            @Nonnull final TranscodingRule rule, @Nullable final XMLObject value) {
+
+        if (value == null) {
+            return null;
+        }
+        
+        final Boolean includeAttributeValue = rule.getOrDefault(PROP_INCLUDE_ATTR_VALUE, Boolean.class, Boolean.FALSE);
+        if (includeAttributeValue) {
+            return new XMLObjectAttributeValue(value);
+        }
+        
+        final List<XMLObject> children = value.getOrderedChildren();
+        
+        if (null == children || children.isEmpty()) {
+            log.debug("Ignoring XMLObject with no child elements");
+            return null;
+        }
+        if (children.size() > 1) {
+            log.debug("XMLObject has more than one child, returning first element only");
+        }
+        
+        final XMLObject child = children.get(0);
+        log.debug("Returning value of type {}", child.getClass().getSimpleName());
+        return new XMLObjectAttributeValue(child);
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/package-info.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/package-info.java
similarity index 85%
rename from idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/package-info.java
rename to idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/package-info.java
index 3760d3e..d7ff608 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/encoding/impl/package-info.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/package-info.java
@@ -15,6 +15,8 @@
  * limitations under the License.
  */
 
-/** SAML specific {@link net.shibboleth.idp.attribute.AttributeEncoder} implementations. */
+/**
+ * Implementations of SAML transcoders.
+ */
 
-package net.shibboleth.idp.saml.attribute.encoding.impl;
\ No newline at end of file
+package net.shibboleth.idp.saml.attribute.transcoding.impl;
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/metadata/impl/AttributeMappingNodeProcessor.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/metadata/impl/AttributeMappingNodeProcessor.java
index 1fe014d..e2e8c72 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/metadata/impl/AttributeMappingNodeProcessor.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/metadata/impl/AttributeMappingNodeProcessor.java
@@ -17,21 +17,22 @@
 
 package net.shibboleth.idp.saml.metadata.impl;
 
-import java.time.Instant;
-import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import javax.annotation.concurrent.NotThreadSafe;
 
+import net.shibboleth.idp.attribute.AttributeDecodingException;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.IdPRequestedAttribute;
-import net.shibboleth.idp.attribute.resolver.AttributeResolver;
-import net.shibboleth.idp.saml.attribute.mapping.AttributesMapContainer;
-import net.shibboleth.idp.saml.attribute.mapping.impl.RequestedAttributesMapper;
-import net.shibboleth.idp.saml.attribute.mapping.impl.SAML2AttributesMapper;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.saml.attribute.transcoding.AttributesMapContainer;
+import net.shibboleth.utilities.java.support.annotation.constraint.Live;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 import net.shibboleth.utilities.java.support.service.ReloadableService;
 import net.shibboleth.utilities.java.support.service.ServiceableComponent;
@@ -48,6 +49,7 @@ import org.opensaml.saml.saml2.metadata.RequestedAttribute;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Multimap;
 
 /**
@@ -60,178 +62,138 @@ public class AttributeMappingNodeProcessor implements MetadataNodeProcessor {
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(AttributeMappingNodeProcessor.class);
 
-    /** Service used to get the resolver used to fetch attributes. */
-    @Nonnull private final ReloadableService<AttributeResolver> attributeResolverService;
-
-    /** Whether the last invocation of {@link #refreshMappers()} failed. */
-    @Nonnull private boolean refreshFailed;
-
-    /** Cached RequestedAttributeMapper. */
-    @Nullable private RequestedAttributesMapper requestedAttributesMapper;
-
-    /** Cached AttributeMapper. */
-    @Nullable private SAML2AttributesMapper attributesMapper;
-
-    /** Date when the cache was last refreshed. */
-    @Nullable private Instant lastReload;
+    /** Service used to get the registry of decoding rules. */
+    @Nonnull private final ReloadableService<AttributeTranscoderRegistry> transcoderRegistry;
 
     /**
      * Constructor.
      * 
-     * @param resolverService the service for the attribute resolver we are to derive unmapping info from
-     */
-    public AttributeMappingNodeProcessor(@Nonnull final ReloadableService<AttributeResolver> resolverService) {
-        attributeResolverService = Constraint.isNotNull(resolverService, "AttributeResolver cannot be null");
-    }
-
-    /**
-     * Get the current RequestedAttributesMapper.
-     * 
-     * @return Returns the mapper.
-     */
-    public RequestedAttributesMapper getRequestedAttributesMapper() {
-        return requestedAttributesMapper;
-    }
-
-    /**
-     * Set the current RequestedAttributesMapper.
-     * 
-     * @param mapper what to set.
+     * @param registry the service for the decoding rules
      */
-    public void setRequestedAttributesMapper(@Nullable final RequestedAttributesMapper mapper) {
-        requestedAttributesMapper = mapper;
+    public AttributeMappingNodeProcessor(@Nonnull final ReloadableService<AttributeTranscoderRegistry> registry) {
+        transcoderRegistry = Constraint.isNotNull(registry, "AttributeTranscoderRegistry cannot be null");
     }
-
-    /**
-     * Get the current AttributesMapper.
-     * 
-     * @return Returns the mapper.
-     */
-    public SAML2AttributesMapper getAttributesMapper() {
-        return attributesMapper;
-    }
-
-    /**
-     * Set the current AttributesMapper.
-     * 
-     * @param mapper what to set.
-     */
-    public void setAttributesMapper(@Nullable final SAML2AttributesMapper mapper) {
-        attributesMapper = mapper;
-    }
-
-    /**
-     * Inspect the service and see whether we need to reload the mappers.
-     * 
-     * @throws FilterException if the mapping generation fails
-     */
-    protected void refreshMappers() throws FilterException {
-        if (lastReload != null && lastReload.equals(attributeResolverService.getLastSuccessfulReloadInstant())) {
-            // Nothing has changed since we last reloaded.
-            return;
-        }
-
-        // Reload
-        ServiceableComponent<AttributeResolver> component = null;
-        RequestedAttributesMapper ram = null;
-        SAML2AttributesMapper am = null;
+    
+    /** {@inheritDoc} */
+    @Override public void process(final XMLObject metadataNode) throws FilterException {
+        
+        ServiceableComponent<AttributeTranscoderRegistry> component = null;
+        
         try {
-            // get date before we get the component. That way we'll not leak changes.
-            final Instant when = attributeResolverService.getLastSuccessfulReloadInstant();
-            component = attributeResolverService.getServiceableComponent();
-            if (null == component) {
-                if (!refreshFailed) {
-                    log.error("Requested Attributes Mapper: Invalid Attribute resolver configuration.");
+            if (metadataNode instanceof AttributeConsumingService) {
+                component = transcoderRegistry.getServiceableComponent();
+                if (component == null) {
+                    log.error("Attribute transcoding service unavailable");
+                } else {
+                    handleAttributeConsumingService(component.getComponent(), (AttributeConsumingService) metadataNode);
+                }
+            } else if (metadataNode instanceof EntityDescriptor) {
+                component = transcoderRegistry.getServiceableComponent();
+                if (component == null) {
+                    log.error("Attribute transcoding service unavailable");
+                } else {
+                    handleEntityDescriptor(component.getComponent(), (EntityDescriptor) metadataNode);
                 }
-                refreshFailed = true;
-            } else {
-                final AttributeResolver attributeResolver = component.getComponent();
-                ram = new RequestedAttributesMapper(attributeResolver);
-                am = new SAML2AttributesMapper(attributeResolver);
-
-                refreshFailed = false;
-                lastReload = when;
             }
         } finally {
-            if (null != component) {
+            if (component != null) {
                 component.unpinComponent();
             }
         }
-        try {
-            if (null != ram) {
-                ram.initialize();
-            }
-            if (null != am) {
-                am.initialize();
-            }
-        } catch (final ComponentInitializationException e) {
-            throw new FilterException(e);
-        }
-        setRequestedAttributesMapper(ram);
-        setAttributesMapper(am);
-    }
-
-    /** {@inheritDoc} */
-    @Override public void process(final XMLObject metadataNode) throws FilterException {
-        refreshMappers();
-        if (metadataNode instanceof AttributeConsumingService) {
-
-            handleAttributeConsumingService((AttributeConsumingService) metadataNode);
-
-        } else if (metadataNode instanceof EntityDescriptor) {
-            handleEntityDescriptor((EntityDescriptor) metadataNode);
-        }
     }
 
     /**
      * Look inside the {@link AttributeConsumingService} for any {@link RequestedAttribute}s and map them.
      * 
+     * @param registry the registry service
      * @param acs the {@link AttributeConsumingService} to look at
      */
-//CheckStyle: ReturnCount OFF
-    private void handleAttributeConsumingService(final AttributeConsumingService acs) {
+    private void handleAttributeConsumingService(@Nonnull final AttributeTranscoderRegistry registry,
+            @Nonnull final AttributeConsumingService acs) {
+        
         final List<RequestedAttribute> requestedAttributes = acs.getRequestAttributes();
-        final RequestedAttributesMapper mapper = getRequestedAttributesMapper();
-        if (null == requestedAttributes || requestedAttributes.isEmpty() || null == mapper) {
+        if (null == requestedAttributes || requestedAttributes.isEmpty()) {
             return;
         }
-        final Multimap<String, IdPRequestedAttribute> maps = mapper.mapAttributes(requestedAttributes);
-        if (null == maps || maps.isEmpty()) {
-            return;
+        
+        final Multimap<String,IdPAttribute> results = HashMultimap.create();
+        for (final RequestedAttribute req : requestedAttributes) {
+            try {
+                decodeAttribute(registry, req, results);
+            } catch (final AttributeDecodingException e) {
+                log.warn("Error decoding RequestedAttribute '{}'", req.getName(), e);
+            }
+        }
+        
+        if (!results.isEmpty()) {
+            acs.getObjectMetadata().put(new AttributesMapContainer<>(results));
         }
-        acs.getObjectMetadata().put(new AttributesMapContainer<>(maps));
     }
-//CheckStyle: ReturnCount ON
 
     /**
      * Look inside the {@link EntityDescriptor} for entities Attributes and map them.
      * 
+     * @param registry the registry service
      * @param entity the entity
      */
 //CheckStyle: ReturnCount OFF
-    private void handleEntityDescriptor(final EntityDescriptor entity) {
-        final SAML2AttributesMapper mapper = getAttributesMapper();
+    private void handleEntityDescriptor(@Nonnull final AttributeTranscoderRegistry registry,
+            @Nonnull final EntityDescriptor entity) {
         final Extensions extensions = entity.getExtensions();
-        if (null == extensions || null == mapper) {
+        if (null == extensions) {
             return;
         }
+        
         final List<XMLObject> entityAttributesList =
                 extensions.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME);
         if (null == entityAttributesList || entityAttributesList.isEmpty()) {
             return;
         }
-        final List<Attribute> entityAttributes = new ArrayList<>();
+        
+        final Multimap<String,IdPAttribute> results = HashMultimap.create();
+        
         for (final XMLObject xmlObj : entityAttributesList) {
             if (xmlObj instanceof EntityAttributes) {
                 final EntityAttributes ea = (EntityAttributes) xmlObj;
-                entityAttributes.addAll(ea.getAttributes());
+                for (final Attribute attr : ea.getAttributes()) {
+                    try {
+                        decodeAttribute(registry, attr, results);
+                    } catch (final AttributeDecodingException e) {
+                        log.warn("Error decoding RequestedAttribute '{}'", attr.getName(), e);
+                    }
+                }
             }
         }
-        final Multimap<String, IdPAttribute> maps = mapper.mapAttributes(entityAttributes);
-        if (null == maps || maps.isEmpty()) {
-            return;
+        
+        if (!results.isEmpty()) {
+            entity.getObjectMetadata().put(new AttributesMapContainer<>(results));
         }
-        entity.getObjectMetadata().put(new AttributesMapContainer<>(maps));
     }
   //CheckStyle: ReturnCount ON
-}
+
+    /**
+     * Access the registry of transcoding rules to decode the input object.
+     * 
+     * @param <T> input type
+     * @param registry  registry of transcoding rules
+     * @param input input object
+     * @param results collection to add results to
+     * 
+     * @throws AttributeDecodingException if an error occurs or no results were obtained
+     */
+    protected <T> void decodeAttribute(@Nonnull final AttributeTranscoderRegistry registry,
+            @Nonnull final T input, @Nonnull @NonnullElements @Live final Multimap<String,IdPAttribute> results)
+                    throws AttributeDecodingException {
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(input);
+        
+        for (final TranscodingRule rules : rulesets) {
+            final AttributeTranscoder<T> transcoder = TranscoderSupport.getTranscoder(rules);
+            final IdPAttribute decodedAttribute = transcoder.decode(null, input, rules);
+            if (decodedAttribute != null) {
+                results.put(decodedAttribute.getId(), decodedAttribute);
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/BaseAddAttributeStatementToAssertion.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/BaseAddAttributeStatementToAssertion.java
index e48e2b6..bcadefb 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/BaseAddAttributeStatementToAssertion.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/BaseAddAttributeStatementToAssertion.java
@@ -17,12 +17,19 @@
 
 package net.shibboleth.idp.saml.profile.impl;
 
+import java.util.Collection;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.context.AttributeContext;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
 import net.shibboleth.idp.profile.AbstractProfileAction;
 import net.shibboleth.idp.profile.config.navigate.IdentifierGenerationStrategyLookupFunction;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
@@ -31,12 +38,17 @@ import net.shibboleth.idp.profile.context.navigate.ResponderIdLookupFunction;
 import org.opensaml.profile.action.ActionSupport;
 import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.common.SAMLObject;
 
+import net.shibboleth.utilities.java.support.annotation.constraint.Live;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 import net.shibboleth.utilities.java.support.security.IdentifierGenerationStrategy;
+import net.shibboleth.utilities.java.support.service.ReloadableService;
 
 import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.slf4j.Logger;
@@ -49,11 +61,13 @@ import org.slf4j.LoggerFactory;
  * an {@link AttributeContext} returned from a
  * lookup strategy, by default located on the {@link RelyingPartyContext} beneath the profile request context.</p>
  * 
+ * @param <T> type of objects being encoded
+ * 
  * @event {@link EventIds#PROCEED_EVENT_ID}
  * @event {@link EventIds#INVALID_MSG_CTX}
  * @event {@link EventIds#INVALID_PROFILE_CTX}
  */
-public abstract class BaseAddAttributeStatementToAssertion extends AbstractProfileAction {
+public abstract class BaseAddAttributeStatementToAssertion<T extends SAMLObject> extends AbstractProfileAction {
 
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(BaseAddAttributeStatementToAssertion.class);
@@ -79,6 +93,9 @@ public abstract class BaseAddAttributeStatementToAssertion extends AbstractProfi
      */
     @Nonnull private Function<ProfileRequestContext,AttributeContext> attributeContextLookupStrategy;
 
+    /** Transcoder registry service object. */
+    @NonnullAfterInit private ReloadableService<AttributeTranscoderRegistry> transcoderRegistry;
+    
     /** AttributeContext to use. */
     @Nullable private AttributeContext attributeCtx;
 
@@ -187,6 +204,26 @@ public abstract class BaseAddAttributeStatementToAssertion extends AbstractProfi
 
         issuerLookupStrategy = Constraint.isNotNull(strategy, "Issuer lookup strategy cannot be null");
     }
+
+    /**
+     * Gets the registry of transcoding rules to apply to encode attributes.
+     * 
+     * @return registry
+     */
+    @NonnullAfterInit public ReloadableService<AttributeTranscoderRegistry> getTranscoderRegistry() {
+        return transcoderRegistry;
+    }
+    
+    /**
+     * Sets the registry of transcoding rules to apply to encode attributes.
+     * 
+     * @param registry registry service interface
+     */
+    public void setTranscoderRegistry(@Nonnull final ReloadableService<AttributeTranscoderRegistry> registry) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        transcoderRegistry = Constraint.isNotNull(registry, "AttributeTranscoderRegistry cannot be null");
+    }
     
     /**
      * Get the {@link AttributeContext} to encode.
@@ -220,6 +257,16 @@ public abstract class BaseAddAttributeStatementToAssertion extends AbstractProfi
 
     /** {@inheritDoc} */
     @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (transcoderRegistry == null) {
+            throw new ComponentInitializationException("AttributeTranscoderRegistry cannot be null");
+        }
+    }
+    
+    /** {@inheritDoc} */
+    @Override
     protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
         if (!super.doPreExecute(profileRequestContext)) {
             return false;
@@ -250,4 +297,52 @@ public abstract class BaseAddAttributeStatementToAssertion extends AbstractProfi
         return true;
     }
 
+
+    /**
+     * Access the registry of transcoding rules to transform the input attribute into a target type.
+     * 
+     * @param registry  registry of transcoding rules
+     * @param profileRequestContext current profile request context
+     * @param attribute input attribute
+     * @param to target type
+     * @param results collection to add results to
+     * 
+     * @return number of results added
+     * 
+     * @throws AttributeEncodingException if a non-ignorable error occurs
+     */
+    protected int encodeAttribute(@Nonnull final AttributeTranscoderRegistry registry,
+            @Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final IdPAttribute attribute,
+            @Nonnull final Class<T> to, @Nonnull @NonnullElements @Live final Collection<T> results)
+                    throws AttributeEncodingException {
+        
+        final Collection<TranscodingRule> transcodingRules = registry.getTranscodingRules(attribute, to);
+        if (transcodingRules.isEmpty()) {
+            log.debug("{} Attribute {} does not have any transcoding rules, nothing to do", getLogPrefix(),
+                    attribute.getId());
+            return 0;
+        }
+        
+        int count = 0;
+        
+        for (final TranscodingRule rules : transcodingRules) {
+            try {
+                final AttributeTranscoder<T> transcoder = TranscoderSupport.<T>getTranscoder(rules);
+                final T encodedAttribute = transcoder.encode(profileRequestContext, attribute, to, rules);
+                if (encodedAttribute != null) {
+                    results.add(encodedAttribute);
+                    count++;
+                }
+            } catch (final AttributeEncodingException e) {
+                if (isIgnoringUnencodableAttributes()) {
+                    log.debug("{} Unable to encode attribute {}", getLogPrefix(), attribute.getId(), e);
+                } else {
+                    throw e;
+                }
+            }
+        }
+        
+        return count;
+    }
+    
 }
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml1/profile/impl/AddAttributeStatementToAssertion.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml1/profile/impl/AddAttributeStatementToAssertion.java
index b3ccd54..9a94785 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml1/profile/impl/AddAttributeStatementToAssertion.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml1/profile/impl/AddAttributeStatementToAssertion.java
@@ -19,29 +19,27 @@ package net.shibboleth.idp.saml.saml1.profile.impl;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Set;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import net.shibboleth.idp.attribute.AttributeEncoder;
 import net.shibboleth.idp.attribute.AttributeEncodingException;
 import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
 import net.shibboleth.idp.profile.IdPEventIds;
-import net.shibboleth.idp.saml.attribute.encoding.SAML1AttributeEncoder;
 import net.shibboleth.idp.saml.profile.impl.BaseAddAttributeStatementToAssertion;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NullableElements;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.service.ServiceableComponent;
 
 import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
 import org.opensaml.profile.action.ActionSupport;
 import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.saml.common.SAMLObjectBuilder;
-import org.opensaml.saml.common.xml.SAMLConstants;
 import org.opensaml.saml.saml1.core.Assertion;
 import org.opensaml.saml.saml1.core.Attribute;
 import org.opensaml.saml.saml1.core.AttributeStatement;
@@ -68,7 +66,7 @@ import com.google.common.collect.Collections2;
  * @event {@link EventIds#INVALID_MSG_CTX}
  * @event {@link IdPEventIds#UNABLE_ENCODE_ATTRIBUTE}
  */
-public class AddAttributeStatementToAssertion extends BaseAddAttributeStatementToAssertion {
+public class AddAttributeStatementToAssertion extends BaseAddAttributeStatementToAssertion<Attribute> {
 
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(AddAttributeStatementToAssertion.class);
@@ -137,8 +135,20 @@ public class AddAttributeStatementToAssertion extends BaseAddAttributeStatementT
         }
 
         final ArrayList<Attribute> encodedAttributes = new ArrayList<>(attributes.size());
-        for (final IdPAttribute attribute : Collections2.filter(attributes, Predicates.notNull())) {
-            encodeAttribute(profileRequestContext, attribute, encodedAttributes);
+        
+        ServiceableComponent<AttributeTranscoderRegistry> component = null;
+        try {
+            component = getTranscoderRegistry().getServiceableComponent();
+            if (component == null) {
+                throw new AttributeEncodingException("Attribute transoding service unavailable");
+            }
+            for (final IdPAttribute attribute : Collections2.filter(attributes, Predicates.notNull())) {
+                encodeAttribute(component.getComponent(), profileRequestContext, attribute, encodedAttributes);
+            }
+        } finally {
+            if (null != component) {
+                component.unpinComponent();
+            }
         }
 
         if (encodedAttributes.isEmpty()) {
@@ -156,50 +166,25 @@ public class AddAttributeStatementToAssertion extends BaseAddAttributeStatementT
     }
 
     /**
-     * Encodes a {@link IdPAttribute} into zero or more {@link Attribute} objects if a proper encoder is available.
+     * Encodes {@link IdPAttribute} into zero or more {@link Attribute} objects if a proper encoder is available.
      * 
+     * @param registry transcoding registry
      * @param profileRequestContext current profile request context
      * @param attribute the attribute to be encoded
      * @param results collection to add the encoded SAML attributes to
      * 
      * @throws AttributeEncodingException thrown if there is a problem encoding an attribute
      */
-    private void encodeAttribute(@Nonnull final ProfileRequestContext profileRequestContext,
+    private void encodeAttribute(@Nonnull final AttributeTranscoderRegistry registry,
+            @Nonnull final ProfileRequestContext profileRequestContext,
             @Nonnull final IdPAttribute attribute, @Nonnull @NonnullElements final Collection<Attribute> results)
                     throws AttributeEncodingException {
 
         log.debug("{} Attempting to encode attribute {} as a SAML 1 Attribute", getLogPrefix(), attribute.getId());
-        
-        final Set<AttributeEncoder<?>> encoders = attribute.getEncoders();
-        if (encoders.isEmpty()) {
-            log.debug("{} Attribute {} does not have any encoders, nothing to do", getLogPrefix(), attribute.getId());
-            return;
-        }
-        
-        boolean added = false; 
-        
-        for (final AttributeEncoder<?> encoder : encoders) {
-            if (SAMLConstants.SAML11P_NS.equals(encoder.getProtocol())
-                    && encoder instanceof SAML1AttributeEncoder
-                    && encoder.getActivationCondition().test(profileRequestContext)) {
-                log.debug("{} Encoding attribute {} as a SAML 1 Attribute", getLogPrefix(), attribute.getId());
-                try {
-                    added = true;
-                    results.add((Attribute) encoder.encode(attribute));
-                } catch (final AttributeEncodingException e) {
-                    if (isIgnoringUnencodableAttributes()) {
-                        log.debug("{} Unable to encode attribute {} as SAML 1 attribute", getLogPrefix(),
-                                attribute.getId(), e);
-                    } else {
-                        throw e;
-                    }
-                }
-            }
-        }
 
-        if (!added) {
-            log.debug(
-                    "{} Attribute {} did not have a usable SAML 1 Attribute encoder associated with it, nothing to do",
+        // Uses the new registry.
+        if (super.encodeAttribute(registry, profileRequestContext, attribute, Attribute.class, results) == 0) {
+            log.debug("{} Attribute {} did not have SAML 1 Attribute transcoder instructions associated, nothing to do",
                     getLogPrefix(), attribute.getId());
         }
     }
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml1/profile/impl/FilterByQueriedAttributeDesignators.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml1/profile/impl/FilterByQueriedAttributeDesignators.java
index d6bba98..b773978 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml1/profile/impl/FilterByQueriedAttributeDesignators.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml1/profile/impl/FilterByQueriedAttributeDesignators.java
@@ -19,6 +19,8 @@ package net.shibboleth.idp.saml.saml1.profile.impl;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
@@ -26,6 +28,8 @@ import javax.annotation.Nullable;
 
 import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.opensaml.messaging.context.navigate.MessageLookup;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
 import org.opensaml.saml.saml1.core.AttributeDesignator;
@@ -34,15 +38,23 @@ import org.opensaml.saml.saml1.core.Request;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.Multimap;
-
+import net.shibboleth.idp.attribute.AttributeDecodingException;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.context.AttributeContext;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
 import net.shibboleth.idp.profile.AbstractProfileAction;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
-import net.shibboleth.idp.saml.attribute.mapping.AttributesMapper;
+import net.shibboleth.utilities.java.support.annotation.constraint.Live;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.service.ReloadableService;
+import net.shibboleth.utilities.java.support.service.ServiceableComponent;
 
 /**
  * Action that filters a set of attributes against the {@link org.opensaml.saml.saml1.core.AttributeDesignator}
@@ -55,9 +67,9 @@ public class FilterByQueriedAttributeDesignators extends AbstractProfileAction {
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(FilterByQueriedAttributeDesignators.class);
 
-    /** Mapper used to get the engine used to filter attributes. */
-    @Nonnull private final  AttributesMapper<AttributeDesignator,IdPAttribute> map;
-
+    /** Transcoder registry service object. */
+    @NonnullAfterInit private ReloadableService<AttributeTranscoderRegistry> transcoderRegistry;
+    
     /** Strategy used to locate the {@link Request} containing the query to filter against. */
     @Nonnull private Function<ProfileRequestContext,Request> requestLookupStrategy;
 
@@ -70,15 +82,8 @@ public class FilterByQueriedAttributeDesignators extends AbstractProfileAction {
     /** AttributeContext to filter. */
     @Nullable private AttributeContext attributeContext;
 
-    /**
-     * Constructor.
-     * 
-     * @param mapper mapper used to consume designators
-     */
-    public FilterByQueriedAttributeDesignators(@Nonnull final
-            AttributesMapper<AttributeDesignator,IdPAttribute> mapper) {
-        map = Constraint.isNotNull(mapper, "Mapper cannot be null");
-        
+    /** Constructor. */
+    public FilterByQueriedAttributeDesignators() {
         attributeContextLookupStrategy = new ChildContextLookup<>(AttributeContext.class).compose(
                 new ChildContextLookup<>(RelyingPartyContext.class));
         
@@ -86,6 +91,17 @@ public class FilterByQueriedAttributeDesignators extends AbstractProfileAction {
     }
 
     /**
+     * Sets the registry of transcoding rules to apply to encode attributes.
+     * 
+     * @param registry registry service interface
+     */
+    public void setTranscoderRegistry(@Nonnull final ReloadableService<AttributeTranscoderRegistry> registry) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        transcoderRegistry = Constraint.isNotNull(registry, "AttributeTranscoderRegistry cannot be null");
+    }
+    
+    /**
      * Set the strategy used to locate the {@link Request} associated with a given {@link ProfileRequestContext}.
      * 
      * @param strategy lookup strategy
@@ -112,6 +128,16 @@ public class FilterByQueriedAttributeDesignators extends AbstractProfileAction {
 
     /** {@inheritDoc} */
     @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (transcoderRegistry == null) {
+            throw new ComponentInitializationException("AttributeTranscoderRegistry cannot be null");
+        }
+    }
+    
+    /** {@inheritDoc} */
+    @Override
     protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
         
         if (!super.doPreExecute(profileRequestContext)) {
@@ -146,13 +172,37 @@ public class FilterByQueriedAttributeDesignators extends AbstractProfileAction {
     @Override
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
         
-        final Collection<IdPAttribute> keepers = new ArrayList<>(query.getAttributeDesignators().size());
+        final Set<String> decodedAttributeIds = new HashSet<>();
+
+        ServiceableComponent<AttributeTranscoderRegistry> component = null;
+        try {
+            component = transcoderRegistry.getServiceableComponent();
+            if (component == null) {
+                log.error("Attribute transcoder service unavailable");
+                ActionSupport.buildEvent(profileRequestContext, EventIds.MESSAGE_PROC_ERROR);
+                return;
+            }
+
+            for (final AttributeDesignator designator : query.getAttributeDesignators()) {
+                try {
+                    decodeAttributeDesignator(component.getComponent(), profileRequestContext, designator,
+                            decodedAttributeIds);
+                } catch (final AttributeDecodingException e) {
+                    log.warn("{} Error decoding AttributeDesignators", getLogPrefix(), e);
+                }
+            }
+        } finally {
+            if (component != null) {
+                component.unpinComponent();
+            }
+        }
+                
         
-        final Multimap<String,IdPAttribute> mapped = map.mapAttributes(query.getAttributeDesignators());
-        log.debug("Query content mapped to attribute IDs: {}", mapped.keySet());
+        final Collection<IdPAttribute> keepers = new ArrayList<>(query.getAttributeDesignators().size());
+        log.debug("Query content mapped to attribute IDs: {}", decodedAttributeIds);
         
         for (final IdPAttribute attribute : attributeContext.getIdPAttributes().values()) {
-            if (mapped.containsKey(attribute.getId())) {
+            if (decodedAttributeIds.contains(attribute.getId())) {
                 log.debug("Retaining attribute '{}' requested by query", attribute.getId());
                 keepers.add(attribute);
             } else {
@@ -163,4 +213,34 @@ public class FilterByQueriedAttributeDesignators extends AbstractProfileAction {
         attributeContext.setIdPAttributes(keepers);
     }
 
+    /**
+     * Access the registry of transcoding rules to decode the input {@link AttributeDesignator}.
+     * 
+     * @param registry  registry of transcoding rules
+     * @param profileRequestContext current profile request context
+     * @param input input object
+     * @param results collection to add attributeIDs to
+     * 
+     * @throws AttributeDecodingException if an error occurs or no results were obtained
+     */
+    private void decodeAttributeDesignator(@Nonnull final AttributeTranscoderRegistry registry,
+            @Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final AttributeDesignator input,
+            @Nonnull @NonnullElements @Live final Collection<String> results)
+                    throws AttributeDecodingException {
+        
+        final Collection<TranscodingRule> transcodingRules = registry.getTranscodingRules(input);
+        if (transcodingRules.isEmpty()) {
+            throw new AttributeDecodingException("No transcoding rule for AttributeDesignator '" +
+                    input.getAttributeName() + "'");
+        }
+        
+        for (final TranscodingRule rules : transcodingRules) {
+            final AttributeTranscoder<AttributeDesignator> transcoder = TranscoderSupport.getTranscoder(rules);
+            final IdPAttribute decodedAttribute = transcoder.decode(profileRequestContext, input, rules);
+            if (decodedAttribute != null) {
+                results.add(decodedAttribute.getId());
+            }
+        }
+    }
+
 }
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAttributeStatementToAssertion.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAttributeStatementToAssertion.java
index 56f03b2..d3e2f04 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAttributeStatementToAssertion.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAttributeStatementToAssertion.java
@@ -19,29 +19,27 @@ package net.shibboleth.idp.saml.saml2.profile.impl;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Set;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import net.shibboleth.idp.attribute.AttributeEncoder;
 import net.shibboleth.idp.attribute.AttributeEncodingException;
 import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
 import net.shibboleth.idp.profile.IdPEventIds;
-import net.shibboleth.idp.saml.attribute.encoding.SAML2AttributeEncoder;
 import net.shibboleth.idp.saml.profile.impl.BaseAddAttributeStatementToAssertion;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NullableElements;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.service.ServiceableComponent;
 
 import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
 import org.opensaml.profile.action.ActionSupport;
 import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.saml.common.SAMLObjectBuilder;
-import org.opensaml.saml.common.xml.SAMLConstants;
 import org.opensaml.saml.saml2.core.Assertion;
 import org.opensaml.saml.saml2.core.Attribute;
 import org.opensaml.saml.saml2.core.AttributeStatement;
@@ -69,7 +67,7 @@ import com.google.common.collect.Collections2;
  * @event {@link EventIds#INVALID_MSG_CTX}
  * @event {@link IdPEventIds#UNABLE_ENCODE_ATTRIBUTE}
  */
-public class AddAttributeStatementToAssertion extends BaseAddAttributeStatementToAssertion {
+public class AddAttributeStatementToAssertion extends BaseAddAttributeStatementToAssertion<Attribute> {
 
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(AddAttributeStatementToAssertion.class);
@@ -139,8 +137,20 @@ public class AddAttributeStatementToAssertion extends BaseAddAttributeStatementT
         }
 
         final ArrayList<Attribute> encodedAttributes = new ArrayList<>(attributes.size());
-        for (final IdPAttribute attribute : Collections2.filter(attributes, Predicates.notNull())) {
-            encodeAttribute(profileRequestContext, attribute, encodedAttributes);
+        
+        ServiceableComponent<AttributeTranscoderRegistry> component = null;
+        try {
+            component = getTranscoderRegistry().getServiceableComponent();
+            if (component == null) {
+                throw new AttributeEncodingException("Attribute transoding service unavailable");
+            }
+            for (final IdPAttribute attribute : Collections2.filter(attributes, Predicates.notNull())) {
+                encodeAttribute(component.getComponent(), profileRequestContext, attribute, encodedAttributes);
+            }
+        } finally {
+            if (null != component) {
+                component.unpinComponent();
+            }
         }
 
         if (encodedAttributes.isEmpty()) {
@@ -160,48 +170,23 @@ public class AddAttributeStatementToAssertion extends BaseAddAttributeStatementT
     /**
      * Encodes a {@link IdPAttribute} into zero or more {@link Attribute} objects if a proper encoder is available.
      * 
+     * @param registry transcoding registry
      * @param profileRequestContext current profile request context
      * @param attribute the attribute to be encoded
      * @param results collection to add the encoded SAML attributes to
      * 
      * @throws AttributeEncodingException thrown if there is a problem encoding an attribute
      */
-    private void encodeAttribute(@Nonnull final ProfileRequestContext profileRequestContext,
+    private void encodeAttribute(@Nonnull final AttributeTranscoderRegistry registry,
+            @Nonnull final ProfileRequestContext profileRequestContext,
             @Nonnull final IdPAttribute attribute, @Nonnull @NonnullElements final Collection<Attribute> results)
                     throws AttributeEncodingException {
 
         log.debug("{} Attempting to encode attribute {} as a SAML 2 Attribute", getLogPrefix(), attribute.getId());
-        
-        final Set<AttributeEncoder<?>> encoders = attribute.getEncoders();
-        if (encoders.isEmpty()) {
-            log.debug("{} Attribute {} does not have any encoders, nothing to do", getLogPrefix(), attribute.getId());
-            return;
-        }
-
-        boolean added = false;
-
-        for (final AttributeEncoder<?> encoder : encoders) {
-            if (SAMLConstants.SAML20P_NS.equals(encoder.getProtocol())
-                    && encoder instanceof SAML2AttributeEncoder
-                    && encoder.getActivationCondition().test(profileRequestContext)) {
-                log.debug("{} Encoding attribute {} as a SAML 2 Attribute", getLogPrefix(), attribute.getId());
-                try {
-                    results.add((Attribute) encoder.encode(attribute));
-                    added = true;
-                } catch (final AttributeEncodingException e) {
-                    if (isIgnoringUnencodableAttributes()) {
-                        log.debug("{} Unable to encode attribute {} as SAML 2 attribute", getLogPrefix(),
-                                attribute.getId(), e);
-                    } else {
-                        throw e;
-                    }
-                }
-            }
-        }
 
-        if (!added) {
-            log.debug(
-                    "{} Attribute {} did not have a usable SAML 2 Attribute encoder associated with it, nothing to do",
+        // Uses the new registry.
+        if (super.encodeAttribute(registry, profileRequestContext, attribute, Attribute.class, results) == 0) {
+            log.debug("{} Attribute {} did not have SAML 2 Attribute transcoder instructions associated, nothing to do",
                     getLogPrefix(), attribute.getId());
         }
     }
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/FilterByQueriedAttributes.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/FilterByQueriedAttributes.java
index f2c303f..2c75274 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/FilterByQueriedAttributes.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/FilterByQueriedAttributes.java
@@ -26,6 +26,8 @@ import javax.annotation.Nullable;
 
 import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.opensaml.messaging.context.navigate.MessageLookup;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
 import org.opensaml.saml.saml2.core.Attribute;
@@ -33,17 +35,27 @@ import org.opensaml.saml.saml2.core.AttributeQuery;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Multimap;
 
+import net.shibboleth.idp.attribute.AttributeDecodingException;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.IdPAttributeValue;
 import net.shibboleth.idp.attribute.context.AttributeContext;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
 import net.shibboleth.idp.profile.AbstractProfileAction;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
-import net.shibboleth.idp.saml.attribute.mapping.AttributesMapper;
+import net.shibboleth.utilities.java.support.annotation.constraint.Live;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.service.ReloadableService;
+import net.shibboleth.utilities.java.support.service.ServiceableComponent;
 
 /**
  * Action that filters a set of attributes against the {@link org.opensaml.saml.saml2.core.Attribute} objects in
@@ -56,8 +68,8 @@ public class FilterByQueriedAttributes extends AbstractProfileAction {
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(FilterByQueriedAttributes.class);
 
-    /** Service used to get the engine used to filter attributes. */
-    @Nonnull private final AttributesMapper<Attribute, IdPAttribute> mapper;
+    /** Transcoder registry service object. */
+    @NonnullAfterInit private ReloadableService<AttributeTranscoderRegistry> transcoderRegistry;
 
     /** Strategy used to locate the {@link AttributeQuery} to filter against. */
     @Nonnull private Function<ProfileRequestContext,AttributeQuery> queryLookupStrategy;
@@ -71,14 +83,8 @@ public class FilterByQueriedAttributes extends AbstractProfileAction {
     /** AttributeContext to filter. */
     @Nullable private AttributeContext attributeContext;
 
-    /**
-     * Constructor.
-     * 
-     * @param theMapper mapper used to consume designators
-     */
-    public FilterByQueriedAttributes(@Nonnull final AttributesMapper<Attribute, IdPAttribute> theMapper) {
-        mapper = Constraint.isNotNull(theMapper, "Mapper cannot be null");
-        
+    /** Constructor. */
+    public FilterByQueriedAttributes() {
         attributeContextLookupStrategy = new ChildContextLookup<>(AttributeContext.class).compose(
                 new ChildContextLookup<>(RelyingPartyContext.class));
         
@@ -86,6 +92,17 @@ public class FilterByQueriedAttributes extends AbstractProfileAction {
     }
 
     /**
+     * Sets the registry of transcoding rules to apply to encode attributes.
+     * 
+     * @param registry registry service interface
+     */
+    public void setTranscoderRegistry(@Nonnull final ReloadableService<AttributeTranscoderRegistry> registry) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        transcoderRegistry = Constraint.isNotNull(registry, "AttributeTranscoderRegistry cannot be null");
+    }
+    
+    /**
      * Set the strategy used to locate the {@link AttributeQuery} associated with a given {@link ProfileRequestContext}.
      * 
      * @param strategy lookup strategy
@@ -112,6 +129,16 @@ public class FilterByQueriedAttributes extends AbstractProfileAction {
 
     /** {@inheritDoc} */
     @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (transcoderRegistry == null) {
+            throw new ComponentInitializationException("AttributeTranscoderRegistry cannot be null");
+        }
+    }
+    
+    /** {@inheritDoc} */
+    @Override
     protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
         
         if (!super.doPreExecute(profileRequestContext)) {
@@ -142,11 +169,34 @@ public class FilterByQueriedAttributes extends AbstractProfileAction {
     /** {@inheritDoc} */
     @Override
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-        
-        final Collection<IdPAttribute> keepers = new ArrayList<>(query.getAttributes().size());
-        
-        final Multimap<String,IdPAttribute> mapped = mapper.mapAttributes(query.getAttributes());
+                
+        final Multimap<String,IdPAttribute> mapped = HashMultimap.create();
+
+        ServiceableComponent<AttributeTranscoderRegistry> component = null;
+        try {
+            component = transcoderRegistry.getServiceableComponent();
+            if (component == null) {
+                log.error("Attribute transcoder service unavailable");
+                ActionSupport.buildEvent(profileRequestContext, EventIds.MESSAGE_PROC_ERROR);
+                return;
+            }
+
+            for (final Attribute designator : query.getAttributes()) {
+                try {
+                    decodeAttribute(component.getComponent(), profileRequestContext, designator, mapped);
+                } catch (final AttributeDecodingException e) {
+                    log.error("{} Error decoding queried Attribute", getLogPrefix(), e);
+                }
+            }
+        } finally {
+            if (component != null) {
+                component.unpinComponent();
+            }
+        }
+                
         log.debug("{} Query content mapped to attribute IDs: {}", getLogPrefix(), mapped.keySet());
+
+        final Collection<IdPAttribute> keepers = new ArrayList<>(query.getAttributes().size());
         
         for (final IdPAttribute attribute : attributeContext.getIdPAttributes().values()) {
             
@@ -174,6 +224,35 @@ public class FilterByQueriedAttributes extends AbstractProfileAction {
     }
     
     /**
+     * Access the registry of transcoding rules to decode the input {@link Attribute}.
+     * 
+     * @param registry  registry of transcoding rules
+     * @param profileRequestContext current profile request context
+     * @param input input object
+     * @param results collection to add results to
+     * 
+     * @throws AttributeDecodingException if an error occurs or no results were obtained
+     */
+    protected void decodeAttribute(@Nonnull final AttributeTranscoderRegistry registry,
+            @Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final Attribute input,
+            @Nonnull @NonnullElements @Live final Multimap<String,IdPAttribute> results)
+                    throws AttributeDecodingException {
+        
+        final Collection<TranscodingRule> transcodingRules = registry.getTranscodingRules(input);
+        if (transcodingRules.isEmpty()) {
+            throw new AttributeDecodingException("No transcoding rule for Attribute '" + input.getName() + "'");
+        }
+        
+        for (final TranscodingRule rules : transcodingRules) {
+            final AttributeTranscoder<Attribute> transcoder = TranscoderSupport.getTranscoder(rules);
+            final IdPAttribute decodedAttribute = transcoder.decode(profileRequestContext, input, rules);
+            if (decodedAttribute != null) {
+                results.put(decodedAttribute.getId(), decodedAttribute);
+            }
+        }
+    }
+    
+    /**
      * Adjust an input attribute's values based on any values requested.
      * 
      * @param attribute attribute to filter
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/MapRequestedAttributesInAttributeConsumingService.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/MapRequestedAttributesInAttributeConsumingService.java
index 6590239..d4f4d0c 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/MapRequestedAttributesInAttributeConsumingService.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/MapRequestedAttributesInAttributeConsumingService.java
@@ -22,9 +22,9 @@ import java.util.function.Function;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import net.shibboleth.idp.attribute.resolver.AttributeResolver;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
 import net.shibboleth.idp.profile.AbstractProfileAction;
-import net.shibboleth.idp.saml.attribute.mapping.AttributesMapContainer;
+import net.shibboleth.idp.saml.attribute.transcoding.AttributesMapContainer;
 import net.shibboleth.idp.saml.metadata.impl.AttributeMappingNodeProcessor;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 import net.shibboleth.utilities.java.support.service.ReloadableService;
@@ -55,8 +55,8 @@ public class MapRequestedAttributesInAttributeConsumingService extends AbstractP
     @Nonnull private Function<ProfileRequestContext, AttributeConsumingServiceContext>
         attributeConsumingServiceContextLookupStrategy;
 
-    /** The attribute resolver we use to map attributes. */
-    @Nullable private ReloadableService<AttributeResolver> attributeResolverService;
+    /** The registry of decoding rules. */
+    @Nullable private ReloadableService<AttributeTranscoderRegistry> transcoderRegistry;
 
     /** The context we use to get and put the {@link AttributeConsumingService}.*/
     private AttributeConsumingServiceContext acsContext;
@@ -65,7 +65,6 @@ public class MapRequestedAttributesInAttributeConsumingService extends AbstractP
      * Constructor.
      */
     public MapRequestedAttributesInAttributeConsumingService() {
-        super();
         // At this point, by default  the SAMLMetadataContext hangs off the SAMLPeerContext
         attributeConsumingServiceContextLookupStrategy =
                 new ChildContextLookup(AttributeConsumingServiceContext.class).compose(
@@ -86,12 +85,12 @@ public class MapRequestedAttributesInAttributeConsumingService extends AbstractP
     }
 
     /**
-     * Sets the service which does the attribute mapping.
+     * Sets the service which provides attribute decoding rules.
      *
-     * @param resolverService the service for the attribute resolver we are to derive unmapping info from
+     * @param registry the registry service
      */
-    public void setResolverService(@Nonnull final ReloadableService<AttributeResolver> resolverService) {
-        attributeResolverService = Constraint.isNotNull(resolverService, "AttributeResolver cannot be null");
+    public void setTranscoderRegistry(@Nonnull final ReloadableService<AttributeTranscoderRegistry> registry) {
+        transcoderRegistry = Constraint.isNotNull(registry, "AttributeResolver cannot be null");
     }
 
     /** {@inheritDoc} */
@@ -112,23 +111,23 @@ public class MapRequestedAttributesInAttributeConsumingService extends AbstractP
 
         final AttributeConsumingService acs = acsContext.getAttributeConsumingService();
         if (acs == null) {
-            log.trace("{} no AttributeConsumingService to map", getLogPrefix());
+            log.trace("{} No AttributeConsumingService to map", getLogPrefix());
             return;
         }
         
-        if (acs.getRequestAttributes().isEmpty() ||
-            acs.getObjectMetadata().containsKey(AttributesMapContainer.class) ||
-            acs.getParent() != null) {
-            log.trace("{} skipping mapping for AttributeConsumingService", getLogPrefix());
+        if (acs.getRequestAttributes().isEmpty() || acs.getObjectMetadata().containsKey(AttributesMapContainer.class) ||
+                acs.getParent() != null) {
+            log.trace("{} Skipping decode of AttributeConsumingService", getLogPrefix());
             // Nothing to map, already mapped, or attached to metadata (and hence already scanned)
             return;
         }
+        
         try {
-            final AttributeMappingNodeProcessor processor = new AttributeMappingNodeProcessor(attributeResolverService);
-            log.debug("{} mapping requested Attributes for generated AttributeConsumingService", getLogPrefix());
+            final AttributeMappingNodeProcessor processor = new AttributeMappingNodeProcessor(transcoderRegistry);
+            log.debug("{} Decoding RequestedAttributes for generated AttributeConsumingService", getLogPrefix());
             processor.process(acs);
         } catch (final FilterException e) {
-            log.error("{} Error mapping Attributesresponding to request", getLogPrefix(), e);
+            log.error("{} Error decoding RequestedAttributes", getLogPrefix(), e);
             ActionSupport.buildEvent(profileRequestContext, EventIds.RUNTIME_EXCEPTION);
         }
     }
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1ByteAttributeEncoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1ByteAttributeEncoderTest.java
deleted file mode 100644
index ecaa61b..0000000
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1ByteAttributeEncoderTest.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding.impl;
-
-import java.util.Arrays;
-import java.util.List;
-
-import org.opensaml.core.OpenSAMLInitBaseTestCase;
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.schema.XSBase64Binary;
-import org.opensaml.saml.saml1.core.Attribute;
-import org.opensaml.saml.saml1.core.AttributeValue;
-import org.testng.Assert;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.ByteAttributeValue;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.utilities.java.support.codec.Base64Support;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-
-/**
- * {@link SAML1ByteAttributeEncoder} Unit test.
- * 
- * Identical code to the {@link SAML1ByteAttributeEncoder} except that the type of assertion and encoder is changed.
- */
-public class SAML1ByteAttributeEncoderTest extends OpenSAMLInitBaseTestCase {
-
-    /** The name we give the test attribute. */
-    private final static String ATTR_NAME = "foo";
-
-    /** A test value. */
-    private final static byte[] BYTE_ARRAY_1 = {1, 2, 3, 4, 5};
-
-    /** A second test value. */
-    private final static byte[] BYTE_ARRAY_2 = {4, 3, 2, 1};
-
-    private SAML1ByteAttributeEncoder encoder;
-
-    @BeforeClass public void initTest() throws ComponentInitializationException {
-        encoder = new SAML1ByteAttributeEncoder();
-        encoder.setName(ATTR_NAME);
-        encoder.setNamespace("NameSpace");
-        encoder.setEncodeType(true);
-        encoder.initialize();
-    }
-
-    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void empty() throws Exception {
-        final IdPAttribute inputAttribute;
-
-        inputAttribute = new IdPAttribute(ATTR_NAME);
-
-        encoder.encode(inputAttribute);
-    }
-
-    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
-        final int[] intArray = {1, 2, 3, 4};
-        final List<IdPAttributeValue<?>> values =
-                Arrays.asList(new StringAttributeValue("foo"), new ScopedStringAttributeValue(
-                        "foo", "bar"), new IdPAttributeValue<Object>() {
-                    public Object getValue() {
-                        return intArray;
-                    }
-
-                    public String getDisplayValue() {
-                        return intArray.toString();
-                    }
-                });
-
-        IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        encoder.encode(inputAttribute);
-    }
-
-    @Test public void single() throws Exception {
-        final List<IdPAttributeValue<?>> values = List.of(new StringAttributeValue("foo"), new ByteAttributeValue(BYTE_ARRAY_1));
-        final IdPAttribute inputAttribute;
-
-        inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        List<XMLObject> children = outputAttribute.getOrderedChildren();
-
-        Assert.assertEquals(children.size(), 1, "Encoding one entry");
-
-        XMLObject child = children.get(0);
-        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME);
-
-        Assert.assertTrue(child instanceof XSBase64Binary, "Child of result attribute should be a base64Binary");
-
-        XSBase64Binary childAsString = (XSBase64Binary) child;
-
-        byte childAsBa[] = Base64Support.decode(childAsString.getValue());
-
-        Assert.assertEquals(childAsBa, BYTE_ARRAY_1, "Input equals output");
-    }
-
-    @Test public void multi() throws Exception {
-        final List<IdPAttributeValue<?>> values = List.of(new ByteAttributeValue(BYTE_ARRAY_1), 
-                new ByteAttributeValue(BYTE_ARRAY_2));
-
-        final IdPAttribute inputAttribute;
-        inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-        Assert.assertEquals(children.size(), 2, "Encoding two entries");
-
-        XMLObject child = children.get(0);
-        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-        Assert.assertTrue(child instanceof XSBase64Binary, "Child of result attribute should be a base64Binary");
-
-        XSBase64Binary childAsString = (XSBase64Binary) child;
-        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-        final byte[] res0 = Base64Support.decode(childAsString.getValue());
-
-        child = children.get(1);
-        Assert.assertTrue(child instanceof XSBase64Binary, "Child of result attribute should be a base64Binary");
-
-        childAsString = (XSBase64Binary) child;
-        final byte[] res1 = Base64Support.decode(childAsString.getValue());
-
-        //
-        // order of results is not guaranteed so sense the result from the length
-        //
-        if (BYTE_ARRAY_1.length == res0.length) {
-            Assert.assertEquals(BYTE_ARRAY_1, res0, "Input matches output");
-            Assert.assertEquals(BYTE_ARRAY_2, res1, "Input matches output");
-        } else if (BYTE_ARRAY_1.length == res1.length) {
-            Assert.assertEquals(BYTE_ARRAY_1, res1, "Input matches output");
-            Assert.assertEquals(BYTE_ARRAY_2, res0, "Input matches output");
-        } else {
-            Assert.assertTrue(BYTE_ARRAY_1.length == res1.length || BYTE_ARRAY_2.length == res1.length,
-                    "One of the output's size should match an input size");
-        }
-    }
-
-}
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1ScopedStringAttributeEncoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1ScopedStringAttributeEncoderTest.java
deleted file mode 100644
index c1d725e..0000000
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1ScopedStringAttributeEncoderTest.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding.impl;
-
-import java.util.Arrays;
-import java.util.List;
-
-import org.opensaml.core.OpenSAMLInitBaseTestCase;
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.schema.XSString;
-import org.opensaml.saml.saml1.core.Attribute;
-import org.opensaml.saml.saml1.core.AttributeValue;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.ByteAttributeValue;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.idp.saml.xmlobject.ScopedValue;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.UninitializedComponentException;
-
-/**
- * {@link SAML1ScopedStringAttributeEncoder} Unit test.
- */
-public class SAML1ScopedStringAttributeEncoderTest extends OpenSAMLInitBaseTestCase {
-
-    /** The name we give the test attribute. */
-    private final static String ATTR_NAME = "foo";
-
-    /** A test value. */
-    private final static String VALUE_1 = "ValueTheFirst";
-
-    /** A second test value. */
-    private final static String VALUE_2 = "Second";
-
-    /** A test scope . */
-    private final static String SCOPE_1 = "scope1.example.org";
-
-    /** A second test value. */
-    private final static String SCOPE_2 = "scope2";
-
-    private final static String DELIMITER = "#";
-
-    private final static String ATTRIBUTE_NAME = "Scope";
-
-    private final ScopedStringAttributeValue value1 = new ScopedStringAttributeValue(VALUE_1, SCOPE_1);
-
-    private final ScopedStringAttributeValue value2 = new ScopedStringAttributeValue(VALUE_2, SCOPE_2);
-
-    public SAML1ScopedStringAttributeEncoder makeEncoder() {
-        final SAML1ScopedStringAttributeEncoder encoder = new SAML1ScopedStringAttributeEncoder();
-        encoder.setName(ATTR_NAME);
-        encoder.setScopeAttributeName(ATTRIBUTE_NAME);
-        encoder.setNamespace("NameSpace");
-        encoder.setEncodeType(true);
-        return encoder;
-    }
-
-    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void empty()
-            throws AttributeEncodingException, ComponentInitializationException {
-        final IdPAttribute inputAttribute;
-
-        inputAttribute = new IdPAttribute(ATTR_NAME);
-
-        SAML1ScopedStringAttributeEncoder encoder = makeEncoder();
-        encoder.initialize();
-        encoder.encode(inputAttribute);
-    }
-
-    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate()
-            throws AttributeEncodingException, ComponentInitializationException {
-        final SAML1ScopedStringAttributeEncoder encoder = makeEncoder();
-        final int[] intArray = {1, 2, 3, 4};
-        final List<IdPAttributeValue<?>> values =
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new StringAttributeValue("dd"),
-                        new IdPAttributeValue<Object>() {
-                            @Override
-                            public Object getValue() {
-                                return intArray;
-                            }
-                            public String getDisplayValue() {
-                                return intArray.toString();
-                            }
-                        });
-
-        final IdPAttribute inputAttribute;
-        inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-        try {
-            encoder.encode(inputAttribute);
-            Assert.fail("not initialized");
-        } catch (UninitializedComponentException e) {
-            // OK
-        }
-        encoder.initialize();
-
-        encoder.encode(inputAttribute);
-    }
-
-    @Test public void single() throws ComponentInitializationException, AttributeEncodingException {
-        final SAML1ScopedStringAttributeEncoder encoder = makeEncoder();
-        try {
-            encoder.setScopeAttributeName(null);
-            encoder.initialize();
-            Assert.fail("Missing attribute name should throw");
-        } catch (ComponentInitializationException e) {
-            // OK
-        }
-        encoder.setScopeAttributeName(ATTRIBUTE_NAME);
-        encoder.setScopeDelimiter(DELIMITER);
-
-        final List<IdPAttributeValue<?>> values =
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), value1);
-
-        final IdPAttribute inputAttribute;
-        inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        encoder.initialize();
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-
-        Assert.assertEquals(children.size(), 1, "Encoding one entry");
-
-        final XMLObject child = children.get(0);
-
-        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-
-        Assert.assertTrue(child instanceof ScopedValue, "Child of result attribute should be a string");
-
-        final ScopedValue childAsScopedValue = (ScopedValue) child;
-
-        Assert.assertEquals(childAsScopedValue.getValue(), VALUE_1, "Input equals output");
-        Assert.assertEquals(childAsScopedValue.getScope(), SCOPE_1, "Input equals output");
-
-    }
-
-    @Test public void multi() throws ComponentInitializationException, AttributeEncodingException {
-        final SAML1ScopedStringAttributeEncoder encoder = makeEncoder();
-        encoder.initialize();
-        final List<IdPAttributeValue<?>> values =
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), value1, value2);
-
-        final IdPAttribute inputAttribute;
-        inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-        Assert.assertEquals(children.size(), 2, "Encoding two entries");
-
-        Assert.assertTrue(children.get(0) instanceof ScopedValue && children.get(1) instanceof ScopedValue,
-                "Child of result attribute should be a string");
-
-        final ScopedValue child1 = (ScopedValue) children.get(0);
-        Assert.assertEquals(child1.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-
-        final ScopedValue child2 = (ScopedValue) children.get(1);
-        Assert.assertEquals(child2.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-        //
-        // order of results is not guaranteed so sense the result from the length
-        //
-        if (child1.getValue().length() == VALUE_1.length()) {
-            Assert.assertEquals(child1.getValue(), VALUE_1, "Input matches output");
-            Assert.assertEquals(child2.getValue(), VALUE_2, "Input matches output");
-            Assert.assertEquals(child1.getScope(), SCOPE_1, "Input matches output");
-            Assert.assertEquals(child2.getScope(), SCOPE_2, "Input matches output");
-        } else if (child1.getValue().length() == VALUE_2.length()) {
-            Assert.assertEquals(child2.getValue(), VALUE_1, "Input matches output");
-            Assert.assertEquals(child1.getValue(), VALUE_2, "Input matches output");
-            Assert.assertEquals(child2.getScope(), SCOPE_1, "Input matches output");
-            Assert.assertEquals(child1.getScope(), SCOPE_2, "Input matches output");
-        } else {
-            Assert.fail("Value mismatch");
-        }
-    }
-
-    @Test public void singleInline() throws ComponentInitializationException, AttributeEncodingException {
-        final SAML1ScopedStringAttributeEncoder encoder = makeEncoder();
-        encoder.setScopeType("wibble");
-        try {
-            encoder.initialize();
-            Assert.fail("Bad type should throw");
-        } catch (ComponentInitializationException e) {
-            // OK
-        }
-
-        encoder.setScopeType("inline");
-        encoder.setScopeDelimiter(DELIMITER);
-        encoder.initialize();
-
-        final List<IdPAttributeValue<?>> values =
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), value1);
-
-        final IdPAttribute inputAttribute;
-        inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-
-        Assert.assertEquals(children.size(), 1, "Encoding one entry");
-
-        final XMLObject child = children.get(0);
-
-        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-
-        Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
-
-        final XSString childAsString = (XSString) child;
-
-        Assert.assertEquals(childAsString.getValue(), VALUE_1 + DELIMITER + SCOPE_1, "Input equals output");
-    }
-
-    @Test public void multiInline() throws Exception {
-        final SAML1ScopedStringAttributeEncoder encoder = makeEncoder();
-        encoder.setScopeType("inline");
-        encoder.setScopeDelimiter(DELIMITER);
-        encoder.setScopeAttributeName(null);
-        encoder.initialize();
-        final List<IdPAttributeValue<?>> values =
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), value1, value2);
-
-        final IdPAttribute inputAttribute;
-        inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-        Assert.assertEquals(children.size(), 2, "Encoding two entries");
-
-        Assert.assertTrue(children.get(0) instanceof XSString && children.get(1) instanceof XSString,
-                "Child of result attribute should be a string");
-
-        final XSString child1 = (XSString) children.get(0);
-        Assert.assertEquals(child1.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-
-        final XSString child2 = (XSString) children.get(1);
-        Assert.assertEquals(child2.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-        //
-        // order of results is not guaranteed so sense the result from the length
-        //
-        if (child1.getValue().equals(VALUE_1 + DELIMITER + SCOPE_1)) {
-            Assert.assertEquals(child2.getValue(), VALUE_2 + DELIMITER + SCOPE_2, "Input matches output");
-        } else {
-            Assert.assertEquals(child1.getValue(), VALUE_2 + DELIMITER + SCOPE_2, "Input matches output");
-            Assert.assertEquals(child2.getValue(), VALUE_1 + DELIMITER + SCOPE_1, "Input matches output");
-        }
-    }
-
-}
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1StringAttributeEncoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1StringAttributeEncoderTest.java
deleted file mode 100644
index 9e6b68c..0000000
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1StringAttributeEncoderTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding.impl;
-
-import java.util.Arrays;
-import java.util.List;
-
-import org.opensaml.core.OpenSAMLInitBaseTestCase;
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.schema.XSString;
-import org.opensaml.saml.saml1.core.Attribute;
-import org.opensaml.saml.saml1.core.AttributeValue;
-import org.testng.Assert;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.ByteAttributeValue;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-
-/**
- * {@link SAML1StringAttributeEncoder} Unit test.
- */
-public class SAML1StringAttributeEncoderTest extends OpenSAMLInitBaseTestCase {
-
-    /** The name we give the test attribute. */
-    private final static String ATTR_NAME = "foo";
-
-    /** A test value. */
-    private final static String STRING_1 = "Value The First";
-
-    /** A second test value. */
-    private final static String STRING_2 = "Second string the value is";
-
-    private SAML1StringAttributeEncoder encoder;
-
-    @BeforeClass public void initTest() throws ComponentInitializationException {
-        encoder = new SAML1StringAttributeEncoder();
-        encoder.setName(ATTR_NAME);
-        encoder.setNamespace("NameSpace");
-        encoder.setEncodeType(true);
-        encoder.initialize();
-    }
-
-    @Test(expectedExceptions={AttributeEncodingException.class,})   public void empty() throws Exception {
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-
-        encoder.encode(inputAttribute);
-    }
-
-    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
-        encoder.initialize();
-        final int[] intArray = {1, 2, 3, 4};
-        final List<IdPAttributeValue<?>> values =
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}),
-                        new IdPAttributeValue<Object>() {
-                            @Override
-                            public Object getValue() {
-                                return intArray;
-                            }
-                            @Override
-                            public String getDisplayValue() {
-                                return intArray.toString();
-                            }
-                        });
-
-        final IdPAttribute inputAttribute;
-        inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        encoder.encode(inputAttribute);
-    }
-
-    @Test public void single() throws Exception {
-        final List<IdPAttributeValue<?>> values = List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), new StringAttributeValue(STRING_1));
-
-        final IdPAttribute inputAttribute;
-        inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-
-        Assert.assertEquals(children.size(), 1, "Encoding one entry");
-
-        final XMLObject child = children.get(0);
-
-        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-
-        Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
-
-        final XSString childAsString = (XSString) child;
-
-        Assert.assertEquals(childAsString.getValue(), STRING_1, "Input equals output");
-    }
-
-    @Test public void multi() throws Exception {
-        final List<IdPAttributeValue<?>> values = List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}),
-                        new StringAttributeValue(STRING_1),
-                        new StringAttributeValue(STRING_2),
-                        new ScopedStringAttributeValue(STRING_2, STRING_1));
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-        Assert.assertEquals(children.size(), 3, "Encoding three entries");
-
-        for (final XMLObject child: children) {
-            Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
-            final String childAsString = ((XSString) children.get(0)).getValue();
-            Assert.assertTrue(STRING_1.equals(childAsString)||STRING_2.equals(childAsString));
-        }
-    }
-
-}
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1XMLObjectAttributeEncoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1XMLObjectAttributeEncoderTest.java
deleted file mode 100644
index c27c41a..0000000
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML1XMLObjectAttributeEncoderTest.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding.impl;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import org.opensaml.core.OpenSAMLInitBaseTestCase;
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.schema.XSString;
-import org.opensaml.saml.saml1.core.Attribute;
-import org.opensaml.saml.saml1.core.AttributeValue;
-import org.testng.Assert;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.ByteAttributeValue;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.idp.attribute.XMLObjectAttributeValue;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-
-/**
- * {@link SAML1XMLObjectAttributeEncoder} Unit test.
- * 
- * Identical code to the {@link SAML1ByteAttributeEncoder} except that the type of assertion and encoder is changed.
- */
-public class SAML1XMLObjectAttributeEncoderTest extends OpenSAMLInitBaseTestCase {
-
-    /** The name we give the test attribute. */
-    private final static String ATTR_NAME = "foo";
-
-    /** A test value. */
-    private final static String STRING_1 = "Value The First";
-
-    /** A second test value. */
-    private final static String STRING_2 = "Second string the value is";
-
-    private SAML1XMLObjectAttributeEncoder encoder;
-
-    private SAML1StringAttributeEncoder strEncoder;
-
-    @BeforeClass public void initTest() throws ComponentInitializationException {
-        encoder = new SAML1XMLObjectAttributeEncoder();
-        encoder.setName(ATTR_NAME);
-        encoder.setNamespace("NameSpace");
-        encoder.initialize();
-        strEncoder = new SAML1StringAttributeEncoder();
-        strEncoder.setName(ATTR_NAME);
-        strEncoder.setNamespace("NameSpace");
-        strEncoder.setEncodeType(true);
-        strEncoder.initialize();
-    }
-
-    /**
-     * Create an XML object from a string which we can test against later. We use the Saml1StringEncoder to do the work
-     * because it is handy in this package.
-     * 
-     * @param value that we encode
-     * @return an XML object
-     */
-    private XMLObjectAttributeValue ObjectFor(final String value) {
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(Collections.singletonList(new StringAttributeValue(value)));
-        try {
-            return new XMLObjectAttributeValue(strEncoder.encode(inputAttribute));
-        } catch (AttributeEncodingException e) {
-            return null;
-        }
-    }
-
-    /**
-     * Check that the input XML object is what was expected.
-     * 
-     * @param input the objects in question.
-     * @param possibles the strings that they might be encoding.
-     */
-    private static void CheckValues(final XMLObject input, final String... possibles) {
-        Assert.assertTrue(input instanceof Attribute);
-        final Attribute attribute = (Attribute) input;
-
-        final List<XMLObject> children = attribute.getOrderedChildren();
-        Assert.assertEquals(children.size(), 1, "Data must have but one entry");
-        Assert.assertTrue(children.get(0) instanceof XSString, "Data must contain one string");
-        final String s = ((XSString) children.get(0)).getValue();
-
-        for (String possible : possibles) {
-            if (s.equals(possible)) {
-                return;
-            }
-        }
-        Assert.assertTrue(false, "No potential match");
-    }
-
-    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void empty() throws Exception {
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-
-        encoder.encode(inputAttribute);
-    }
-
-    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
-        final int[] intArray = {1, 2, 3, 4};
-        final List<IdPAttributeValue<?>> values =
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}),
-                        new ScopedStringAttributeValue("foo", "bar"), new IdPAttributeValue<Object>() {
-                            @Override
-                            public Object getValue() {
-                                return intArray;
-                            }
-                            public String getDisplayValue() {
-                                return intArray.toString();
-                            }
-                        });
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        encoder.encode(inputAttribute);
-    }
-
-    @Test public void single() throws Exception {
-        final List<IdPAttributeValue<?>> values = List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), ObjectFor(STRING_1));
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-        Assert.assertEquals(children.size(), 1, "Encoding one entry");
-        Assert.assertEquals(children.get(0).getElementQName(),
-                AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-        Assert.assertEquals(children.get(0).getOrderedChildren().size(), 1,
-                "Expected exactly one child inside the <AttributeValue/>");
-
-        CheckValues(children.get(0).getOrderedChildren().get(0), STRING_1);
-    }
-
-    @Test public void testMulti() throws Exception {
-        final List<IdPAttributeValue<?>> values = List.of(ObjectFor(STRING_1), ObjectFor(STRING_2));
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-        Assert.assertEquals(children.size(), 2, "Encoding two entries");
-
-        Assert.assertEquals(children.get(0).getElementQName(),
-                AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-        Assert.assertEquals(children.get(0).getOrderedChildren().size(), 1,
-                "Expected exactly one child inside the <AttributeValue/> for first Attribute");
-
-        Assert.assertEquals(children.get(1).getElementQName(),
-                AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-        Assert.assertEquals(children.get(1).getOrderedChildren().size(), 1,
-                "Expected exactly one child inside the <AttributeValue/> for second Attribute");
-
-        CheckValues(children.get(0).getOrderedChildren().get(0), STRING_1, STRING_2);
-        CheckValues(children.get(1).getOrderedChildren().get(0), STRING_1, STRING_2);
-    }
-}
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2ByteAttributeEncoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2ByteAttributeEncoderTest.java
deleted file mode 100644
index 35dd30b..0000000
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2ByteAttributeEncoderTest.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding.impl;
-
-import java.util.Arrays;
-import java.util.List;
-
-import org.opensaml.core.OpenSAMLInitBaseTestCase;
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.schema.XSBase64Binary;
-import org.opensaml.saml.saml2.core.Attribute;
-import org.opensaml.saml.saml2.core.AttributeValue;
-import org.testng.Assert;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.ByteAttributeValue;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.utilities.java.support.codec.Base64Support;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-
-/**
- * {@link SAML2ByteAttributeEncoder} Unit test.
- * 
- * Identical code to the {@link SAML1ByteAttributeEncoder} except that the type of assertion and encoder is changed.
- */
-public class SAML2ByteAttributeEncoderTest extends OpenSAMLInitBaseTestCase {
-
-    /** The name we give the test attribute. */
-    private final static String ATTR_NAME = "foo";
-
-    /** A test value. */
-    private final static byte[] BYTE_ARRAY_1 = {1, 2, 3, 4, 5};
-
-    /** A second test value. */
-    private final static byte[] BYTE_ARRAY_2 = {4, 3, 2, 1};
-
-    private SAML2ByteAttributeEncoder encoder;
-
-    @BeforeClass public void initTest() throws ComponentInitializationException {
-        encoder = new SAML2ByteAttributeEncoder();
-        encoder.setName(ATTR_NAME);
-        encoder.setNameFormat("NameSpace");
-        encoder.setFriendlyName("friendly");
-        encoder.setEncodeType(true);
-        encoder.initialize();
-    }
-
-    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void empty() throws Exception {
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-
-        encoder.encode(inputAttribute);
-    }
-
-    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
-        final int[] intArray = {1, 2, 3, 4};
-        final List<IdPAttributeValue<?>> values =
-                Arrays.asList(new StringAttributeValue("foo"), new ScopedStringAttributeValue("foo", "bar"),
-                        new IdPAttributeValue<Object>() {
-                            public Object getValue() {
-                                return intArray;
-                            }
-                            public String getDisplayValue() {
-                                return intArray.toString();
-                            }
-                        });
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        encoder.encode(inputAttribute);
-    }
-
-    @Test public void single() throws Exception {
-        final List<IdPAttributeValue<?>> values = List.of(new StringAttributeValue("foo"), new ByteAttributeValue(BYTE_ARRAY_1));
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        List<XMLObject> children = outputAttribute.getOrderedChildren();
-
-        Assert.assertEquals(children.size(), 1, "Encoding one entry");
-
-        XMLObject child = children.get(0);
-        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME);
-
-        Assert.assertTrue(child instanceof XSBase64Binary, "Child of result attribute should be a base64Binary");
-
-        XSBase64Binary childAsString = (XSBase64Binary) child;
-
-        byte childAsBa[] = Base64Support.decode(childAsString.getValue());
-
-        Assert.assertEquals(childAsBa, BYTE_ARRAY_1, "Input equals output");
-    }
-
-    @Test public void multi() throws Exception {
-        final List<IdPAttributeValue<?>> values = List.of(new ByteAttributeValue(BYTE_ARRAY_1), new ByteAttributeValue(BYTE_ARRAY_2));
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-        Assert.assertEquals(children.size(), 2, "Encoding two entries");
-
-        XMLObject child = children.get(0);
-        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-        Assert.assertTrue(child instanceof XSBase64Binary, "Child of result attribute should be a base64Binary");
-
-        XSBase64Binary childAsString = (XSBase64Binary) child;
-        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-        final byte[] res0 = Base64Support.decode(childAsString.getValue());
-        
-        child = children.get(1);
-        Assert.assertTrue(child instanceof XSBase64Binary, "Child of result attribute should be a base64Binary");
-
-        childAsString = (XSBase64Binary) child;
-        final byte[] res1 = Base64Support.decode(childAsString.getValue());
-
-        //
-        // order of results is not guaranteed so sense the result from the length
-        //
-        if (BYTE_ARRAY_1.length == res0.length) {
-            Assert.assertEquals(BYTE_ARRAY_1, res0, "Input matches output");
-            Assert.assertEquals(BYTE_ARRAY_2, res1, "Input matches output");
-        } else if (BYTE_ARRAY_1.length == res1.length) {
-            Assert.assertEquals(BYTE_ARRAY_1, res1, "Input matches output");
-            Assert.assertEquals(BYTE_ARRAY_2, res0, "Input matches output");
-        } else {
-            Assert.assertTrue(BYTE_ARRAY_1.length == res1.length || BYTE_ARRAY_2.length == res1.length,
-                    "One of the output's size should match an input size");
-        }
-    }
-
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2ScopedStringAttributeEncoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2ScopedStringAttributeEncoderTest.java
deleted file mode 100644
index d3d6ce9..0000000
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2ScopedStringAttributeEncoderTest.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding.impl;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-import org.opensaml.core.OpenSAMLInitBaseTestCase;
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.schema.XSString;
-import org.opensaml.saml.saml2.core.Attribute;
-import org.opensaml.saml.saml2.core.AttributeValue;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.ByteAttributeValue;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.idp.saml.xmlobject.ScopedValue;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.UninitializedComponentException;
-
-/**
- * {@link SAML2ScopedStringAttributeEncoder} Unit test. Looks a lot like the SAML1 one
- */
-public class SAML2ScopedStringAttributeEncoderTest extends OpenSAMLInitBaseTestCase {
-
-    /** The name we give the test attribute. */
-    private final static String ATTR_NAME = "foo";
-
-    /** A test value. */
-    private final static String VALUE_1 = "ValueTheFirst";
-
-    /** A second test value. */
-    private final static String VALUE_2 = "Second";
-
-    /** A test scope . */
-    private final static String SCOPE_1 = "scope1.example.org";
-
-    /** A second test value. */
-    private final static String SCOPE_2 = "scope2";
-
-    private final static String DELIMITER = "#";
-
-    private final static String ATTRIBUTE_NAME = "Scope";
-
-    private final ScopedStringAttributeValue value1 = new ScopedStringAttributeValue(VALUE_1, SCOPE_1);
-
-    private final ScopedStringAttributeValue value2 = new ScopedStringAttributeValue(VALUE_2, SCOPE_2);
-
-    public SAML2ScopedStringAttributeEncoder makeEncoder() {
-        SAML2ScopedStringAttributeEncoder encoder;
-        encoder = new SAML2ScopedStringAttributeEncoder();
-        encoder.setName(ATTR_NAME);
-        encoder.setScopeDelimiter(DELIMITER);
-        encoder.setNameFormat("NameSpace");
-        encoder.setFriendlyName("friendly");
-        encoder.setEncodeType(true);
-        return encoder;
-    }
-
-    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void empty()
-            throws AttributeEncodingException, ComponentInitializationException {
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-
-        SAML2ScopedStringAttributeEncoder encoder = makeEncoder();
-        encoder.initialize();
-
-        encoder.encode(inputAttribute);
-    }
-
-    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate()
-            throws AttributeEncodingException, ComponentInitializationException {
-        final SAML2ScopedStringAttributeEncoder encoder = makeEncoder();
-        final int[] intArray = {1, 2, 3, 4};
-        final var values =
-                List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), new StringAttributeValue("dd"),
-                        new IdPAttributeValue<Object>() {
-                            public Object getValue() {
-                                return intArray;
-                            }
-                            public String getDisplayValue() {
-                                return intArray.toString();
-                            }
-                        });
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-        try {
-            encoder.encode(inputAttribute);
-            Assert.fail("not initialized");
-        } catch (UninitializedComponentException e) {
-            // OK
-        }
-        encoder.initialize();
-
-        encoder.encode(inputAttribute);
-    }
-
-    @Test public void singleAttribute() throws ComponentInitializationException, AttributeEncodingException {
-        final SAML2ScopedStringAttributeEncoder encoder = makeEncoder();
-        encoder.setScopeType("attribute");
-        try {
-            encoder.setScopeAttributeName(null);
-            encoder.initialize();
-            Assert.fail("Missing attribute name should throw");
-        } catch (ComponentInitializationException e) {
-            // OK
-        }
-        encoder.setScopeAttributeName(ATTRIBUTE_NAME);
-        encoder.setScopeDelimiter(DELIMITER);
-
-        final Collection<IdPAttributeValue<?>> values =
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), value1);
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        encoder.initialize();
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-
-        Assert.assertEquals(children.size(), 1, "Encoding one entry");
-
-        final XMLObject child = children.get(0);
-
-        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-
-        Assert.assertTrue(child instanceof ScopedValue, "Child of result attribute should be a string");
-
-        final ScopedValue childAsScopedValue = (ScopedValue) child;
-
-        Assert.assertEquals(childAsScopedValue.getValue(), VALUE_1, "Input equals output");
-        Assert.assertEquals(childAsScopedValue.getScope(), SCOPE_1, "Input equals output");
-
-    }
-
-    @Test public void multiAttribute() throws ComponentInitializationException, AttributeEncodingException {
-        final SAML2ScopedStringAttributeEncoder encoder = makeEncoder();
-        encoder.setScopeType("attribute");
-        encoder.setScopeAttributeName(ATTR_NAME);
-        encoder.initialize();
-        Collection<IdPAttributeValue<?>> values = List.of(value1, new ByteAttributeValue(new byte[] {1, 2, 3,}),  value2);
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-        Assert.assertEquals(children.size(), 2, "Encoding two entries");
-
-        Assert.assertTrue(children.get(0) instanceof ScopedValue && children.get(1) instanceof ScopedValue,
-                "Child of result attribute should be a string");
-
-        final ScopedValue child1 = (ScopedValue) children.get(0);
-        Assert.assertEquals(child1.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-
-        final ScopedValue child2 = (ScopedValue) children.get(1);
-        Assert.assertEquals(child2.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-        //
-        // order of results is not guaranteed so sense the result from the length
-        //
-        if (child1.getValue().length() == VALUE_1.length()) {
-            Assert.assertEquals(child1.getValue(), VALUE_1, "Input matches output");
-            Assert.assertEquals(child2.getValue(), VALUE_2, "Input matches output");
-            Assert.assertEquals(child1.getScope(), SCOPE_1, "Input matches output");
-            Assert.assertEquals(child2.getScope(), SCOPE_2, "Input matches output");
-        } else if (child1.getValue().length() == VALUE_2.length()) {
-            Assert.assertEquals(child2.getValue(), VALUE_1, "Input matches output");
-            Assert.assertEquals(child1.getValue(), VALUE_2, "Input matches output");
-            Assert.assertEquals(child2.getScope(), SCOPE_1, "Input matches output");
-            Assert.assertEquals(child1.getScope(), SCOPE_2, "Input matches output");
-        } else {
-            Assert.fail("Value mismatch");
-        }
-    }
-
-    @Test public void singleInline() throws ComponentInitializationException, AttributeEncodingException {
-        final SAML2ScopedStringAttributeEncoder encoder = makeEncoder();
-        encoder.setScopeType("wibble");
-        try {
-            encoder.initialize();
-            Assert.fail("Bad type should throw");
-        } catch (ComponentInitializationException e) {
-            // OK
-        }
-        encoder.setScopeAttributeName(ATTRIBUTE_NAME);
-        encoder.setScopeDelimiter("");
-        encoder.setScopeType("inline");
-        try {
-            encoder.initialize();
-            Assert.fail("Missing delimiter should throw");
-        } catch (ComponentInitializationException e) {
-            // OK
-        }
-        encoder.setScopeDelimiter(DELIMITER);
-        encoder.initialize();
-
-        final List<IdPAttributeValue<?>> values = List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), value1);
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-
-        Assert.assertEquals(children.size(), 1, "Encoding one entry");
-
-        final XMLObject child = children.get(0);
-
-        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-
-        Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
-
-        final XSString childAsString = (XSString) child;
-
-        Assert.assertEquals(childAsString.getValue(), VALUE_1 + DELIMITER + SCOPE_1, "Input equals output");
-    }
-
-    @Test public void multiInline() throws Exception {
-        final SAML2ScopedStringAttributeEncoder encoder = makeEncoder();
-        encoder.setScopeDelimiter(DELIMITER);
-        encoder.setScopeAttributeName(null);
-        encoder.initialize();
-        final List<IdPAttributeValue<?>> values = List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), value1, value2);
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-        Assert.assertEquals(children.size(), 2, "Encoding two entries");
-
-        Assert.assertTrue(children.get(0) instanceof XSString && children.get(1) instanceof XSString,
-                "Child of result attribute should be a string");
-
-        final XSString child1 = (XSString) children.get(0);
-        Assert.assertEquals(child1.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-
-        final XSString child2 = (XSString) children.get(1);
-        Assert.assertEquals(child2.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-        //
-        // order of results is not guaranteed so sense the result from the length
-        //
-        if (child1.getValue().equals(VALUE_1 + DELIMITER + SCOPE_1)) {
-            Assert.assertEquals(child2.getValue(), VALUE_2 + DELIMITER + SCOPE_2, "Input matches output");
-        } else {
-            Assert.assertEquals(child1.getValue(), VALUE_2 + DELIMITER + SCOPE_2, "Input matches output");
-            Assert.assertEquals(child2.getValue(), VALUE_1 + DELIMITER + SCOPE_1, "Input matches output");
-        }
-    }
-
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2StringAttributeEncoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2StringAttributeEncoderTest.java
deleted file mode 100644
index 80ce8b6..0000000
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2StringAttributeEncoderTest.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding.impl;
-
-import java.util.List;
-
-import org.opensaml.core.OpenSAMLInitBaseTestCase;
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.schema.XSString;
-import org.opensaml.saml.saml2.core.Attribute;
-import org.opensaml.saml.saml2.core.AttributeValue;
-import org.testng.Assert;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.ByteAttributeValue;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-
-/**
- * {@link SAML2StringAttributeEncoder} Unit test.
- * 
- * Identical code to the {@link SAML1StringAttributeEncoder} except that the type of assertion and encoder is changed.
- */
-public class SAML2StringAttributeEncoderTest extends OpenSAMLInitBaseTestCase {
-
-    /** The name we give the test attribute. */
-    private final static String ATTR_NAME = "foo";
-
-    /** A test value. */
-    private final static String STRING_1 = "Value The First";
-
-    /** A second test value. */
-    private final static String STRING_2 = "Second string the value is";
-
-    private SAML2StringAttributeEncoder encoder;
-
-    @BeforeClass public void initTest() throws ComponentInitializationException {
-        encoder = new SAML2StringAttributeEncoder();
-        encoder.setName(ATTR_NAME);
-        encoder.setNameFormat("NameSpace");
-        encoder.setFriendlyName("friendly");
-        encoder.setEncodeType(true);
-        encoder.initialize();
-    }
-
-    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void empty() throws Exception {
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-
-        encoder.encode(inputAttribute);
-    }
-
-    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
-        final int[] intArray = {1, 2, 3, 4};
-        final var values = List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), new IdPAttributeValue<Object>() {
-                    @Override
-                    public Object getValue() {
-                        return intArray;
-                    }
-                    @Override
-                    public String getDisplayValue() {
-                        return intArray.toString();
-                    }
-                });
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        encoder.encode(inputAttribute);
-    }
-
-    @Test public void single() throws Exception {
-        final List<IdPAttributeValue<?>> values = List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), new StringAttributeValue(STRING_1));
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-
-        Assert.assertEquals(children.size(), 1, "Encoding one entry");
-
-        final XMLObject child = children.get(0);
-
-        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-
-        Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
-
-        final XSString childAsString = (XSString) child;
-
-        Assert.assertEquals(childAsString.getValue(), STRING_1, "Input equals output");
-    }
-
-    @Test public void multi() throws Exception {
-        final List<IdPAttributeValue<?>> values = List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}),
-                        new StringAttributeValue(STRING_1),
-                        new StringAttributeValue(STRING_2),
-                        new ScopedStringAttributeValue(STRING_1, STRING_2));
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-        Assert.assertEquals(children.size(), 3, "Encoding three entries");
-
-        for (final XMLObject child: children) {
-            Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
-            final String childAsString = ((XSString) children.get(0)).getValue();
-            Assert.assertTrue(STRING_1.equals(childAsString)||STRING_2.equals(childAsString));
-        }
-    }
-
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2XMLObjectAttributeEncoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2XMLObjectAttributeEncoderTest.java
deleted file mode 100644
index b6675a0..0000000
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/encoding/impl/SAML2XMLObjectAttributeEncoderTest.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.encoding.impl;
-
-import java.util.Collections;
-import java.util.List;
-
-import org.opensaml.core.OpenSAMLInitBaseTestCase;
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.schema.XSString;
-import org.opensaml.saml.saml2.core.Attribute;
-import org.opensaml.saml.saml2.core.AttributeValue;
-import org.testng.Assert;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import net.shibboleth.idp.attribute.AttributeEncodingException;
-import net.shibboleth.idp.attribute.ByteAttributeValue;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.idp.attribute.XMLObjectAttributeValue;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-
-/**
- * {@link SAML2XMLObjectAttributeEncoder} Unit test.
- * 
- * Identical code to the {@link SAML1XMLObjectAttributeEncoder} except that the type of assertion and encoder is changed.
- */
-public class SAML2XMLObjectAttributeEncoderTest extends OpenSAMLInitBaseTestCase {
-
-    /** The name we give the test attribute. */
-    private final static String ATTR_NAME = "foo";
-
-    /** A test value. */
-    private final static String STRING_1 = "Value The First";
-
-    /** A second test value. */
-    private final static String STRING_2 = "Second string the value is";
-
-    private SAML2XMLObjectAttributeEncoder encoder;
-
-    private SAML1StringAttributeEncoder strEncoder;
-
-    @BeforeClass public void initTest() throws ComponentInitializationException {
-        encoder = new SAML2XMLObjectAttributeEncoder();
-        encoder.setName(ATTR_NAME);
-        encoder.setNameFormat("NameSpace");
-        encoder.setFriendlyName("friendly");
-        encoder.initialize();
-
-        strEncoder = new SAML1StringAttributeEncoder();
-        strEncoder.setName(ATTR_NAME);
-        strEncoder.setNamespace("NameSpace");
-        strEncoder.setEncodeType(true);
-        strEncoder.initialize();
-    }
-
-    /**
-     * Create an XML object from a string which we can test against later. We use the Saml1StringEncoder to do the work
-     * because it is handy in this package.
-     * 
-     * @param value that we encode
-     * @return an XML object
-     */
-    private XMLObjectAttributeValue ObjectFor(final String value) {
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(Collections.singletonList(new StringAttributeValue(value)));
-        try {
-            return new XMLObjectAttributeValue(strEncoder.encode(inputAttribute));
-        } catch (AttributeEncodingException e) {
-            return null;
-        }
-    }
-
-    /**
-     * Check that the input XML object is what was expected.
-     * 
-     * @param input the objects in question.
-     * @param possibles the strings that they might be encoding.
-     */
-    private static void CheckValues(final XMLObject input, final String... possibles) {
-        Assert.assertTrue(input instanceof org.opensaml.saml.saml1.core.Attribute);
-        final org.opensaml.saml.saml1.core.Attribute attribute = (org.opensaml.saml.saml1.core.Attribute) input;
-
-        final List<XMLObject> children = attribute.getOrderedChildren();
-        Assert.assertEquals(children.size(), 1, "Data must have but one entry");
-        Assert.assertTrue(children.get(0) instanceof XSString, "Data must contain one string");
-        final String s = ((XSString) children.get(0)).getValue();
-
-        for (String possible : possibles) {
-            if (s.equals(possible)) {
-                return;
-            }
-        }
-        Assert.assertTrue(false, "No potential match");
-    }
-
-    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void empty() throws Exception {
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-
-        encoder.encode(inputAttribute);
-    }
-
-    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
-        final int[] intArray = {1, 2, 3, 4};
-        final var values = List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), new ScopedStringAttributeValue("foo",
-                        "bar"), new IdPAttributeValue<Object>() {
-                    @Override
-                    public Object getValue() {
-                        return intArray;
-                    }
-                    public String getDisplayValue() {
-                        return intArray.toString();
-                    }
-                });
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        encoder.encode(inputAttribute);
-    }
-
-    @Test public void single() throws Exception {
-        final var values = List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), ObjectFor(STRING_1));
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-        Assert.assertEquals(children.size(), 1, "Encoding one entry");
-        Assert.assertEquals(children.get(0).getElementQName(),
-                AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-        Assert.assertEquals(children.get(0).getOrderedChildren().size(), 1,
-                "Expected exactly one child inside the <AttributeValue/>");
-
-        CheckValues(children.get(0).getOrderedChildren().get(0), STRING_1);
-    }
-
-    @Test public void multi() throws Exception {
-        final List<IdPAttributeValue<?>> values = List.of(ObjectFor(STRING_1), ObjectFor(STRING_2));
-
-        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
-        inputAttribute.setValues(values);
-
-        final Attribute outputAttribute = encoder.encode(inputAttribute);
-        Assert.assertNotNull(outputAttribute);
-
-        final List<XMLObject> children = outputAttribute.getOrderedChildren();
-        Assert.assertEquals(children.size(), 2, "Encoding two entries");
-
-        Assert.assertEquals(children.get(0).getElementQName(),
-                AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-        Assert.assertEquals(children.get(0).getOrderedChildren().size(), 1,
-                "Expected exactly one child inside the <AttributeValue/> for first Attribute");
-
-        Assert.assertEquals(children.get(1).getElementQName(),
-                AttributeValue.DEFAULT_ELEMENT_NAME,
-                "Attribute Value not inside <AttributeValue/>");
-        Assert.assertEquals(children.get(1).getOrderedChildren().size(), 1,
-                "Expected exactly one child inside the <AttributeValue/> for second Attribute");
-
-        CheckValues(children.get(0).getOrderedChildren().get(0), STRING_1, STRING_2);
-        CheckValues(children.get(1).getOrderedChildren().get(0), STRING_1, STRING_2);
-
-    }
-
-}
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/mapping/impl/AttributeValueMapperTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/mapping/impl/AttributeValueMapperTest.java
deleted file mode 100644
index 45d515f..0000000
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/mapping/impl/AttributeValueMapperTest.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping.impl;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import net.shibboleth.idp.attribute.EmptyAttributeValue;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.ByteAttributeValue;
-import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.idp.attribute.XMLObjectAttributeValue;
-import net.shibboleth.utilities.java.support.codec.Base64Support;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.saml.ext.saml2mdui.UIInfo;
-import org.opensaml.saml.saml2.metadata.RequestedAttribute;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-/**
- * Tests for AttributeValueMapper.
- */
-public class AttributeValueMapperTest extends MappingTests {
-
-    private List<RequestedAttribute> attributes;
-
-    private void loadAttributes() {
-        if (null != attributes) {
-            return;
-        }
-        attributes = loadFile("requestedAttributeValues.xml");
-    }
-
-    private List<XMLObject> getValues(String attributeName) {
-        loadAttributes();
-        for (RequestedAttribute attribute : attributes) {
-            if (attributeName.equals(attribute.getName())) {
-                return attribute.getAttributeValues();
-            }
-        }
-        return null;
-    }
-
-    @Test public void testString() throws ComponentInitializationException {
-
-        final StringAttributeValueMapper mapper = new StringAttributeValueMapper();
-
-        mapper.setLogPrefix("testString");
-        mapper.initialize();
-        final List<IdPAttributeValue<?>> result = mapper.decodeValues(getValues("stdFormat"));
-
-        Assert.assertEquals(result.size(), 4);
-        final Set<String> strings = new HashSet(4);
-
-        for (IdPAttributeValue val : result) {
-            strings.add(((StringAttributeValue) val).getValue());
-        }
-        Assert.assertTrue(strings.contains("String"));
-        Assert.assertTrue(strings.contains("1"));
-        Assert.assertTrue(strings.contains("U2NvdCBDYW50b3IgaXMgdGhlIGdvZCBvZiBTQU1M"));
-        Assert.assertTrue(strings.contains("scope at scoped.example.org"));
-    }
-
-    @Test public void testScopedString() throws ComponentInitializationException {
-
-        ScopedStringAttributeValueMapper mapper = new ScopedStringAttributeValueMapper();
-
-        mapper.setLogPrefix("testScopedString");
-        mapper.initialize();
-        List<IdPAttributeValue<?>> result = mapper.decodeValues(getValues("stdFormat"));
-        Assert.assertEquals(result.size(), 1);
-        ScopedStringAttributeValue value = (ScopedStringAttributeValue) result.get(0);
-        Assert.assertEquals(value.getValue(), "scope");
-        Assert.assertEquals(value.getScope(), "scoped.example.org");
-
-        mapper = new ScopedStringAttributeValueMapper();
-        mapper.setDelimiter("0b3IgaX");
-        mapper.setLogPrefix("testScopedString2");
-        mapper.initialize();
-        result = mapper.decodeValues(getValues("stdFormat"));
-        Assert.assertEquals(result.size(), 1);
-        value = (ScopedStringAttributeValue) result.get(0);
-        Assert.assertEquals(value.getValue(), "U2NvdCBDYW5");
-        Assert.assertEquals(value.getScope(), "MgdGhlIGdvZCBvZiBTQU1M");
-
-        result = mapper.decodeValues(getValues("urn:oid:0.9.2342.19200300.100.1.3"));
-        Assert.assertTrue(result.isEmpty());
-    }
-
-    @Test public void testByte() throws ComponentInitializationException {
-
-        ByteAttributeValueMapper mapper = new ByteAttributeValueMapper();
-        
-        mapper.setLogPrefix("testByte");
-        mapper.initialize();
-        final List<IdPAttributeValue<?>> result = mapper.decodeValues(getValues("stdFormat"));
-        Assert.assertEquals(result.size(), 4);
-        final Set<String> strings = new HashSet(4);
-        
-        for (IdPAttributeValue<?> val: result) {
-            if (val instanceof EmptyAttributeValue) {
-                strings.add(Base64Support.encode(new byte[0], Base64Support.CHUNKED));
-            } else {
-                strings.add(Base64Support.encode(((ByteAttributeValue)val).getValue(), Base64Support.CHUNKED));
-            }
-        }
-        Assert.assertTrue(strings.contains("U2NvdCBDYW50b3IgaXMgdGhlIGdvZCBvZiBTQU1M"));        
-    }
-    
-    @Test public void testXMLObject() throws ComponentInitializationException {
-
-        XMLObjectAttributeValueMapper mapper = new XMLObjectAttributeValueMapper();
-        
-        mapper.setLogPrefix("testXMLObject");
-        mapper.initialize();
-        final List<IdPAttributeValue<?>> result = mapper.decodeValues(getValues("stdFormat"));
-        Assert.assertEquals(result.size(), 1);
-        XMLObject object = ((XMLObjectAttributeValue) result.get(0)).getValue();
-        
-        Assert.assertTrue(object instanceof UIInfo);
-    }
-}
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/mapping/impl/MappingTests.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/mapping/impl/MappingTests.java
deleted file mode 100644
index 5de81a8..0000000
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/mapping/impl/MappingTests.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping.impl;
-
-import java.util.List;
-
-import org.opensaml.core.xml.XMLObjectBaseTestCase;
-import org.opensaml.saml.saml2.metadata.AttributeConsumingService;
-import org.opensaml.saml.saml2.metadata.RequestedAttribute;
-
-/**
- * Base class for testing attribute mapping
- */
-public class MappingTests  extends XMLObjectBaseTestCase  {
-
-    public static final String FILE_PATH = "/net/shibboleth/idp/saml/impl/attribute/mapping/";
-
-    protected  static final String THE_FORMAT = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri";
-
-    protected  static final String SAML_NAME_ONE = "stdFormat";
-    
-    protected  static final String SAML_NAME_TWO = "oddFormat";
-    
-    protected  static final String SAML_NAME_THREE = "urn:oid:2.16.840.1.113730.3.1.241";
-    
-    protected List<RequestedAttribute> loadFile(final String file)
-    {
-        AttributeConsumingService service = unmarshallElement(FILE_PATH + file);
-        
-        return service.getRequestAttributes();
-    }
-
-    
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/mapping/impl/NativeSpringTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/mapping/impl/NativeSpringTest.java
deleted file mode 100644
index 2743f10..0000000
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/mapping/impl/NativeSpringTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping.impl;
-
-import java.util.Collection;
-
-import net.shibboleth.ext.spring.util.SchemaTypeAwareXMLBeanDefinitionReader;
-
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
-import org.springframework.context.support.GenericApplicationContext;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-/**
- * kicks the wheels tests.
- */
-public class NativeSpringTest {
-
-    protected <Type> Type getBean(String fileName, Class<Type> claz) {
-
-        GenericApplicationContext context = new GenericApplicationContext();
-        try {
-            SchemaTypeAwareXMLBeanDefinitionReader beanDefinitionReader =
-                    new SchemaTypeAwareXMLBeanDefinitionReader(context);
-    
-            beanDefinitionReader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);
-            beanDefinitionReader.loadBeanDefinitions(fileName);
-            
-            context.refresh();
-    
-            Collection<Type> beans = context.getBeansOfType(claz).values();
-            Assert.assertEquals(beans.size(), 1);
-    
-            return beans.iterator().next();
-        } finally {
-            context.close();
-        }
-    }
-
-    
-    @Test
-    public void stringAttrValue() {
-        RequestedAttributesMapper map = getBean(MappingTests.FILE_PATH + "attributesMapper.xml", RequestedAttributesMapper.class);
-        
-        LoggerFactory.getLogger(NativeSpringTest.class).debug(map.toString());
-    }
-}
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/mapping/impl/RequestedAttributeMapperTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/mapping/impl/RequestedAttributeMapperTest.java
deleted file mode 100644
index b29f6db..0000000
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/mapping/impl/RequestedAttributeMapperTest.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping.impl;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import net.shibboleth.idp.attribute.IdPRequestedAttribute;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-
-import org.opensaml.saml.saml2.metadata.RequestedAttribute;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-/**
- * Tests for {@link RequestedAttributeMapper}.
- */
-public class RequestedAttributeMapperTest extends MappingTests {
-
-    private static final String ID = "atributeID";
-
-    private List<RequestedAttribute> attributes;
-
-    private void loadAttributes() {
-        if (null != attributes) {
-            return;
-        }
-        attributes = loadFile("requestedAttributeValues.xml");
-    }
-
-    private RequestedAttribute getAttr(String attributeName) {
-        loadAttributes();
-        for (RequestedAttribute attribute : attributes) {
-            if (attributeName.equals(attribute.getName())) {
-                return attribute;
-            }
-        }
-        return null;
-    }
-
-    @Test public void setterGetterInit() throws ComponentInitializationException {
-        final RequestedAttributeMapper mapper = new RequestedAttributeMapper();
-
-        Assert.assertNull(mapper.getId());
-        Assert.assertTrue(mapper.getAttributeIds().isEmpty());
-        Assert.assertNull(mapper.getAttributeFormat());
-        Assert.assertNull(mapper.getSAMLName());
-        Assert.assertNull(mapper.getValueMapper());
-
-        mapper.setId(ID);
-        try {
-            mapper.initialize();
-            Assert.fail("SAML Name absent");
-        } catch (ComponentInitializationException e) {
-            // OK
-        }
-        mapper.setSAMLName(SAML_NAME_ONE);
-
-        try {
-            mapper.initialize();
-            Assert.fail("Value Mapper absent");
-        } catch (ComponentInitializationException e) {
-            // OK
-        }
-        mapper.setValueMapper(new StringAttributeValueMapper());
-
-        mapper.setAttributeIds(Collections.singletonList("one"));
-        mapper.setAttributeFormat(THE_FORMAT);
-
-        mapper.initialize();
-        Assert.assertEquals(mapper.getId(), ID);
-        Assert.assertEquals(mapper.getSAMLName(), SAML_NAME_ONE);
-        Assert.assertEquals(mapper.getAttributeFormat(), THE_FORMAT);
-
-        final List<String> list = mapper.getAttributeIds();
-        Assert.assertEquals(list.size(), 1);
-        Assert.assertEquals(list.get(0), "one");
-
-        Assert.assertTrue(mapper.getValueMapper() instanceof StringAttributeValueMapper);
-    }
-
-    @Test public void mapAttributeFormat() throws ComponentInitializationException {
-        RequestedAttributeMapper mapper = new RequestedAttributeMapper();
-
-        mapper.setId(ID);
-        mapper.setSAMLName(SAML_NAME_ONE);
-        mapper.setValueMapper(new StringAttributeValueMapper());
-        mapper.setAttributeIds(Arrays.asList("one", ID));
-        mapper.setAttributeFormat(THE_FORMAT);
-        mapper.initialize();
-
-        final Map<String, IdPRequestedAttribute> result =
-                mapper.mapAttribute(getAttr(SAML_NAME_ONE));
-
-        Assert.assertEquals(result.size(), 2);
-        Assert.assertTrue(result.containsKey(ID));
-        Assert.assertTrue(result.containsKey("one"));
-        Assert.assertNotNull(result.get(ID));
-        Assert.assertNotNull(result.get("one"));
-        Assert.assertEquals(result.get(ID).getValues().size(), 4);
-
-        Assert.assertTrue(mapper.mapAttribute(getAttr(SAML_NAME_TWO)).isEmpty());
-
-        mapper = new RequestedAttributeMapper();
-
-        mapper.setId(ID);
-        mapper.setSAMLName(SAML_NAME_TWO);
-        mapper.setAttributeFormat(THE_FORMAT);
-        mapper.setValueMapper(new StringAttributeValueMapper());
-        mapper.setAttributeIds(Collections.singletonList(ID));
-        mapper.initialize();
-        Assert.assertTrue(mapper.mapAttribute(getAttr(SAML_NAME_TWO)).isEmpty());
-
-    }
-
-    @Test public void mapAttributeNoFormat() throws ComponentInitializationException {
-        RequestedAttributeMapper mapper = new RequestedAttributeMapper();
-
-        mapper.setId(ID);
-        mapper.setAttributeIds(Collections.singletonList(ID));
-        mapper.setSAMLName(SAML_NAME_TWO);
-        mapper.setValueMapper(new StringAttributeValueMapper());
-        mapper.initialize();
-
-        Map<String, net.shibboleth.idp.attribute.IdPRequestedAttribute> result =
-                mapper.mapAttribute(getAttr(SAML_NAME_TWO));
-
-        Assert.assertEquals(result.size(), 1);
-        Assert.assertTrue(result.get(ID).getValues().isEmpty());
-
-    }
-
-    @Test public void mapNoValueMatch() throws ComponentInitializationException {
-        RequestedAttributeMapper mapper = new RequestedAttributeMapper();
-
-        mapper.setId(ID);
-        mapper.setAttributeIds(Collections.singletonList(ID));
-        mapper.setSAMLName(SAML_NAME_THREE);
-        mapper.setValueMapper(new ScopedStringAttributeValueMapper());
-        mapper.initialize();
-
-        Map<String, IdPRequestedAttribute> result =
-                mapper.mapAttribute(getAttr(SAML_NAME_THREE));
-        Assert.assertFalse(result.containsKey(ID));
-    }
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/mapping/impl/RequestedAttributesMapperTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/mapping/impl/RequestedAttributesMapperTest.java
deleted file mode 100644
index 060277b..0000000
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/mapping/impl/RequestedAttributesMapperTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements.  See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.mapping.impl;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-
-import net.shibboleth.idp.attribute.IdPRequestedAttribute;
-import net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributeValueMapper;
-import net.shibboleth.idp.saml.attribute.mapping.AttributeMapper;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-
-import org.opensaml.saml.saml2.metadata.RequestedAttribute;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.Multimap;
-
-/**
- * tests for {@link RequestedAttributesMapper}.
- */
-public class RequestedAttributesMapperTest extends MappingTests {
-    
-    private AttributeMapper<RequestedAttribute, IdPRequestedAttribute> buildMapper(String name, String samlFormat, AbstractSAMLAttributeValueMapper mapper) {
-        
-        RequestedAttributeMapper result = new RequestedAttributeMapper();
-        result.setId(name);
-        result.setAttributeIds(Collections.singletonList(name));
-        result.setSAMLName(samlFormat);
-        result.setValueMapper(mapper);
-        return result;
-    }
-    
-    
-    @Test public void setterGetter() throws ComponentInitializationException {
-        
-        RequestedAttributesMapper mapper = new RequestedAttributesMapper();
-        mapper.setId("id");
-        Assert.assertTrue(mapper.getMappers().isEmpty());
-        mapper.setMappers(Collections.singletonList(buildMapper("name", "saml", new StringAttributeValueMapper())));
-        
-        mapper.initialize();
-    }
-
-
-    @Test public void map() throws ComponentInitializationException {
-        
-        RequestedAttributesMapper mapper = new RequestedAttributesMapper();
-        mapper.setId("id");
-   
-        mapper.setMappers(Arrays.asList(buildMapper("id", SAML_NAME_ONE, new StringAttributeValueMapper()),
-                buildMapper("id", SAML_NAME_THREE, new ScopedStringAttributeValueMapper()),
-                buildMapper("id2", SAML_NAME_THREE, new StringAttributeValueMapper())));
-        
-        mapper.initialize();
-        
-        Multimap<String, IdPRequestedAttribute> result = mapper.mapAttributes(loadFile("requestedAttributeValues.xml"));
-        
-        Assert.assertEquals(result.keySet().size(), 2);
-        
-        Collection<IdPRequestedAttribute> id = result.get("id");
-        Assert.assertEquals(id.size(), 1);
-        Iterator<IdPRequestedAttribute> itr = id.iterator();
-        Assert.assertEquals(itr.next().getId(), "id");
-        
-        Assert.assertEquals(result.get("id2").size(), 1);
-    }
-}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1ByteAttributeTranscoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1ByteAttributeTranscoderTest.java
new file mode 100644
index 0000000..ebce4b8
--- /dev/null
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1ByteAttributeTranscoderTest.java
@@ -0,0 +1,368 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.shibboleth.idp.attribute.AttributeDecodingException;
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.ByteAttributeValue;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.IdPRequestedAttribute;
+import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.utilities.java.support.codec.Base64Support;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+import org.opensaml.core.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.XMLObjectBuilder;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.schema.XSBase64Binary;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml1.core.Attribute;
+import org.opensaml.saml.saml1.core.AttributeValue;
+import org.opensaml.saml.saml1.core.AttributeDesignator;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/** {@link SAML1ByteAttributeTranscoder} unit test. */
+public class SAML1ByteAttributeTranscoderTest extends OpenSAMLInitBaseTestCase {
+
+    private AttributeTranscoderRegistryImpl registry;
+    
+    private XMLObjectBuilder<XSString> stringBuilder;
+
+    private SAMLObjectBuilder<Attribute> attributeBuilder;
+
+    private SAMLObjectBuilder<AttributeDesignator> designatorBuilder;
+
+    private final static String ATTR_NAME = "foo";
+    private final static String ATTR_NAMESPACE = "Namespace";
+    private final static byte[] BYTE_ARRAY_1 = {1, 2, 3, 4, 5};
+    private final static byte[] BYTE_ARRAY_2 = {4, 3, 2, 1};
+
+    @BeforeClass public void setUp() throws ComponentInitializationException {
+        
+        stringBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSString>getBuilderOrThrow(XSString.TYPE_NAME);
+        
+        attributeBuilder = (SAMLObjectBuilder<Attribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<Attribute>getBuilderOrThrow(
+                        Attribute.TYPE_NAME);
+        designatorBuilder = (SAMLObjectBuilder<AttributeDesignator>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<AttributeDesignator>getBuilderOrThrow(
+                        AttributeDesignator.TYPE_NAME);
+        
+        registry = new AttributeTranscoderRegistryImpl();
+        registry.setId("test");
+                
+        final SAML1ByteAttributeTranscoder transcoder = new SAML1ByteAttributeTranscoder();
+        transcoder.initialize();
+        
+        registry.setNamingRegistry(Collections.singletonMap(transcoder.getEncodedType(),
+                new AbstractSAML1AttributeTranscoder.NamingFunction()));
+        
+        final Map<String,Object> ruleset1 = new HashMap<>();
+        ruleset1.put(AttributeTranscoderRegistry.PROP_ID, ATTR_NAME);
+        ruleset1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        ruleset1.put(SAMLAttributeTranscoder.PROP_ENCODE_TYPE, true);
+        ruleset1.put(SAMLAttributeTranscoder.PROP_NAME, ATTR_NAME);
+        ruleset1.put(SAML1AttributeTranscoder.PROP_NAMESPACE, ATTR_NAMESPACE);
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset1)));
+        
+        registry.initialize();
+    }
+    
+    @AfterClass public void tearDown() {
+        registry.destroy();
+        registry = null;
+    }
+
+    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void emptyEncode() throws Exception {
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+    }
+
+    @Test public void emptyRequestedEncode() throws Exception {
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, AttributeDesignator.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final AttributeDesignator attr = TranscoderSupport.<AttributeDesignator>getTranscoder(ruleset).encode(
+                null, inputAttribute, AttributeDesignator.class, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getAttributeName(), ATTR_NAME);
+        Assert.assertEquals(attr.getAttributeNamespace(), ATTR_NAMESPACE);
+    }
+
+    @Test public void emptyDecode() throws Exception {
+        
+        // This isn't technically legal in SAML, but it should functionally work in this direction.
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setAttributeName(ATTR_NAME);
+        samlAttribute.setAttributeNamespace(ATTR_NAMESPACE);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+
+    @Test public void emptyRequestedDecode() throws Exception {
+        
+        final AttributeDesignator samlAttribute = designatorBuilder.buildObject();
+        samlAttribute.setAttributeName(ATTR_NAME);
+        samlAttribute.setAttributeNamespace(ATTR_NAMESPACE);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<AttributeDesignator>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertTrue(attr instanceof IdPRequestedAttribute);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertFalse(((IdPRequestedAttribute) attr).getIsRequired());
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+    
+    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
+        final int[] intArray = {1, 2, 3, 4};
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new StringAttributeValue("foo"), new ScopedStringAttributeValue("foo", "bar"),
+                        new IdPAttributeValue<Object>() {
+                            public Object getValue() {
+                                return intArray;
+                            }
+                            public String getDisplayValue() {
+                                return intArray.toString();
+                            }
+                        });
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(null, inputAttribute, Attribute.class, ruleset);
+    }
+    
+    @Test public void single() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new StringAttributeValue("foo"), new ByteAttributeValue(BYTE_ARRAY_1));
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getAttributeName(), ATTR_NAME);
+        Assert.assertEquals(attr.getAttributeNamespace(), ATTR_NAMESPACE);
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+
+        Assert.assertEquals(children.size(), 1, "Encoding one entry");
+
+        final XMLObject child = children.get(0);
+
+        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+        Assert.assertTrue(child instanceof XSBase64Binary, "Child of result attribute should be a base64Binary");
+
+        XSBase64Binary childAsString = (XSBase64Binary) child;
+
+        byte childAsBa[] = Base64Support.decode(childAsString.getValue());
+
+        Assert.assertEquals(childAsBa, BYTE_ARRAY_1, "Input equals output");
+    }
+
+    @Test public void singleRequested() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new StringAttributeValue("foo"), new ByteAttributeValue(BYTE_ARRAY_1));
+
+        final IdPRequestedAttribute inputAttribute = new IdPRequestedAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+
+        final AttributeDesignator attr = TranscoderSupport.<AttributeDesignator>getTranscoder(ruleset).encode(
+                null, inputAttribute, AttributeDesignator.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getAttributeName(), ATTR_NAME);
+        Assert.assertEquals(attr.getAttributeNamespace(), ATTR_NAMESPACE);
+    }
+    
+    @Test public void singleDecode() throws Exception {
+                
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue(Base64Support.encode(BYTE_ARRAY_1, Base64Support.UNCHUNKED));
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setAttributeName(ATTR_NAME);
+        samlAttribute.setAttributeNamespace(ATTR_NAMESPACE);
+        samlAttribute.getAttributeValues().add(stringValue);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertEquals(attr.getValues().size(), 1);
+        Assert.assertEquals(attr.getValues().get(0).getValue(), BYTE_ARRAY_1);
+    }
+
+    @Test(expectedExceptions = {AttributeDecodingException.class,}) public void badDecode() throws Exception {
+        
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue("******");
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setAttributeName(ATTR_NAME);
+        samlAttribute.setAttributeNamespace(ATTR_NAMESPACE);
+        samlAttribute.getAttributeValues().add(stringValue);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+    }
+    
+    @Test public void multi() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(BYTE_ARRAY_1), new ByteAttributeValue(BYTE_ARRAY_2));
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+        Assert.assertEquals(children.size(), 2, "Encoding three entries");
+
+        XMLObject child = children.get(0);
+        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+        Assert.assertTrue(child instanceof XSBase64Binary, "Child of result attribute should be a base64Binary");
+
+        XSBase64Binary childAsString = (XSBase64Binary) child;
+        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+        final byte[] res0 = Base64Support.decode(childAsString.getValue());
+        
+        child = children.get(1);
+        Assert.assertTrue(child instanceof XSBase64Binary, "Child of result attribute should be a base64Binary");
+
+        childAsString = (XSBase64Binary) child;
+        final byte[] res1 = Base64Support.decode(childAsString.getValue());
+
+        //
+        // order of results is not guaranteed so sense the result from the length
+        //
+        if (BYTE_ARRAY_1.length == res0.length) {
+            Assert.assertEquals(BYTE_ARRAY_1, res0, "Input matches output");
+            Assert.assertEquals(BYTE_ARRAY_2, res1, "Input matches output");
+        } else if (BYTE_ARRAY_1.length == res1.length) {
+            Assert.assertEquals(BYTE_ARRAY_1, res1, "Input matches output");
+            Assert.assertEquals(BYTE_ARRAY_2, res0, "Input matches output");
+        } else {
+            Assert.assertTrue(BYTE_ARRAY_1.length == res1.length || BYTE_ARRAY_2.length == res1.length,
+                    "One of the output's size should match an input size");
+        }
+    }
+
+    @Test public void multiDecode() throws Exception {
+        
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue(Base64Support.encode(BYTE_ARRAY_1, Base64Support.UNCHUNKED));
+
+        final XSString stringValue2 = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue2.setValue(Base64Support.encode(BYTE_ARRAY_2, Base64Support.UNCHUNKED));
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setAttributeName(ATTR_NAME);
+        samlAttribute.setAttributeNamespace(ATTR_NAMESPACE);
+        samlAttribute.getAttributeValues().add(stringValue);
+        samlAttribute.getAttributeValues().add(stringValue2);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertEquals(attr.getValues().size(), 2);
+        Assert.assertEquals(attr.getValues().get(0).getValue(), BYTE_ARRAY_1);
+        Assert.assertEquals(attr.getValues().get(1).getValue(), BYTE_ARRAY_2);
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1ScopedStringAttributeTranscoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1ScopedStringAttributeTranscoderTest.java
new file mode 100644
index 0000000..1071168
--- /dev/null
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1ScopedStringAttributeTranscoderTest.java
@@ -0,0 +1,370 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.ByteAttributeValue;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.IdPRequestedAttribute;
+import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.idp.saml.xmlobject.ScopedValue;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+import org.opensaml.core.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.XMLObjectBuilder;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml1.core.Attribute;
+import org.opensaml.saml.saml1.core.AttributeDesignator;
+import org.opensaml.saml.saml1.core.AttributeValue;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/** {@link SAML1ScopedStringAttributeTranscoder} unit test. */
+public class SAML1ScopedStringAttributeTranscoderTest extends OpenSAMLInitBaseTestCase {
+
+    private AttributeTranscoderRegistryImpl registry;
+
+    private XMLObjectBuilder<XSString> stringBuilder;
+
+    private XMLObjectBuilder<ScopedValue> scopedBuilder;
+
+    private SAMLObjectBuilder<Attribute> attributeBuilder;
+
+    private SAMLObjectBuilder<AttributeDesignator> designatorBuilder;
+
+    private final static String ATTR_NAME = "foo";
+    private final static String ATTR_NAMESPACE = "Namespace";
+    private final static String STRING_1 = "Value The First";
+    private final static String STRING_2 = "Second string the value is";
+    private final static String SCOPE_1 = "scope1.example.org";
+    private final static String SCOPE_2 = "scope2";
+    private final static String DELIMITER = "#";
+
+    @BeforeClass public void setUp() throws ComponentInitializationException {
+        
+        stringBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSString>getBuilderOrThrow(
+                XSString.TYPE_NAME);
+
+        scopedBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<ScopedValue>getBuilderOrThrow(
+                ScopedValue.TYPE_NAME);
+        
+        attributeBuilder = (SAMLObjectBuilder<Attribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<Attribute>getBuilderOrThrow(
+                        Attribute.TYPE_NAME);
+        designatorBuilder = (SAMLObjectBuilder<AttributeDesignator>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<AttributeDesignator>getBuilderOrThrow(
+                        AttributeDesignator.TYPE_NAME);
+        
+        registry = new AttributeTranscoderRegistryImpl();
+        registry.setId("test");
+        
+        final SAML1ScopedStringAttributeTranscoder transcoder = new SAML1ScopedStringAttributeTranscoder();
+        transcoder.initialize();
+        
+        registry.setNamingRegistry(Collections.singletonMap(transcoder.getEncodedType(),
+                new AbstractSAML1AttributeTranscoder.NamingFunction()));
+        
+        final Map<String,Object> ruleset1 = new HashMap<>();
+        ruleset1.put(AttributeTranscoderRegistry.PROP_ID, ATTR_NAME);
+        ruleset1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        ruleset1.put(SAMLAttributeTranscoder.PROP_ENCODE_TYPE, true);
+        ruleset1.put(SAMLAttributeTranscoder.PROP_NAME, ATTR_NAME);
+        ruleset1.put(SAML1AttributeTranscoder.PROP_NAMESPACE, ATTR_NAMESPACE);
+        ruleset1.put(SAML1ScopedStringAttributeTranscoder.PROP_SCOPE_DELIMITER, DELIMITER);
+        ruleset1.put(SAML1ScopedStringAttributeTranscoder.PROP_SCOPE_TYPE, "attribute");
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset1)));
+        
+        registry.initialize();
+    }
+    
+    @AfterClass public void tearDown() {
+        registry.destroy();
+        registry = null;
+    }
+
+    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void emptyEncode() throws Exception {
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(null, inputAttribute, Attribute.class, ruleset);
+    }
+
+    @Test public void emptyDecode() throws Exception {
+        
+        // This isn't technically legal in SAML, but it should functionally work in this direction.
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setAttributeName(ATTR_NAME);
+        samlAttribute.setAttributeNamespace(ATTR_NAMESPACE);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+
+    @Test public void emptyRequestedDecode() throws Exception {
+        
+        final AttributeDesignator samlAttribute = designatorBuilder.buildObject();
+        samlAttribute.setAttributeName(ATTR_NAME);
+        samlAttribute.setAttributeNamespace(ATTR_NAMESPACE);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<AttributeDesignator>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertTrue(attr instanceof IdPRequestedAttribute);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertFalse(((IdPRequestedAttribute) attr).getIsRequired());
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+    
+    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
+        final int[] intArray = {1, 2, 3, 4};
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new IdPAttributeValue<Object>() {
+                    @Override
+                    public Object getValue() {
+                        return intArray;
+                    }
+                    @Override
+                    public String getDisplayValue() {
+                        return intArray.toString();
+                    }
+                });
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(null, inputAttribute, Attribute.class, ruleset);
+    }
+    
+    @Test public void single() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}),
+                        new ScopedStringAttributeValue(STRING_1, SCOPE_1),
+                        new StringAttributeValue(STRING_1),
+                        new StringAttributeValue(STRING_1 + "@" + SCOPE_1));
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getAttributeName(), ATTR_NAME);
+        Assert.assertEquals(attr.getAttributeNamespace(), ATTR_NAMESPACE);
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+
+        Assert.assertEquals(children.size(), 1, "Encoding one entry");
+
+        final XMLObject child = children.get(0);
+
+        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+
+        Assert.assertTrue(child instanceof ScopedValue, "Child of result attribute should be a string");
+
+        final ScopedValue childAsScopedValue = (ScopedValue) child;
+
+        Assert.assertEquals(childAsScopedValue.getValue(), STRING_1, "Input equals output");
+        Assert.assertEquals(childAsScopedValue.getScope(), SCOPE_1, "Input equals output");
+    }
+
+    @Test public void singleRequested() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}),
+                        new ScopedStringAttributeValue(STRING_1, SCOPE_1));
+
+        final IdPRequestedAttribute inputAttribute = new IdPRequestedAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+
+        final AttributeDesignator attr = TranscoderSupport.<AttributeDesignator>getTranscoder(ruleset).encode(
+                null, inputAttribute, AttributeDesignator.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getAttributeName(), ATTR_NAME);
+        Assert.assertEquals(attr.getAttributeNamespace(), ATTR_NAMESPACE);
+    }
+    
+    @Test public void singleDecode() throws Exception {
+                
+        final ScopedValue scopedValue = scopedBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        scopedValue.setScopeAttributeName("Scope");
+        scopedValue.setValue(STRING_1);
+        scopedValue.setScope(SCOPE_1);
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setAttributeName(ATTR_NAME);
+        samlAttribute.setAttributeNamespace(ATTR_NAMESPACE);
+        samlAttribute.getAttributeValues().add(scopedValue);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertEquals(attr.getValues().size(), 1);
+        
+        final ScopedStringAttributeValue value = (ScopedStringAttributeValue) attr.getValues().get(0);
+        Assert.assertEquals(value.getValue(), STRING_1);
+        Assert.assertEquals(value.getScope(), SCOPE_1);
+    }
+        
+    @Test public void multi() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}),
+                        new ScopedStringAttributeValue(STRING_1, SCOPE_1),
+                        new ScopedStringAttributeValue(STRING_2, SCOPE_2));
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+        Assert.assertEquals(children.size(), 2, "Encoding 2 entries");
+
+        Assert.assertTrue(children.get(0) instanceof ScopedValue && children.get(1) instanceof ScopedValue,
+                "Child of result attribute should be a string");
+
+        final ScopedValue child1 = (ScopedValue) children.get(0);
+        Assert.assertEquals(child1.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+
+        final ScopedValue child2 = (ScopedValue) children.get(1);
+        Assert.assertEquals(child2.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+        //
+        // order of results is not guaranteed so sense the result from the length
+        //
+        if (child1.getValue().length() == STRING_1.length()) {
+            Assert.assertEquals(child1.getValue(), STRING_1, "Input matches output");
+            Assert.assertEquals(child2.getValue(), STRING_2, "Input matches output");
+            Assert.assertEquals(child1.getScope(), SCOPE_1, "Input matches output");
+            Assert.assertEquals(child2.getScope(), SCOPE_2, "Input matches output");
+        } else if (child1.getValue().length() == STRING_2.length()) {
+            Assert.assertEquals(child2.getValue(), STRING_1, "Input matches output");
+            Assert.assertEquals(child1.getValue(), STRING_2, "Input matches output");
+            Assert.assertEquals(child2.getScope(), SCOPE_1, "Input matches output");
+            Assert.assertEquals(child1.getScope(), SCOPE_2, "Input matches output");
+        } else {
+            Assert.fail("Value mismatch");
+        }
+    }
+
+    @Test public void multiDecode() throws Exception {
+        
+        final ScopedValue scopedValue = scopedBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        scopedValue.setScopeAttributeName("Scope");
+        scopedValue.setValue(STRING_1);
+        scopedValue.setScope(SCOPE_1);
+
+        final ScopedValue scopedValue2 = scopedBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        scopedValue2.setScopeAttributeName("Scope");
+        scopedValue2.setValue(STRING_2);
+        scopedValue2.setScope(SCOPE_2);
+
+        final XSString stringValue3 = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue3.setValue(STRING_2 + "@" + SCOPE_2);
+
+        final XSString stringValue4 = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue4.setValue(STRING_2);
+
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setAttributeName(ATTR_NAME);
+        samlAttribute.setAttributeNamespace(ATTR_NAMESPACE);
+        samlAttribute.getAttributeValues().add(scopedValue);
+        samlAttribute.getAttributeValues().add(scopedValue2);
+        samlAttribute.getAttributeValues().add(stringValue3);
+        samlAttribute.getAttributeValues().add(stringValue4);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertEquals(attr.getValues().size(), 2);
+        
+        final ScopedStringAttributeValue value1 = (ScopedStringAttributeValue) attr.getValues().get(0);
+        final ScopedStringAttributeValue value2 = (ScopedStringAttributeValue) attr.getValues().get(1);
+        Assert.assertTrue(STRING_1.equals(value1.getValue()) || STRING_1.equals(value2.getValue()));
+        Assert.assertTrue(STRING_2.equals(value1.getValue()) || STRING_2.equals(value2.getValue()));
+        Assert.assertTrue(SCOPE_1.equals(value1.getScope()) || SCOPE_1.equals(value2.getScope()));
+        Assert.assertTrue(SCOPE_2.equals(value1.getScope()) || SCOPE_2.equals(value2.getScope()));
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1StringAttributeTranscoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1StringAttributeTranscoderTest.java
new file mode 100644
index 0000000..ad33578
--- /dev/null
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML1StringAttributeTranscoderTest.java
@@ -0,0 +1,327 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.ByteAttributeValue;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.IdPRequestedAttribute;
+import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+import org.opensaml.core.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.XMLObjectBuilder;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml1.core.Attribute;
+import org.opensaml.saml.saml1.core.AttributeValue;
+import org.opensaml.saml.saml1.core.AttributeDesignator;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/** {@link SAML1StringAttributeTranscoder} unit test. */
+public class SAML1StringAttributeTranscoderTest extends OpenSAMLInitBaseTestCase {
+
+    private AttributeTranscoderRegistryImpl registry;
+    
+    private XMLObjectBuilder<XSString> stringBuilder;
+
+    private SAMLObjectBuilder<Attribute> attributeBuilder;
+
+    private SAMLObjectBuilder<AttributeDesignator> designatorBuilder;
+
+    private final static String ATTR_NAME = "foo";
+    private final static String ATTR_NAMESPACE = "Namespace";
+    private final static String STRING_1 = "Value The First";
+    private final static String STRING_2 = "Second string the value is";
+
+    @BeforeClass public void setUp() throws ComponentInitializationException {
+        
+        stringBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSString>getBuilderOrThrow(XSString.TYPE_NAME);
+        
+        attributeBuilder = (SAMLObjectBuilder<Attribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<Attribute>getBuilderOrThrow(
+                        Attribute.TYPE_NAME);
+        designatorBuilder = (SAMLObjectBuilder<AttributeDesignator>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<AttributeDesignator>getBuilderOrThrow(
+                        AttributeDesignator.TYPE_NAME);
+        
+        registry = new AttributeTranscoderRegistryImpl();
+        registry.setId("test");
+                
+        final SAML1StringAttributeTranscoder transcoder = new SAML1StringAttributeTranscoder();
+        transcoder.initialize();
+        
+        registry.setNamingRegistry(Collections.singletonMap(transcoder.getEncodedType(),
+                new AbstractSAML1AttributeTranscoder.NamingFunction()));
+        
+        final Map<String,Object> ruleset1 = new HashMap<>();
+        ruleset1.put(AttributeTranscoderRegistry.PROP_ID, ATTR_NAME);
+        ruleset1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        ruleset1.put(SAMLAttributeTranscoder.PROP_ENCODE_TYPE, true);
+        ruleset1.put(SAMLAttributeTranscoder.PROP_NAME, ATTR_NAME);
+        ruleset1.put(SAML1AttributeTranscoder.PROP_NAMESPACE, ATTR_NAMESPACE);
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset1)));
+        
+        registry.initialize();
+    }
+    
+    @AfterClass public void tearDown() {
+        registry.destroy();
+        registry = null;
+    }
+
+    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void emptyEncode() throws Exception {
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+    }
+
+    @Test public void emptyRequestedEncode() throws Exception {
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, AttributeDesignator.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final AttributeDesignator attr = TranscoderSupport.<AttributeDesignator>getTranscoder(ruleset).encode(
+                null, inputAttribute, AttributeDesignator.class, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getAttributeName(), ATTR_NAME);
+        Assert.assertEquals(attr.getAttributeNamespace(), ATTR_NAMESPACE);
+    }
+
+    @Test public void emptyDecode() throws Exception {
+        
+        // This isn't technically legal in SAML, but it should functionally work in this direction.
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setAttributeName(ATTR_NAME);
+        samlAttribute.setAttributeNamespace(ATTR_NAMESPACE);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+
+    @Test public void emptyRequestedDecode() throws Exception {
+        
+        final AttributeDesignator samlAttribute = designatorBuilder.buildObject();
+        samlAttribute.setAttributeName(ATTR_NAME);
+        samlAttribute.setAttributeNamespace(ATTR_NAMESPACE);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<AttributeDesignator>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertTrue(attr instanceof IdPRequestedAttribute);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertFalse(((IdPRequestedAttribute) attr).getIsRequired());
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+    
+    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
+        final int[] intArray = {1, 2, 3, 4};
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new IdPAttributeValue<Object>() {
+                    @Override
+                    public Object getValue() {
+                        return intArray;
+                    }
+                    @Override
+                    public String getDisplayValue() {
+                        return intArray.toString();
+                    }
+                });
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(null, inputAttribute, Attribute.class, ruleset);
+    }
+    
+    @Test public void single() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new StringAttributeValue(STRING_1));
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getAttributeName(), ATTR_NAME);
+        Assert.assertEquals(attr.getAttributeNamespace(), ATTR_NAMESPACE);
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+
+        Assert.assertEquals(children.size(), 1, "Encoding one entry");
+
+        final XMLObject child = children.get(0);
+
+        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+
+        Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
+
+        final XSString childAsString = (XSString) child;
+
+        Assert.assertEquals(childAsString.getValue(), STRING_1);
+    }
+
+    @Test public void singleRequested() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new StringAttributeValue(STRING_1));
+
+        final IdPRequestedAttribute inputAttribute = new IdPRequestedAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+
+        final AttributeDesignator attr = TranscoderSupport.<AttributeDesignator>getTranscoder(ruleset).encode(
+                null, inputAttribute, AttributeDesignator.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getAttributeName(), ATTR_NAME);
+        Assert.assertEquals(attr.getAttributeNamespace(), ATTR_NAMESPACE);
+    }
+    
+    @Test public void singleDecode() throws Exception {
+                
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue(STRING_1);
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setAttributeName(ATTR_NAME);
+        samlAttribute.setAttributeNamespace(ATTR_NAMESPACE);
+        samlAttribute.getAttributeValues().add(stringValue);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertEquals(attr.getValues().size(), 1);
+        Assert.assertEquals(attr.getValues().get(0).getValue().toString(), STRING_1);
+    }
+        
+    @Test public void multi() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}),
+                        new StringAttributeValue(STRING_1),
+                        new StringAttributeValue(STRING_2),
+                        new ScopedStringAttributeValue(STRING_1, STRING_2));
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+        Assert.assertEquals(children.size(), 3, "Encoding three entries");
+
+        for (final XMLObject child: children) {
+            Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
+            final String childAsString = ((XSString) children.get(0)).getValue();
+            Assert.assertTrue(STRING_1.equals(childAsString)||STRING_2.equals(childAsString));
+        }
+    }
+
+    @Test public void multiDecode() throws Exception {
+        
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue(STRING_1);
+
+        final XSString stringValue2 = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue2.setValue(STRING_2);
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setAttributeName(ATTR_NAME);
+        samlAttribute.setAttributeNamespace(ATTR_NAMESPACE);
+        samlAttribute.getAttributeValues().add(stringValue);
+        samlAttribute.getAttributeValues().add(stringValue2);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertEquals(attr.getValues().size(), 2);
+        Assert.assertEquals(attr.getValues().get(0).getValue().toString(), STRING_1);
+        Assert.assertEquals(attr.getValues().get(1).getValue().toString(), STRING_2);
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2ByteAttributeTranscoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2ByteAttributeTranscoderTest.java
new file mode 100644
index 0000000..203a93c
--- /dev/null
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2ByteAttributeTranscoderTest.java
@@ -0,0 +1,405 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.shibboleth.idp.attribute.AttributeDecodingException;
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.ByteAttributeValue;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.IdPRequestedAttribute;
+import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.utilities.java.support.codec.Base64Support;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+import org.opensaml.core.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.XMLObjectBuilder;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.schema.XSBase64Binary;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.AttributeValue;
+import org.opensaml.saml.saml2.metadata.RequestedAttribute;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/** {@link SAML2ByteAttributeTranscoder} unit test. */
+public class SAML2ByteAttributeTranscoderTest extends OpenSAMLInitBaseTestCase {
+
+    private AttributeTranscoderRegistryImpl registry;
+    
+    private XMLObjectBuilder<XSString> stringBuilder;
+
+    private SAMLObjectBuilder<Attribute> attributeBuilder;
+
+    private SAMLObjectBuilder<RequestedAttribute> reqAttributeBuilder;
+
+    private final static String ATTR_NAME = "foo";
+    private final static String ATTR_NAMEFORMAT = "Namespace";
+    private final static String ATTR_FRIENDLYNAME = "friendly";
+    private final static byte[] BYTE_ARRAY_1 = {1, 2, 3, 4, 5};
+    private final static byte[] BYTE_ARRAY_2 = {4, 3, 2, 1};
+
+    @BeforeClass public void setUp() throws ComponentInitializationException {
+        
+        stringBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSString>getBuilderOrThrow(XSString.TYPE_NAME);
+        
+        attributeBuilder = (SAMLObjectBuilder<Attribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<Attribute>getBuilderOrThrow(
+                        Attribute.TYPE_NAME);
+        reqAttributeBuilder = (SAMLObjectBuilder<RequestedAttribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<RequestedAttribute>getBuilderOrThrow(
+                        RequestedAttribute.TYPE_NAME);
+        
+        registry = new AttributeTranscoderRegistryImpl();
+        registry.setId("test");
+
+        final SAML2ByteAttributeTranscoder transcoder = new SAML2ByteAttributeTranscoder();
+        transcoder.initialize();
+        
+        registry.setNamingRegistry(Collections.singletonMap(transcoder.getEncodedType(),
+                new AbstractSAML2AttributeTranscoder.NamingFunction()));
+        
+        final Map<String,Object> ruleset1 = new HashMap<>();
+        ruleset1.put(AttributeTranscoderRegistry.PROP_ID, ATTR_NAME);
+        ruleset1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        ruleset1.put(SAMLAttributeTranscoder.PROP_ENCODE_TYPE, true);
+        ruleset1.put(SAMLAttributeTranscoder.PROP_NAME, ATTR_NAME);
+        ruleset1.put(SAML2AttributeTranscoder.PROP_NAME_FORMAT, ATTR_NAMEFORMAT);
+        ruleset1.put(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME, ATTR_FRIENDLYNAME);
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset1)));
+        
+        registry.initialize();
+    }
+    
+    @AfterClass public void tearDown() {
+        registry.destroy();
+        registry = null;
+    }
+
+    @Test public void emptyEncode() throws Exception {
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getName(), ATTR_NAME);
+        Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+        Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+        Assert.assertTrue(attr.getAttributeValues().isEmpty());
+    }
+
+    @Test public void emptyDecode() throws Exception {
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+
+    @Test public void emptyRequestedDecode() throws Exception {
+        
+        final RequestedAttribute samlAttribute = reqAttributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.setIsRequired(true);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertTrue(attr instanceof IdPRequestedAttribute);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(((IdPRequestedAttribute) attr).getIsRequired());
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+    
+    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
+        final int[] intArray = {1, 2, 3, 4};
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new StringAttributeValue("foo"), new ScopedStringAttributeValue("foo", "bar"),
+                        new IdPAttributeValue<Object>() {
+                            public Object getValue() {
+                                return intArray;
+                            }
+                            public String getDisplayValue() {
+                                return intArray.toString();
+                            }
+                        });
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(null, inputAttribute, Attribute.class, ruleset);
+    }
+    
+    @Test public void single() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new StringAttributeValue("foo"), new ByteAttributeValue(BYTE_ARRAY_1));
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getName(), ATTR_NAME);
+        Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+        Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+
+        Assert.assertEquals(children.size(), 1, "Encoding one entry");
+
+        final XMLObject child = children.get(0);
+
+        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+        Assert.assertTrue(child instanceof XSBase64Binary, "Child of result attribute should be a base64Binary");
+
+        XSBase64Binary childAsString = (XSBase64Binary) child;
+
+        byte childAsBa[] = Base64Support.decode(childAsString.getValue());
+
+        Assert.assertEquals(childAsBa, BYTE_ARRAY_1, "Input equals output");
+    }
+
+    @Test public void singleRequested() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new StringAttributeValue("foo"), new ByteAttributeValue(BYTE_ARRAY_1));
+
+        final IdPRequestedAttribute inputAttribute = new IdPRequestedAttribute(ATTR_NAME);
+        inputAttribute.setRequired(true);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+
+        final RequestedAttribute attr = TranscoderSupport.<RequestedAttribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, RequestedAttribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getName(), ATTR_NAME);
+        Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+        Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+        Assert.assertTrue(attr.isRequired());
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+
+        Assert.assertEquals(children.size(), 1, "Encoding one entry");
+
+        final XMLObject child = children.get(0);
+
+        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+        Assert.assertTrue(child instanceof XSBase64Binary, "Child of result attribute should be a base64Binary");
+
+        XSBase64Binary childAsString = (XSBase64Binary) child;
+
+        byte childAsBa[] = Base64Support.decode(childAsString.getValue());
+
+        Assert.assertEquals(childAsBa, BYTE_ARRAY_1, "Input equals output");
+    }
+    
+    @Test public void singleDecode() throws Exception {
+                
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue(Base64Support.encode(BYTE_ARRAY_1, Base64Support.UNCHUNKED));
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.getAttributeValues().add(stringValue);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertEquals(attr.getValues().size(), 1);
+        Assert.assertEquals(attr.getValues().get(0).getValue(), BYTE_ARRAY_1);
+    }
+    
+    @Test(expectedExceptions = {AttributeDecodingException.class,}) public void badDecode() throws Exception {
+        
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue("******");
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.getAttributeValues().add(stringValue);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+    }
+    
+    @Test public void singleRequestedDecode() throws Exception {
+        
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue(Base64Support.encode(BYTE_ARRAY_1, Base64Support.UNCHUNKED));
+        
+        final RequestedAttribute samlAttribute = reqAttributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.setIsRequired(true);
+        samlAttribute.getAttributeValues().add(stringValue);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertTrue(attr instanceof IdPRequestedAttribute);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(((IdPRequestedAttribute) attr).getIsRequired());
+        Assert.assertEquals(attr.getValues().size(), 1);
+        Assert.assertEquals(attr.getValues().get(0).getValue(), BYTE_ARRAY_1);
+    }
+    
+    @Test public void multi() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(BYTE_ARRAY_1), new ByteAttributeValue(BYTE_ARRAY_2));
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+        Assert.assertEquals(children.size(), 2, "Encoding three entries");
+
+        XMLObject child = children.get(0);
+        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+        Assert.assertTrue(child instanceof XSBase64Binary, "Child of result attribute should be a base64Binary");
+
+        XSBase64Binary childAsString = (XSBase64Binary) child;
+        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+        final byte[] res0 = Base64Support.decode(childAsString.getValue());
+        
+        child = children.get(1);
+        Assert.assertTrue(child instanceof XSBase64Binary, "Child of result attribute should be a base64Binary");
+
+        childAsString = (XSBase64Binary) child;
+        final byte[] res1 = Base64Support.decode(childAsString.getValue());
+
+        //
+        // order of results is not guaranteed so sense the result from the length
+        //
+        if (BYTE_ARRAY_1.length == res0.length) {
+            Assert.assertEquals(BYTE_ARRAY_1, res0, "Input matches output");
+            Assert.assertEquals(BYTE_ARRAY_2, res1, "Input matches output");
+        } else if (BYTE_ARRAY_1.length == res1.length) {
+            Assert.assertEquals(BYTE_ARRAY_1, res1, "Input matches output");
+            Assert.assertEquals(BYTE_ARRAY_2, res0, "Input matches output");
+        } else {
+            Assert.assertTrue(BYTE_ARRAY_1.length == res1.length || BYTE_ARRAY_2.length == res1.length,
+                    "One of the output's size should match an input size");
+        }
+    }
+
+    @Test public void multiDecode() throws Exception {
+        
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue(Base64Support.encode(BYTE_ARRAY_1, Base64Support.UNCHUNKED));
+
+        final XSString stringValue2 = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue2.setValue(Base64Support.encode(BYTE_ARRAY_2, Base64Support.UNCHUNKED));
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.getAttributeValues().add(stringValue);
+        samlAttribute.getAttributeValues().add(stringValue2);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertEquals(attr.getValues().size(), 2);
+        Assert.assertEquals(attr.getValues().get(0).getValue(), BYTE_ARRAY_1);
+        Assert.assertEquals(attr.getValues().get(1).getValue(), BYTE_ARRAY_2);
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2ScopedStringAttributeTranscoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2ScopedStringAttributeTranscoderTest.java
new file mode 100644
index 0000000..05d1b7c
--- /dev/null
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2ScopedStringAttributeTranscoderTest.java
@@ -0,0 +1,394 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.ByteAttributeValue;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.IdPRequestedAttribute;
+import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+import org.opensaml.core.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.XMLObjectBuilder;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.AttributeValue;
+import org.opensaml.saml.saml2.metadata.RequestedAttribute;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/** {@link SAML2ScopedStringAttributeTranscoder} unit test. */
+public class SAML2ScopedStringAttributeTranscoderTest extends OpenSAMLInitBaseTestCase {
+
+    private AttributeTranscoderRegistryImpl registry;
+    
+    private XMLObjectBuilder<XSString> stringBuilder;
+
+    private SAMLObjectBuilder<Attribute> attributeBuilder;
+
+    private SAMLObjectBuilder<RequestedAttribute> reqAttributeBuilder;
+
+    private final static String ATTR_NAME = "foo";
+    private final static String ATTR_NAMEFORMAT = "Namespace";
+    private final static String ATTR_FRIENDLYNAME = "friendly";
+    private final static String STRING_1 = "Value The First";
+    private final static String STRING_2 = "Second string the value is";
+    private final static String SCOPE_1 = "scope1.example.org";
+    private final static String SCOPE_2 = "scope2";
+    private final static String DELIMITER = "#";
+
+    @BeforeClass public void setUp() throws ComponentInitializationException {
+        
+        stringBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSString>getBuilderOrThrow(XSString.TYPE_NAME);
+        
+        attributeBuilder = (SAMLObjectBuilder<Attribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<Attribute>getBuilderOrThrow(
+                        Attribute.TYPE_NAME);
+        reqAttributeBuilder = (SAMLObjectBuilder<RequestedAttribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<RequestedAttribute>getBuilderOrThrow(
+                        RequestedAttribute.TYPE_NAME);
+        
+        registry = new AttributeTranscoderRegistryImpl();
+        registry.setId("test");
+        
+        final SAML2ScopedStringAttributeTranscoder transcoder = new SAML2ScopedStringAttributeTranscoder();
+        transcoder.initialize();
+        
+        registry.setNamingRegistry(Collections.singletonMap(transcoder.getEncodedType(),
+                new AbstractSAML2AttributeTranscoder.NamingFunction()));
+        
+        final Map<String,Object> ruleset1 = new HashMap<>();
+        ruleset1.put(AttributeTranscoderRegistry.PROP_ID, ATTR_NAME);
+        ruleset1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        ruleset1.put(SAMLAttributeTranscoder.PROP_ENCODE_TYPE, true);
+        ruleset1.put(SAMLAttributeTranscoder.PROP_NAME, ATTR_NAME);
+        ruleset1.put(SAML2AttributeTranscoder.PROP_NAME_FORMAT, ATTR_NAMEFORMAT);
+        ruleset1.put(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME, ATTR_FRIENDLYNAME);
+        ruleset1.put(SAML2ScopedStringAttributeTranscoder.PROP_SCOPE_DELIMITER, DELIMITER);
+        ruleset1.put(SAML2ScopedStringAttributeTranscoder.PROP_SCOPE_TYPE, "inline");
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset1)));
+        
+        registry.initialize();
+    }
+    
+    @AfterClass public void tearDown() {
+        registry.destroy();
+        registry = null;
+    }
+
+    @Test public void emptyEncode() throws Exception {
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getName(), ATTR_NAME);
+        Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+        Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+        Assert.assertTrue(attr.getAttributeValues().isEmpty());
+    }
+
+    @Test public void emptyDecode() throws Exception {
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+
+    @Test public void emptyRequestedDecode() throws Exception {
+        
+        final RequestedAttribute samlAttribute = reqAttributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.setIsRequired(true);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertTrue(attr instanceof IdPRequestedAttribute);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(((IdPRequestedAttribute) attr).getIsRequired());
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+    
+    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
+        final int[] intArray = {1, 2, 3, 4};
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new IdPAttributeValue<Object>() {
+                    @Override
+                    public Object getValue() {
+                        return intArray;
+                    }
+                    @Override
+                    public String getDisplayValue() {
+                        return intArray.toString();
+                    }
+                });
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(null, inputAttribute, Attribute.class, ruleset);
+    }
+    
+    @Test public void single() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}),
+                        new ScopedStringAttributeValue(STRING_1, SCOPE_1),
+                        new StringAttributeValue(STRING_1),
+                        new StringAttributeValue(STRING_1 + "@" + SCOPE_1));
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getName(), ATTR_NAME);
+        Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+        Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+
+        Assert.assertEquals(children.size(), 1, "Encoding one entry");
+
+        final XMLObject child = children.get(0);
+
+        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+
+        Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
+
+        final XSString childAsString = (XSString) child;
+
+        Assert.assertEquals(childAsString.getValue(), STRING_1 + DELIMITER + SCOPE_1);
+    }
+
+    @Test public void singleRequested() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}),
+                        new ScopedStringAttributeValue(STRING_1, SCOPE_1));
+
+        final IdPRequestedAttribute inputAttribute = new IdPRequestedAttribute(ATTR_NAME);
+        inputAttribute.setRequired(true);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+
+        final RequestedAttribute attr = TranscoderSupport.<RequestedAttribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, RequestedAttribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getName(), ATTR_NAME);
+        Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+        Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+        Assert.assertTrue(attr.isRequired());
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+
+        Assert.assertEquals(children.size(), 1, "Encoding one entry");
+
+        final XMLObject child = children.get(0);
+
+        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+
+        Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
+
+        final XSString childAsString = (XSString) child;
+
+        Assert.assertEquals(childAsString.getValue(), STRING_1 + DELIMITER + SCOPE_1);
+    }
+    
+    @Test public void singleDecode() throws Exception {
+                
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue(STRING_1 + DELIMITER + SCOPE_1);
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.getAttributeValues().add(stringValue);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertEquals(attr.getValues().size(), 1);
+        
+        final ScopedStringAttributeValue value = (ScopedStringAttributeValue) attr.getValues().get(0);
+        Assert.assertEquals(value.getValue(), STRING_1);
+        Assert.assertEquals(value.getScope(), SCOPE_1);
+    }
+    
+    
+    @Test public void singleRequestedDecode() throws Exception {
+        
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue(STRING_1 + DELIMITER + SCOPE_1);
+        
+        final RequestedAttribute samlAttribute = reqAttributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.setIsRequired(true);
+        samlAttribute.getAttributeValues().add(stringValue);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertTrue(attr instanceof IdPRequestedAttribute);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(((IdPRequestedAttribute) attr).getIsRequired());
+        Assert.assertEquals(attr.getValues().size(), 1);
+        
+        final ScopedStringAttributeValue value = (ScopedStringAttributeValue) attr.getValues().get(0);
+        Assert.assertEquals(value.getValue(), STRING_1);
+        Assert.assertEquals(value.getScope(), SCOPE_1);
+    }
+    
+    @Test public void multi() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}),
+                        new ScopedStringAttributeValue(STRING_1, SCOPE_1),
+                        new ScopedStringAttributeValue(STRING_2, SCOPE_2));
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+        Assert.assertEquals(children.size(), 2, "Encoding 2 entries");
+
+        final String s1 = STRING_1 + DELIMITER + SCOPE_1;
+        final String s2 = STRING_2 + DELIMITER + SCOPE_2;
+        
+        for (final XMLObject child: children) {
+            Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
+            final String childAsString = ((XSString) children.get(0)).getValue();
+            Assert.assertTrue(s1.equals(childAsString) || s2.equals(childAsString));
+        }
+    }
+
+    @Test public void multiDecode() throws Exception {
+        
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue(STRING_1 + DELIMITER + SCOPE_1);
+
+        final XSString stringValue2 = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue2.setValue(STRING_2 + DELIMITER + SCOPE_2);
+
+        final XSString stringValue3 = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue3.setValue(STRING_2 + "@" + SCOPE_2);
+
+        final XSString stringValue4 = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue4.setValue(STRING_2);
+
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.getAttributeValues().add(stringValue);
+        samlAttribute.getAttributeValues().add(stringValue2);
+        samlAttribute.getAttributeValues().add(stringValue3);
+        samlAttribute.getAttributeValues().add(stringValue4);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertEquals(attr.getValues().size(), 2);
+        
+        final ScopedStringAttributeValue value1 = (ScopedStringAttributeValue) attr.getValues().get(0);
+        final ScopedStringAttributeValue value2 = (ScopedStringAttributeValue) attr.getValues().get(1);
+        Assert.assertTrue(STRING_1.equals(value1.getValue()) || STRING_1.equals(value2.getValue()));
+        Assert.assertTrue(STRING_2.equals(value1.getValue()) || STRING_2.equals(value2.getValue()));
+        Assert.assertTrue(SCOPE_1.equals(value1.getScope()) || SCOPE_1.equals(value2.getScope()));
+        Assert.assertTrue(SCOPE_2.equals(value1.getScope()) || SCOPE_2.equals(value2.getScope()));
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeTranscoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeTranscoderTest.java
new file mode 100644
index 0000000..1182837
--- /dev/null
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeTranscoderTest.java
@@ -0,0 +1,364 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.ByteAttributeValue;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.IdPRequestedAttribute;
+import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+import org.opensaml.core.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.XMLObjectBuilder;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.AttributeValue;
+import org.opensaml.saml.saml2.metadata.RequestedAttribute;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/** {@link SAML2StringAttributeTranscoder} unit test. */
+public class SAML2StringAttributeTranscoderTest extends OpenSAMLInitBaseTestCase {
+
+    private AttributeTranscoderRegistryImpl registry;
+    
+    private XMLObjectBuilder<XSString> stringBuilder;
+
+    private SAMLObjectBuilder<Attribute> attributeBuilder;
+
+    private SAMLObjectBuilder<RequestedAttribute> reqAttributeBuilder;
+
+    private final static String ATTR_NAME = "foo";
+    private final static String ATTR_NAMEFORMAT = "Namespace";
+    private final static String ATTR_FRIENDLYNAME = "friendly";
+    private final static String STRING_1 = "Value The First";
+    private final static String STRING_2 = "Second string the value is";
+
+    @BeforeClass public void setUp() throws ComponentInitializationException {
+        
+        stringBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSString>getBuilderOrThrow(XSString.TYPE_NAME);
+        
+        attributeBuilder = (SAMLObjectBuilder<Attribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<Attribute>getBuilderOrThrow(
+                        Attribute.TYPE_NAME);
+        reqAttributeBuilder = (SAMLObjectBuilder<RequestedAttribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<RequestedAttribute>getBuilderOrThrow(
+                        RequestedAttribute.TYPE_NAME);
+        
+        registry = new AttributeTranscoderRegistryImpl();
+        registry.setId("test");
+
+        final SAML2StringAttributeTranscoder transcoder = new SAML2StringAttributeTranscoder();
+        transcoder.initialize();
+        
+        registry.setNamingRegistry(Collections.singletonMap(transcoder.getEncodedType(),
+                new AbstractSAML2AttributeTranscoder.NamingFunction()));
+        
+        final Map<String,Object> ruleset1 = new HashMap<>();
+        ruleset1.put(AttributeTranscoderRegistry.PROP_ID, ATTR_NAME);
+        ruleset1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        ruleset1.put(SAMLAttributeTranscoder.PROP_ENCODE_TYPE, true);
+        ruleset1.put(SAMLAttributeTranscoder.PROP_NAME, ATTR_NAME);
+        ruleset1.put(SAML2AttributeTranscoder.PROP_NAME_FORMAT, ATTR_NAMEFORMAT);
+        ruleset1.put(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME, ATTR_FRIENDLYNAME);
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset1)));
+        
+        registry.initialize();
+    }
+    
+    @AfterClass public void tearDown() {
+        registry.destroy();
+        registry = null;
+    }
+
+    @Test public void emptyEncode() throws Exception {
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getName(), ATTR_NAME);
+        Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+        Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+        Assert.assertTrue(attr.getAttributeValues().isEmpty());
+    }
+
+    @Test public void emptyDecode() throws Exception {
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+
+    @Test public void emptyRequestedDecode() throws Exception {
+        
+        final RequestedAttribute samlAttribute = reqAttributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.setIsRequired(true);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertTrue(attr instanceof IdPRequestedAttribute);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(((IdPRequestedAttribute) attr).getIsRequired());
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+    
+    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
+        final int[] intArray = {1, 2, 3, 4};
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new IdPAttributeValue<Object>() {
+                    @Override
+                    public Object getValue() {
+                        return intArray;
+                    }
+                    @Override
+                    public String getDisplayValue() {
+                        return intArray.toString();
+                    }
+                });
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(null, inputAttribute, Attribute.class, ruleset);
+    }
+    
+    @Test public void single() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new StringAttributeValue(STRING_1));
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getName(), ATTR_NAME);
+        Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+        Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+
+        Assert.assertEquals(children.size(), 1, "Encoding one entry");
+
+        final XMLObject child = children.get(0);
+
+        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+
+        Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
+
+        final XSString childAsString = (XSString) child;
+
+        Assert.assertEquals(childAsString.getValue(), STRING_1);
+    }
+
+    @Test public void singleRequested() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new StringAttributeValue(STRING_1));
+
+        final IdPRequestedAttribute inputAttribute = new IdPRequestedAttribute(ATTR_NAME);
+        inputAttribute.setRequired(true);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+
+        final RequestedAttribute attr = TranscoderSupport.<RequestedAttribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, RequestedAttribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getName(), ATTR_NAME);
+        Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+        Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+        Assert.assertTrue(attr.isRequired());
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+
+        Assert.assertEquals(children.size(), 1, "Encoding one entry");
+
+        final XMLObject child = children.get(0);
+
+        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+
+        Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
+
+        final XSString childAsString = (XSString) child;
+
+        Assert.assertEquals(childAsString.getValue(), STRING_1);
+    }
+    
+    @Test public void singleDecode() throws Exception {
+                
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue(STRING_1);
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.getAttributeValues().add(stringValue);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertEquals(attr.getValues().size(), 1);
+        Assert.assertEquals(attr.getValues().get(0).getValue().toString(), STRING_1);
+    }
+    
+    
+    @Test public void singleRequestedDecode() throws Exception {
+        
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue(STRING_1);
+        
+        final RequestedAttribute samlAttribute = reqAttributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.setIsRequired(true);
+        samlAttribute.getAttributeValues().add(stringValue);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertTrue(attr instanceof IdPRequestedAttribute);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(((IdPRequestedAttribute) attr).getIsRequired());
+        Assert.assertEquals(attr.getValues().size(), 1);
+        Assert.assertEquals(attr.getValues().get(0).getValue().toString(), STRING_1);
+    }
+    
+    @Test public void multi() throws Exception {
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}),
+                        new StringAttributeValue(STRING_1),
+                        new StringAttributeValue(STRING_2),
+                        new ScopedStringAttributeValue(STRING_1, STRING_2));
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+        Assert.assertEquals(children.size(), 3, "Encoding three entries");
+
+        for (final XMLObject child: children) {
+            Assert.assertTrue(child instanceof XSString, "Child of result attribute should be a string");
+            final String childAsString = ((XSString) children.get(0)).getValue();
+            Assert.assertTrue(STRING_1.equals(childAsString)||STRING_2.equals(childAsString));
+        }
+    }
+
+    @Test public void multiDecode() throws Exception {
+        
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue(STRING_1);
+
+        final XSString stringValue2 = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue2.setValue(STRING_2);
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.getAttributeValues().add(stringValue);
+        samlAttribute.getAttributeValues().add(stringValue2);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertEquals(attr.getValues().size(), 2);
+        Assert.assertEquals(attr.getValues().get(0).getValue().toString(), STRING_1);
+        Assert.assertEquals(attr.getValues().get(1).getValue().toString(), STRING_2);
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2XMLObjectAttributeTranscoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2XMLObjectAttributeTranscoderTest.java
new file mode 100644
index 0000000..92f1e2c
--- /dev/null
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2XMLObjectAttributeTranscoderTest.java
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.saml.attribute.transcoding.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.namespace.QName;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.ByteAttributeValue;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.IdPRequestedAttribute;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.XMLObjectAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLEncoderSupport;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+import org.opensaml.core.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.XMLObjectBuilder;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.AttributeValue;
+import org.opensaml.saml.saml2.metadata.RequestedAttribute;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/** {@link SAML2XMLObjectAttributeTranscoder} unit test. */
+public class SAML2XMLObjectAttributeTranscoderTest extends OpenSAMLInitBaseTestCase {
+
+    private AttributeTranscoderRegistryImpl registry;
+    
+    private XMLObjectBuilder<XSAny> anyBuilder;
+    
+    private XMLObjectBuilder<XSString> stringBuilder;
+
+    private SAMLObjectBuilder<Attribute> attributeBuilder;
+
+    private SAMLObjectBuilder<RequestedAttribute> reqAttributeBuilder;
+
+    private final static String ATTR_NAME = "foo";
+    private final static String ATTR_NAMEFORMAT = "Namespace";
+    private final static String ATTR_FRIENDLYNAME = "friendly";
+    private final static String STRING_1 = "Value The First";
+    private final static String STRING_2 = "Second string the value is";
+
+    @BeforeClass public void setUp() throws ComponentInitializationException {
+        
+        anyBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSAny>getBuilderOrThrow(XSAny.TYPE_NAME);
+        stringBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSString>getBuilderOrThrow(XSString.TYPE_NAME);
+        
+        attributeBuilder = (SAMLObjectBuilder<Attribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<Attribute>getBuilderOrThrow(
+                        Attribute.TYPE_NAME);
+        reqAttributeBuilder = (SAMLObjectBuilder<RequestedAttribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<RequestedAttribute>getBuilderOrThrow(
+                        RequestedAttribute.TYPE_NAME);
+        
+        registry = new AttributeTranscoderRegistryImpl();
+        registry.setId("test");
+
+        final SAML2XMLObjectAttributeTranscoder transcoder = new SAML2XMLObjectAttributeTranscoder();
+        transcoder.initialize();
+        
+        registry.setNamingRegistry(Collections.singletonMap(transcoder.getEncodedType(),
+                new AbstractSAML2AttributeTranscoder.NamingFunction()));
+        
+        final Map<String,Object> ruleset1 = new HashMap<>();
+        ruleset1.put(AttributeTranscoderRegistry.PROP_ID, ATTR_NAME);
+        ruleset1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        ruleset1.put(SAMLAttributeTranscoder.PROP_ENCODE_TYPE, true);
+        ruleset1.put(SAMLAttributeTranscoder.PROP_NAME, ATTR_NAME);
+        ruleset1.put(SAML2AttributeTranscoder.PROP_NAME_FORMAT, ATTR_NAMEFORMAT);
+        ruleset1.put(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME, ATTR_FRIENDLYNAME);
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset1)));
+        
+        registry.initialize();
+    }
+    
+    @AfterClass public void tearDown() {
+        registry.destroy();
+        registry = null;
+    }
+
+    @Test public void emptyEncode() throws Exception {
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getName(), ATTR_NAME);
+        Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+        Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+        Assert.assertTrue(attr.getAttributeValues().isEmpty());
+    }
+
+    @Test public void emptyDecode() throws Exception {
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+
+    @Test public void emptyRequestedDecode() throws Exception {
+        
+        final RequestedAttribute samlAttribute = reqAttributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.setIsRequired(true);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertTrue(attr instanceof IdPRequestedAttribute);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(((IdPRequestedAttribute) attr).getIsRequired());
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+    
+    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
+        final int[] intArray = {1, 2, 3, 4};
+        final Collection<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new IdPAttributeValue<Object>() {
+                    @Override
+                    public Object getValue() {
+                        return intArray;
+                    }
+                    @Override
+                    public String getDisplayValue() {
+                        return intArray.toString();
+                    }
+                });
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(null, inputAttribute, Attribute.class, ruleset);
+    }
+    
+    @Test public void single() throws Exception {
+        final List<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), objectFor(STRING_1));
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getName(), ATTR_NAME);
+        Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+        Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+        Assert.assertEquals(children.size(), 1, "Encoding one entry");
+        Assert.assertEquals(children.get(0).getElementQName(),
+                AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+        Assert.assertEquals(children.get(0).getOrderedChildren().size(), 1,
+                "Expected exactly one child inside the <AttributeValue/>");
+        
+        checkValues(children.get(0).getOrderedChildren().get(0), STRING_1);
+    }
+
+    @Test public void singleRequested() throws Exception {
+        final List<IdPAttributeValue<?>> values =
+                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), objectFor(STRING_1));
+
+        final IdPRequestedAttribute inputAttribute = new IdPRequestedAttribute(ATTR_NAME);
+        inputAttribute.setRequired(true);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+
+        final RequestedAttribute attr = TranscoderSupport.<RequestedAttribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, RequestedAttribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getName(), ATTR_NAME);
+        Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+        Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+        Assert.assertTrue(attr.isRequired());
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+        Assert.assertEquals(children.size(), 1, "Encoding one entry");
+        Assert.assertEquals(children.get(0).getElementQName(),
+                AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+        Assert.assertEquals(children.get(0).getOrderedChildren().size(), 1,
+                "Expected exactly one child inside the <AttributeValue/>");
+        
+        checkValues(children.get(0).getOrderedChildren().get(0), STRING_1);
+    }
+    
+    @Test public void singleDecode() throws Exception {
+                
+        final XSString stringValue = stringBuilder.buildObject(new QName("Foo"));
+        stringValue.setValue(STRING_1);
+        
+        final XSAny attrValue = anyBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        attrValue.getUnknownXMLObjects().add(stringValue);
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.getAttributeValues().add(attrValue);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertEquals(attr.getValues().size(), 1);
+        
+        final XMLObjectAttributeValue value = (XMLObjectAttributeValue) attr.getValues().get(0);
+        Assert.assertTrue(value.getValue() instanceof XSString);
+        Assert.assertEquals(value.getValue().getElementQName().getLocalPart(), "Foo");
+        Assert.assertEquals(((XSString) value.getValue()).getValue(), STRING_1);
+    }
+    
+    
+    @Test public void singleRequestedDecode() throws Exception {
+        
+        final XSString stringValue = stringBuilder.buildObject(new QName("Foo"));
+        stringValue.setValue(STRING_1);
+        
+        final XSAny attrValue = anyBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        attrValue.getUnknownXMLObjects().add(stringValue);
+        
+        final RequestedAttribute samlAttribute = reqAttributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.setIsRequired(true);
+        samlAttribute.getAttributeValues().add(attrValue);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertTrue(attr instanceof IdPRequestedAttribute);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(((IdPRequestedAttribute) attr).getIsRequired());
+        Assert.assertEquals(attr.getValues().size(), 1);
+
+        final XMLObjectAttributeValue value = (XMLObjectAttributeValue) attr.getValues().get(0);
+        Assert.assertTrue(value.getValue() instanceof XSString);
+        Assert.assertEquals(((XSString) value.getValue()).getValue(), STRING_1);
+    }
+    
+    @Test public void multi() throws Exception {
+        final List<IdPAttributeValue<?>> values =
+                Arrays.asList(objectFor(STRING_1), objectFor(STRING_2));
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+        Assert.assertEquals(children.size(), 2, "Encoding two entries");
+
+        Assert.assertEquals(children.get(0).getElementQName(),
+                AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+        Assert.assertEquals(children.get(0).getOrderedChildren().size(), 1,
+                "Expected exactly one child inside the <AttributeValue/> for first Attribute");
+
+        Assert.assertEquals(children.get(1).getElementQName(),
+                AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+        Assert.assertEquals(children.get(1).getOrderedChildren().size(), 1,
+                "Expected exactly one child inside the <AttributeValue/> for second Attribute");
+
+        checkValues(children.get(0).getOrderedChildren().get(0), STRING_1, STRING_2);
+        checkValues(children.get(1).getOrderedChildren().get(0), STRING_1, STRING_2);
+    }
+
+    @Test public void multiDecode() throws Exception {
+
+        final XSString stringValue = stringBuilder.buildObject(new QName("Foo"));
+        stringValue.setValue(STRING_1);
+        
+        final XSAny attrValue = anyBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        attrValue.getUnknownXMLObjects().add(stringValue);
+
+        final XSString stringValue2 = stringBuilder.buildObject(new QName("Bar"));
+        stringValue2.setValue(STRING_2);
+        
+        final XSAny attrValue2 = anyBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        attrValue2.getUnknownXMLObjects().add(stringValue2);
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.getAttributeValues().add(attrValue);
+        samlAttribute.getAttributeValues().add(attrValue2);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertEquals(attr.getValues().size(), 2);
+        
+        final XMLObjectAttributeValue value = (XMLObjectAttributeValue) attr.getValues().get(0);
+        Assert.assertTrue(value.getValue() instanceof XSString);
+
+        final XMLObjectAttributeValue value2 = (XMLObjectAttributeValue) attr.getValues().get(1);
+        Assert.assertTrue(value2.getValue() instanceof XSString);
+
+        final String s1 = ((XSString) value.getValue()).getValue();
+        final String s2 = ((XSString) value2.getValue()).getValue();
+        
+        Assert.assertTrue(STRING_1.equals(s1) || STRING_2.equals(s1));
+        Assert.assertTrue(STRING_1.equals(s2) || STRING_2.equals(s2));
+    }
+
+    /**
+     * Create an XML object from a string which we can test against later.
+     * 
+     * @param value that we encode
+     * @return an XML object
+     */
+    private static XMLObjectAttributeValue objectFor(final String value) {
+        
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(Collections.singletonList(new StringAttributeValue(value)));
+        return new XMLObjectAttributeValue(
+                SAMLEncoderSupport.encodeStringValue(inputAttribute, new QName("Foo"), value, true));
+    }
+
+    /**
+     * Check that the input XML object is what was expected.
+     * 
+     * @param input the objects in question.
+     * @param possibles the strings that they might be encoding.
+     */
+    private static void checkValues(final XMLObject input, final String... possibles) {
+
+        Assert.assertTrue(input instanceof XSString);
+        final String s = ((XSString) input).getValue();
+
+        for (String possible : possibles) {
+            if (s.equals(possible)) {
+                return;
+            }
+        }
+        Assert.assertTrue(false, "No potential match");
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml1/profile/impl/AddAttributeStatementToAssertionTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml1/profile/impl/AddAttributeStatementToAssertionTest.java
index c7685f8..a0a4660 100644
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml1/profile/impl/AddAttributeStatementToAssertionTest.java
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml1/profile/impl/AddAttributeStatementToAssertionTest.java
@@ -18,8 +18,9 @@
 package net.shibboleth.idp.saml.saml1.profile.impl;
 
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -28,14 +29,22 @@ import net.shibboleth.idp.attribute.AttributeEncodingException;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.StringAttributeValue;
 import net.shibboleth.idp.attribute.context.AttributeContext;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
 import net.shibboleth.idp.profile.ActionTestingSupport;
 import net.shibboleth.idp.profile.IdPEventIds;
 import net.shibboleth.idp.profile.RequestContextBuilder;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML1StringAttributeEncoder;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML1AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1StringAttributeTranscoder;
 import net.shibboleth.idp.saml.saml1.profile.SAML1ActionTestingSupport;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.service.AbstractReloadableService;
+import net.shibboleth.utilities.java.support.service.ServiceableComponent;
 
 import org.opensaml.core.OpenSAMLInitBaseTestCase;
 import org.opensaml.core.xml.XMLObject;
@@ -44,6 +53,7 @@ import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.saml.saml1.core.Assertion;
 import org.opensaml.saml.saml1.core.Attribute;
+import org.opensaml.saml.saml1.core.AttributeDesignator;
 import org.opensaml.saml.saml1.core.AttributeStatement;
 import org.opensaml.saml.saml1.core.Response;
 import org.springframework.webflow.execution.Event;
@@ -79,12 +89,49 @@ public class AddAttributeStatementToAssertionTest extends OpenSAMLInitBaseTestCa
     
     private AddAttributeStatementToAssertion action;
     
+    private AttributeTranscoderRegistryImpl registry;
+    
     @BeforeMethod public void setUp() throws ComponentInitializationException {
         rc = new RequestContextBuilder().setOutboundMessage(
                 SAML1ActionTestingSupport.buildResponse()).buildRequestContext();
         prc = new WebflowRequestContextProfileRequestContextLookup().apply(rc);
         
+        registry = new AttributeTranscoderRegistryImpl();
+        registry.setId("test");
+        
+        registry.setNamingRegistry(Collections.singletonMap(AttributeDesignator.class,
+                new AbstractSAML1AttributeTranscoder.NamingFunction()));
+
+        final SAML1StringAttributeTranscoder transcoder = new SAML1StringAttributeTranscoder();
+        transcoder.initialize();
+        
+        final Map<String,Object> rule1_1 = new HashMap<>();
+        rule1_1.put(AttributeTranscoderRegistry.PROP_ID, MY_NAME_1);
+        rule1_1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        rule1_1.put(SAMLAttributeTranscoder.PROP_NAME, MY_NAME_1);
+        rule1_1.put(SAML1AttributeTranscoder.PROP_NAMESPACE, MY_NAMESPACE);
+
+        final Map<String,Object> rule1_2 = new HashMap<>();
+        rule1_2.put(AttributeTranscoderRegistry.PROP_ID, MY_NAME_1);
+        rule1_2.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        rule1_2.put(SAMLAttributeTranscoder.PROP_NAME, MY_ALTNAME_1);
+        rule1_2.put(SAML1AttributeTranscoder.PROP_NAMESPACE, MY_NAMESPACE);
+
+        final Map<String,Object> rule2_1 = new HashMap<>();
+        rule2_1.put(AttributeTranscoderRegistry.PROP_ID, MY_NAME_2);
+        rule2_1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        rule2_1.put(SAMLAttributeTranscoder.PROP_NAME, MY_NAME_2);
+        rule2_1.put(SAML1AttributeTranscoder.PROP_NAMESPACE, MY_NAMESPACE);
+
+        registry.setTranscoderRegistry(Arrays.asList(
+                new TranscodingRule(rule1_1),
+                new TranscodingRule(rule1_2),
+                new TranscodingRule(rule2_1)));
+        
+        registry.initialize();
+        
         action = new AddAttributeStatementToAssertion();
+        action.setTranscoderRegistry(new RegistryService(registry));
     }
 
     /** Test that the action errors out properly if there is no relying party context. */
@@ -127,14 +174,30 @@ public class AddAttributeStatementToAssertionTest extends OpenSAMLInitBaseTestCa
 
     /** Test that the action ignores attribute encoding errors. */
     @Test public void testIgnoreAttributeEncodingErrors() throws Exception {
-        final MockSAML1StringAttributeEncoder attributeEncoder = new MockSAML1StringAttributeEncoder();
+
+        final AttributeTranscoderRegistryImpl localregistry = new AttributeTranscoderRegistryImpl();
+        localregistry.setId("test");
+        
+        localregistry.setNamingRegistry(Collections.singletonMap(AttributeDesignator.class,
+                new AbstractSAML1AttributeTranscoder.NamingFunction()));
+        
+        final MockSAML1StringAttributeTranscoder transcoder = new MockSAML1StringAttributeTranscoder();
+        transcoder.initialize();
+        
+        final Map<String,Object> rule = new HashMap<>();
+        rule.put(AttributeTranscoderRegistry.PROP_ID, MY_NAME_1);
+        rule.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        rule.put(SAMLAttributeTranscoder.PROP_NAME, MY_NAME_1);
+        rule.put(SAML1AttributeTranscoder.PROP_NAMESPACE, MY_NAMESPACE);
+        
+        localregistry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(rule)));
+        localregistry.initialize();
+        
+        action.setTranscoderRegistry(new RegistryService(localregistry));
 
         final IdPAttribute attribute = new IdPAttribute(MY_NAME_1);
         attribute.setValues(Arrays.asList(new StringAttributeValue(MY_VALUE_1)));
 
-        final Collection collection = Arrays.asList(attributeEncoder);
-        attribute.setEncoders(collection);
-
         final AttributeContext attribCtx = new AttributeContext();
         attribCtx.setIdPAttributes(Arrays.asList(attribute));
         prc.getSubcontext(RelyingPartyContext.class).addSubcontext(attribCtx);
@@ -147,14 +210,30 @@ public class AddAttributeStatementToAssertionTest extends OpenSAMLInitBaseTestCa
 
     /** Test that the action returns the correct transition when an attribute encoding error occurs. */
     @Test public void failOnAttributeEncodingErrors() throws Exception {
-        final MockSAML1StringAttributeEncoder attributeEncoder = new MockSAML1StringAttributeEncoder();
+        
+        final AttributeTranscoderRegistryImpl localregistry = new AttributeTranscoderRegistryImpl();
+        localregistry.setId("test");
+        
+        localregistry.setNamingRegistry(Collections.singletonMap(AttributeDesignator.class,
+                new AbstractSAML1AttributeTranscoder.NamingFunction()));
+
+        final MockSAML1StringAttributeTranscoder transcoder = new MockSAML1StringAttributeTranscoder();
+        transcoder.initialize();
+
+        final Map<String,Object> rule = new HashMap<>();
+        rule.put(AttributeTranscoderRegistry.PROP_ID, MY_NAME_1);
+        rule.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        rule.put(SAMLAttributeTranscoder.PROP_NAME, MY_NAME_1);
+        rule.put(SAML1AttributeTranscoder.PROP_NAMESPACE, MY_NAMESPACE);
+        
+        localregistry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(rule)));
+        localregistry.initialize();
+        
+        action.setTranscoderRegistry(new RegistryService(localregistry));
 
         final IdPAttribute attribute = new IdPAttribute(MY_NAME_1);
         attribute.setValues(Arrays.asList(new StringAttributeValue(MY_VALUE_1)));
 
-        final Collection collection = Arrays.asList(attributeEncoder);
-        attribute.setEncoders(collection);
-
         final AttributeContext attribCtx = new AttributeContext();
         attribCtx.setIdPAttributes(Arrays.asList(attribute));
         prc.getSubcontext(RelyingPartyContext.class).addSubcontext(attribCtx);
@@ -254,30 +333,9 @@ public class AddAttributeStatementToAssertionTest extends OpenSAMLInitBaseTestCa
         final IdPAttribute attribute1 = new IdPAttribute(MY_NAME_1);
         attribute1.setValues(Arrays.asList(new StringAttributeValue(MY_VALUE_1)));
 
-        final SAML1StringAttributeEncoder attributeEncoder1 = new SAML1StringAttributeEncoder();
-        attributeEncoder1.setName(MY_NAME_1);
-        attributeEncoder1.setNamespace(MY_NAMESPACE);
-        attributeEncoder1.initialize();
-
-        final SAML1StringAttributeEncoder attributeEncoder1_2 = new SAML1StringAttributeEncoder();
-        attributeEncoder1_2.setName(MY_ALTNAME_1);
-        attributeEncoder1_2.setNamespace(MY_NAMESPACE);
-        attributeEncoder1_2.initialize();
-
-        final Collection collection1 = Arrays.asList(attributeEncoder1, attributeEncoder1_2);
-        attribute1.setEncoders(collection1);
-
         final IdPAttribute attribute2 = new IdPAttribute(MY_NAME_2);
         attribute2.setValues(Collections.singletonList(new StringAttributeValue(MY_VALUE_2)));
 
-        final SAML1StringAttributeEncoder attributeEncoder2 = new SAML1StringAttributeEncoder();
-        attributeEncoder2.setName(MY_NAME_2);
-        attributeEncoder2.setNamespace(MY_NAMESPACE);
-        attributeEncoder2.initialize();
-
-        final Collection collection2 = Arrays.asList(attributeEncoder2);
-        attribute2.setEncoders(collection2);
-
         final AttributeContext attribCtx = new AttributeContext();
         attribCtx.setIdPAttributes(Arrays.asList(attribute1, attribute2));
 
@@ -321,12 +379,40 @@ public class AddAttributeStatementToAssertionTest extends OpenSAMLInitBaseTestCa
     }
 
     /** A mock SAML1 string attribute encoder which always throws an {@link AttributeEncodingException}. */
-    private class MockSAML1StringAttributeEncoder extends SAML1StringAttributeEncoder {
+    private class MockSAML1StringAttributeTranscoder extends SAML1StringAttributeTranscoder {
 
         /** {@inheritDoc} */
-        @Nullable public Attribute encode(@Nonnull final IdPAttribute attribute) throws AttributeEncodingException {
+        @Override
+        @Nullable public Attribute encode(@Nullable final ProfileRequestContext profileRequestContext,
+                @Nonnull final IdPAttribute attribute, @Nonnull final Class<? extends AttributeDesignator> to,
+                @Nonnull final TranscodingRule rule) throws AttributeEncodingException {
             throw new AttributeEncodingException("Always thrown.");
         }
     }
 
+    private static class RegistryService extends AbstractReloadableService<AttributeTranscoderRegistry> {
+
+        private ServiceableComponent<AttributeTranscoderRegistry> component;
+
+        protected RegistryService(ServiceableComponent<AttributeTranscoderRegistry> what) {
+            component = what;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        @Nullable public ServiceableComponent<AttributeTranscoderRegistry> getServiceableComponent() {
+            if (null == component) {
+                return null;
+            }
+            component.pinComponent();
+            return component;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        protected boolean shouldReload() {
+            return false;
+        }
+    }
+
 }
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml1/profile/impl/FilterByQueriedAttributeDesignatorsTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml1/profile/impl/FilterByQueriedAttributeDesignatorsTest.java
index 9c56761..4d6bbb0 100644
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml1/profile/impl/FilterByQueriedAttributeDesignatorsTest.java
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml1/profile/impl/FilterByQueriedAttributeDesignatorsTest.java
@@ -22,11 +22,12 @@ import static org.testng.Assert.assertEquals;
 import java.util.Collection;
 import java.util.List;
 
+import javax.annotation.Nullable;
+
 import org.opensaml.core.xml.XMLObjectBaseTestCase;
 import org.opensaml.core.xml.io.UnmarshallingException;
 import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.saml.saml1.core.AttributeDesignator;
-import org.opensaml.saml.saml1.core.AttributeQuery;
+import org.opensaml.saml.saml1.core.Request;
 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
 import org.springframework.context.support.GenericApplicationContext;
 import org.springframework.webflow.execution.Event;
@@ -41,13 +42,16 @@ import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
 import net.shibboleth.idp.attribute.StringAttributeValue;
 import net.shibboleth.idp.attribute.context.AttributeContext;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
 import net.shibboleth.idp.profile.ActionTestingSupport;
 import net.shibboleth.idp.profile.RequestContextBuilder;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
-import net.shibboleth.idp.saml.attribute.mapping.AttributesMapper;
-import net.shibboleth.idp.saml.attribute.mapping.impl.SAML1AttributeDesignatorsMapper;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.service.AbstractReloadableService;
+import net.shibboleth.utilities.java.support.service.ReloadableService;
+import net.shibboleth.utilities.java.support.service.ServiceableComponent;
 import net.shibboleth.utilities.java.support.xml.XMLParserException;
 
 /** Tests for {@link FilterByQueriedAttributeDesignators} */
@@ -55,9 +59,9 @@ public class FilterByQueriedAttributeDesignatorsTest extends XMLObjectBaseTestCa
 
     static final String PATH = "/net/shibboleth/idp/saml/impl/profile/";
     
-    private AttributeQuery query;
+    private Request query;
     
-    private AttributesMapper<AttributeDesignator, IdPAttribute> mapper;
+    private ReloadableService<AttributeTranscoderRegistry> registry;
     
     private FilterByQueriedAttributeDesignators action;
     
@@ -86,16 +90,19 @@ public class FilterByQueriedAttributeDesignatorsTest extends XMLObjectBaseTestCa
         }
     }
         
-    @BeforeClass public void setup() throws XMLParserException, UnmarshallingException {
-        query = unmarshallElement(PATH + "AttributeQuerySaml1.xml", true);        
-        mapper = getBean(PATH + "saml1Mapper.xml", SAML1AttributeDesignatorsMapper.class);
+    @BeforeClass public void setup() {
+        registry = new RegistryService(getBean(PATH + "saml1Mapper.xml", AttributeTranscoderRegistryImpl.class));
     }
     
-    @BeforeMethod public void setUpMethod() throws ComponentInitializationException {
-        action = new FilterByQueriedAttributeDesignators(mapper);
+    @BeforeMethod public void setUpMethod() throws ComponentInitializationException, XMLParserException, UnmarshallingException {
+        query = unmarshallElement(PATH + "AttributeQuerySaml1.xml", true);
+
+        action = new FilterByQueriedAttributeDesignators();
+        action.setTranscoderRegistry(registry);
+        action.initialize();
+        
         rc = new RequestContextBuilder().setInboundMessage(query).buildRequestContext();
         prc = new WebflowRequestContextProfileRequestContextLookup().apply(rc);
-        action.initialize();
     }
 
     @Test public void noAttributes() {
@@ -107,13 +114,18 @@ public class FilterByQueriedAttributeDesignatorsTest extends XMLObjectBaseTestCa
     @Test public void noValues() {
         final RelyingPartyContext rpc = prc.getSubcontext(RelyingPartyContext.class,true);
         final AttributeContext ac = rpc.getSubcontext(AttributeContext.class,true);
-        final List<IdPAttribute> attributes = List.of(new IdPAttribute("eduPersonAssurance"), new IdPAttribute("flooby"), new IdPAttribute("eduPersonScopedAffiliation"),  new IdPAttribute("eduPersonTargetedID"));
+        final List<IdPAttribute> attributes = List.of(
+                new IdPAttribute("eduPersonAssurance"),
+                new IdPAttribute("flooby"),
+                new IdPAttribute("eduPersonScopedAffiliation"), 
+                new IdPAttribute("eduPersonTargetedID"));
         ac.setIdPAttributes(attributes);
         final Event event = action.execute(rc);
         ActionTestingSupport.assertProceedEvent(event);
-        assertEquals(ac.getIdPAttributes().size(), 4);
+        assertEquals(ac.getIdPAttributes().size(), 3);
     }
-    
+
+    /** This is a non-issue for SAML 1, but just for completeness. */
     @Test public void values() {
         final RelyingPartyContext rpc = prc.getSubcontext(RelyingPartyContext.class,true);
         final AttributeContext ac = rpc.getSubcontext(AttributeContext.class,true);
@@ -128,7 +140,32 @@ public class FilterByQueriedAttributeDesignatorsTest extends XMLObjectBaseTestCa
         ac.setIdPAttributes(attributes);
         final Event event = action.execute(rc);
         ActionTestingSupport.assertProceedEvent(event);
-        assertEquals(ac.getIdPAttributes().size(), 4);
+        assertEquals(ac.getIdPAttributes().size(), 3);
+    }
+
+    private static class RegistryService extends AbstractReloadableService<AttributeTranscoderRegistry> {
+
+        private ServiceableComponent<AttributeTranscoderRegistry> component;
+
+        protected RegistryService(ServiceableComponent<AttributeTranscoderRegistry> what) {
+            component = what;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        @Nullable public ServiceableComponent<AttributeTranscoderRegistry> getServiceableComponent() {
+            if (null == component) {
+                return null;
+            }
+            component.pinComponent();
+            return component;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        protected boolean shouldReload() {
+            return false;
+        }
     }
 
-}
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAttributeStatementToAssertionTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAttributeStatementToAssertionTest.java
index 6279116..762877a 100644
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAttributeStatementToAssertionTest.java
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAttributeStatementToAssertionTest.java
@@ -18,8 +18,9 @@
 package net.shibboleth.idp.saml.saml2.profile.impl;
 
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -28,14 +29,22 @@ import net.shibboleth.idp.attribute.AttributeEncodingException;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.StringAttributeValue;
 import net.shibboleth.idp.attribute.context.AttributeContext;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
 import net.shibboleth.idp.profile.ActionTestingSupport;
 import net.shibboleth.idp.profile.IdPEventIds;
 import net.shibboleth.idp.profile.RequestContextBuilder;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
-import net.shibboleth.idp.saml.attribute.encoding.impl.SAML2StringAttributeEncoder;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLAttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2StringAttributeTranscoder;
 import net.shibboleth.idp.saml.saml2.profile.SAML2ActionTestingSupport;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.service.AbstractReloadableService;
+import net.shibboleth.utilities.java.support.service.ServiceableComponent;
 
 import org.opensaml.core.OpenSAMLInitBaseTestCase;
 import org.opensaml.core.xml.XMLObject;
@@ -79,12 +88,48 @@ public class AddAttributeStatementToAssertionTest extends OpenSAMLInitBaseTestCa
     
     private AddAttributeStatementToAssertion action;
     
+    private AttributeTranscoderRegistryImpl registry;
+    
     @BeforeMethod public void setUp() throws ComponentInitializationException {
         rc = new RequestContextBuilder().setOutboundMessage(
                 SAML2ActionTestingSupport.buildResponse()).buildRequestContext();
         prc = new WebflowRequestContextProfileRequestContextLookup().apply(rc);
         
+        registry = new AttributeTranscoderRegistryImpl();
+        registry.setId("test");
+        
+        registry.setNamingRegistry(Collections.singletonMap(Attribute.class,
+                new AbstractSAML2AttributeTranscoder.NamingFunction()));
+
+        final SAML2StringAttributeTranscoder transcoder = new SAML2StringAttributeTranscoder();
+        transcoder.initialize();
+        
+        final Map<String,Object> rule1_1 = new HashMap<>();
+        rule1_1.put(AttributeTranscoderRegistry.PROP_ID, MY_NAME_1);
+        rule1_1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        rule1_1.put(SAMLAttributeTranscoder.PROP_NAME, MY_NAME_1);
+        rule1_1.put(SAML2AttributeTranscoder.PROP_NAME_FORMAT, MY_NAMESPACE);
+
+        final Map<String,Object> rule1_2 = new HashMap<>();
+        rule1_2.put(AttributeTranscoderRegistry.PROP_ID, MY_NAME_1);
+        rule1_2.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        rule1_2.put(SAMLAttributeTranscoder.PROP_NAME, MY_ALTNAME_1);
+        rule1_2.put(SAML2AttributeTranscoder.PROP_NAME_FORMAT, MY_NAMESPACE);
+
+        final Map<String,Object> rule2_1 = new HashMap<>();
+        rule2_1.put(AttributeTranscoderRegistry.PROP_ID, MY_NAME_2);
+        rule2_1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        rule2_1.put(SAMLAttributeTranscoder.PROP_NAME, MY_NAME_2);
+        rule2_1.put(SAML2AttributeTranscoder.PROP_NAME_FORMAT, MY_NAMESPACE);
+
+        registry.setTranscoderRegistry(Arrays.asList(
+                new TranscodingRule(rule1_1),
+                new TranscodingRule(rule1_2),
+                new TranscodingRule(rule2_1)));
+        registry.initialize();
+        
         action = new AddAttributeStatementToAssertion();
+        action.setTranscoderRegistry(new RegistryService(registry));
     }
 
     /** Test that the action errors out properly if there is no relying party context. */
@@ -127,14 +172,30 @@ public class AddAttributeStatementToAssertionTest extends OpenSAMLInitBaseTestCa
 
     /** Test that the action ignores attribute encoding errors. */
     @Test public void testIgnoreAttributeEncodingErrors() throws Exception {
-        final MockSAML2StringAttributeEncoder attributeEncoder = new MockSAML2StringAttributeEncoder();
+        
+        final AttributeTranscoderRegistryImpl localregistry = new AttributeTranscoderRegistryImpl();
+        localregistry.setId("test");
+        
+        localregistry.setNamingRegistry(Collections.singletonMap(Attribute.class,
+                new AbstractSAML2AttributeTranscoder.NamingFunction()));
+        
+        final MockSAML2StringAttributeTranscoder transcoder = new MockSAML2StringAttributeTranscoder();
+        transcoder.initialize();
+
+        final Map<String,Object> rule = new HashMap<>();
+        rule.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        rule.put(AttributeTranscoderRegistry.PROP_ID, MY_NAME_1);
+        rule.put(SAMLAttributeTranscoder.PROP_NAME, MY_NAME_1);
+        rule.put(SAML2AttributeTranscoder.PROP_NAME_FORMAT, MY_NAMESPACE);
+        
+        localregistry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(rule)));
+        localregistry.initialize();
+        
+        action.setTranscoderRegistry(new RegistryService(localregistry));
 
         final IdPAttribute attribute = new IdPAttribute(MY_NAME_1);
         attribute.setValues(Arrays.asList(new StringAttributeValue(MY_VALUE_1)));
 
-        final Collection collection = Arrays.asList(attributeEncoder);
-        attribute.setEncoders(collection);
-
         final AttributeContext attribCtx = new AttributeContext();
         attribCtx.setIdPAttributes(Arrays.asList(attribute));
         ((RelyingPartyContext) prc.getSubcontext(RelyingPartyContext.class)).addSubcontext(attribCtx);
@@ -147,14 +208,30 @@ public class AddAttributeStatementToAssertionTest extends OpenSAMLInitBaseTestCa
 
     /** Test that the action returns the correct transition when an attribute encoding error occurs. */
     @Test public void failOnAttributeEncodingErrors() throws Exception {
-        final MockSAML2StringAttributeEncoder attributeEncoder = new MockSAML2StringAttributeEncoder();
+        
+        final AttributeTranscoderRegistryImpl localregistry = new AttributeTranscoderRegistryImpl();
+        localregistry.setId("test");
+        
+        localregistry.setNamingRegistry(Collections.singletonMap(Attribute.class,
+                new AbstractSAML2AttributeTranscoder.NamingFunction()));
+
+        final MockSAML2StringAttributeTranscoder transcoder = new MockSAML2StringAttributeTranscoder();
+        transcoder.initialize();
+
+        final Map<String,Object> rule = new HashMap<>();
+        rule.put(AttributeTranscoderRegistry.PROP_ID, MY_NAME_1);
+        rule.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        rule.put(SAMLAttributeTranscoder.PROP_NAME, MY_NAME_1);
+        rule.put(SAML2AttributeTranscoder.PROP_NAME_FORMAT, MY_NAMESPACE);
+        
+        localregistry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(rule)));
+        localregistry.initialize();
+        
+        action.setTranscoderRegistry(new RegistryService(localregistry));
 
         final IdPAttribute attribute = new IdPAttribute(MY_NAME_1);
         attribute.setValues(Arrays.asList(new StringAttributeValue(MY_VALUE_1)));
 
-        final Collection collection = Arrays.asList(attributeEncoder);
-        attribute.setEncoders(collection);
-
         final AttributeContext attribCtx = new AttributeContext();
         attribCtx.setIdPAttributes(Arrays.asList(attribute));
         ((RelyingPartyContext) prc.getSubcontext(RelyingPartyContext.class)).addSubcontext(attribCtx);
@@ -254,30 +331,9 @@ public class AddAttributeStatementToAssertionTest extends OpenSAMLInitBaseTestCa
         final IdPAttribute attribute1 = new IdPAttribute(MY_NAME_1);
         attribute1.setValues(Arrays.asList(new StringAttributeValue(MY_VALUE_1)));
 
-        final SAML2StringAttributeEncoder attributeEncoder1 = new SAML2StringAttributeEncoder();
-        attributeEncoder1.setName(MY_NAME_1);
-        attributeEncoder1.setNameFormat(MY_NAMESPACE);
-        attributeEncoder1.initialize();
-
-        final SAML2StringAttributeEncoder attributeEncoder1_2 = new SAML2StringAttributeEncoder();
-        attributeEncoder1_2.setName(MY_ALTNAME_1);
-        attributeEncoder1_2.setNameFormat(MY_NAMESPACE);
-        attributeEncoder1_2.initialize();
-
-        final Collection collection1 = Arrays.asList(attributeEncoder1, attributeEncoder1_2);
-        attribute1.setEncoders(collection1);
-
         final IdPAttribute attribute2 = new IdPAttribute(MY_NAME_2);
         attribute2.setValues(Collections.singletonList(new StringAttributeValue(MY_VALUE_2)));
 
-        final SAML2StringAttributeEncoder attributeEncoder2 = new SAML2StringAttributeEncoder();
-        attributeEncoder2.setName(MY_NAME_2);
-        attributeEncoder2.setNameFormat(MY_NAMESPACE);
-        attributeEncoder2.initialize();
-
-        final Collection collection2 = Arrays.asList(attributeEncoder2);
-        attribute2.setEncoders(collection2);
-
         final AttributeContext attribCtx = new AttributeContext();
         attribCtx.setIdPAttributes(Arrays.asList(attribute1, attribute2));
 
@@ -321,13 +377,41 @@ public class AddAttributeStatementToAssertionTest extends OpenSAMLInitBaseTestCa
         }
     }
 
-    /** A mock SAML2 string attribute encoder which always throws an {@link AttributeEncodingException}. */
-    private class MockSAML2StringAttributeEncoder extends SAML2StringAttributeEncoder {
+    /** A mock SAML2 string attribute transcoder which always throws an {@link AttributeEncodingException}. */
+    private class MockSAML2StringAttributeTranscoder extends SAML2StringAttributeTranscoder {
 
         /** {@inheritDoc} */
-        @Nullable public Attribute encode(@Nonnull final IdPAttribute attribute) throws AttributeEncodingException {
+        @Override
+        @Nullable public Attribute encode(@Nullable final ProfileRequestContext profileRequestContext,
+                @Nonnull final IdPAttribute attribute, @Nonnull final Class<? extends Attribute> to,
+                @Nonnull final TranscodingRule rule) throws AttributeEncodingException {
             throw new AttributeEncodingException("Always thrown.");
         }
     }
 
+    private static class RegistryService extends AbstractReloadableService<AttributeTranscoderRegistry> {
+
+        private ServiceableComponent<AttributeTranscoderRegistry> component;
+
+        protected RegistryService(ServiceableComponent<AttributeTranscoderRegistry> what) {
+            component = what;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        @Nullable public ServiceableComponent<AttributeTranscoderRegistry> getServiceableComponent() {
+            if (null == component) {
+                return null;
+            }
+            component.pinComponent();
+            return component;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        protected boolean shouldReload() {
+            return false;
+        }
+    }
+
 }
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/FilterByQueriedAttributesTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/FilterByQueriedAttributesTest.java
index 4c6d0c5..cc6738c 100644
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/FilterByQueriedAttributesTest.java
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/FilterByQueriedAttributesTest.java
@@ -22,10 +22,11 @@ import static org.testng.Assert.assertEquals;
 import java.util.Collection;
 import java.util.List;
 
+import javax.annotation.Nullable;
+
 import org.opensaml.core.xml.XMLObjectBaseTestCase;
 import org.opensaml.core.xml.io.UnmarshallingException;
 import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.saml.saml2.core.Attribute;
 import org.opensaml.saml.saml2.core.AttributeQuery;
 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
 import org.springframework.context.support.GenericApplicationContext;
@@ -41,13 +42,16 @@ import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
 import net.shibboleth.idp.attribute.StringAttributeValue;
 import net.shibboleth.idp.attribute.context.AttributeContext;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
 import net.shibboleth.idp.profile.ActionTestingSupport;
 import net.shibboleth.idp.profile.RequestContextBuilder;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
-import net.shibboleth.idp.saml.attribute.mapping.AttributesMapper;
-import net.shibboleth.idp.saml.attribute.mapping.impl.SAML2AttributesMapper;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.service.AbstractReloadableService;
+import net.shibboleth.utilities.java.support.service.ReloadableService;
+import net.shibboleth.utilities.java.support.service.ServiceableComponent;
 import net.shibboleth.utilities.java.support.xml.XMLParserException;
 
 /** Tests for {@link FilterByQueriedAttributes} */
@@ -57,7 +61,7 @@ public class FilterByQueriedAttributesTest extends XMLObjectBaseTestCase {
     
     private AttributeQuery query;
     
-    private AttributesMapper<Attribute, IdPAttribute> mapper;
+    private ReloadableService<AttributeTranscoderRegistry> registry;
     
     private FilterByQueriedAttributes action;
     
@@ -86,16 +90,18 @@ public class FilterByQueriedAttributesTest extends XMLObjectBaseTestCase {
         }
     }
         
-    @BeforeClass public void setup() throws XMLParserException, UnmarshallingException {
-        query = unmarshallElement(PATH + "AttributeQuery.xml", true);        
-        mapper = getBean(PATH + "saml2Mapper.xml", SAML2AttributesMapper.class);
+    @BeforeClass public void setup() {
+        registry = new RegistryService(getBean(PATH + "saml2Mapper.xml", AttributeTranscoderRegistryImpl.class));
     }
     
-    @BeforeMethod public void setUpMethod() throws ComponentInitializationException {
-        action = new FilterByQueriedAttributes(mapper);
+    @BeforeMethod public void setUpMethod() throws ComponentInitializationException, XMLParserException, UnmarshallingException {
+        query = unmarshallElement(PATH + "AttributeQuery.xml", true);        
+        action = new FilterByQueriedAttributes();
+        action.setTranscoderRegistry(registry);
+        action.initialize();
+
         rc = new RequestContextBuilder().setInboundMessage(query).buildRequestContext();
         prc = new WebflowRequestContextProfileRequestContextLookup().apply(rc);
-        action.initialize();
     }
 
     @Test public void noAttributes() {
@@ -107,7 +113,11 @@ public class FilterByQueriedAttributesTest extends XMLObjectBaseTestCase {
     @Test public void noValues() {
         final RelyingPartyContext rpc = prc.getSubcontext(RelyingPartyContext.class,true);
         final AttributeContext ac = rpc.getSubcontext(AttributeContext.class,true);
-        final List<IdPAttribute> attributes = List.of(new IdPAttribute("eduPersonAssurance"), new IdPAttribute("flooby"), new IdPAttribute("eduPersonScopedAffiliation"),  new IdPAttribute("eduPersonTargetedID"));
+        final List<IdPAttribute> attributes = List.of(
+                new IdPAttribute("eduPersonAssurance"),
+                new IdPAttribute("flooby"),
+                new IdPAttribute("eduPersonScopedAffiliation"),
+                new IdPAttribute("eduPersonTargetedID"));
         ac.setIdPAttributes(attributes);
         final Event event = action.execute(rc);
         ActionTestingSupport.assertProceedEvent(event);
@@ -131,4 +141,29 @@ public class FilterByQueriedAttributesTest extends XMLObjectBaseTestCase {
         assertEquals(ac.getIdPAttributes().size(), 2);
     }
 
-}
+    private static class RegistryService extends AbstractReloadableService<AttributeTranscoderRegistry> {
+
+        private ServiceableComponent<AttributeTranscoderRegistry> component;
+
+        protected RegistryService(ServiceableComponent<AttributeTranscoderRegistry> what) {
+            component = what;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        @Nullable public ServiceableComponent<AttributeTranscoderRegistry> getServiceableComponent() {
+            if (null == component) {
+                return null;
+            }
+            component.pinComponent();
+            return component;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        protected boolean shouldReload() {
+            return false;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/attribute/mapping/attributesMapper.xml b/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/attribute/mapping/attributesMapper.xml
deleted file mode 100644
index a079a3d..0000000
--- a/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/attribute/mapping/attributesMapper.xml
+++ /dev/null
@@ -1,107 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
-	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
-
-    <!--  This is what we are trying to reverse map (note bi-directional aliasing, just to confuse things.  And me.
-    
-            <AttributeDefinition xsi:type="ad:Simple" id="eduPersonAssurance" >
-                <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:eduPersonAssurance" />
-                <AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" friendlyName="eduPersonAssurance" />
-                <AttributeEncoder xsi:type="SAML2String" nameFormat="http://example.org/Format" name="http://example.org/name/for/Attribute" friendlyName="otherSAMLName" />
-            </AttributeDefinition>
-
-            <AttributeDefinition xsi:type="ad:Simple" id="otherPersonAssurance" >
-                <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:eduPersonAssurance" />
-                <AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" friendlyName="eduPersonAssurance" />
-            </AttributeDefinition>
-                
-            <AttributeDefinition xsi:type="ad:Scoped" id="eduPersonScopedAffiliation" scope="example.org" >
-                <AttributeEncoder xsi:type="SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonScopedAffiliation" />
-                <AttributeEncoder xsi:type="SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" friendlyName="eduPersonScopedAffiliation" 
-                       scopeDelimiter='#'/>
-            </AttributeDefinition>
-            
-            <AttributeDefinition xsi:type="ad:SAML2NameID" id="eduPersonTargetedID" 
-                                          nameIdFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">
-                <AttributeEncoder xsi:type="SAML1XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" />
-                <AttributeEncoder xsi:type="SAML2XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" friendlyName="eduPersonTargetedID" />
-            </AttributeDefinition> 
-    
-    
-          But note that this is unmappable (no source name)
-          
-            <AttributeDefinition id="transientId" xsi:type="ad:TransientId">
-                <AttributeEncoder xsi:type="SAML1StringNameIdentifier" nameFormat="urn:mace:shibboleth:1.0:nameIdentifier"/>
-                <AttributeEncoder xsi:type="SAML2StringNameID" nameFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"/>
-            </AttributeDefinition>
-          
-    
-     -->
-     
-	<bean id="abstractStringMapper" init-method="initialize" abstract="true" 
-		class="net.shibboleth.idp.saml.attribute.mapping.impl.RequestedAttributeMapper">
-		<property name="valueMapper">
-			<bean
-				class="net.shibboleth.idp.saml.attribute.mapping.impl.StringAttributeValueMapper" />
-		</property>
-	</bean>
-
-	<bean id="RequestedAttributesMapper"
-		class="net.shibboleth.idp.saml.attribute.mapping.impl.RequestedAttributesMapper"
-		init-method="initialize" p:id="RequestedAttributesMapper">
-		<property name="mappers">
-			<list>
-				<bean parent="abstractStringMapper">
-					<property name="id" value="eduPersonAssurance" />
-					<property name="SAMLName" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" />
-					<property name="attributeIds">
-						<list>
-							<value>eduPersonAssurance</value>
-							<value>otherPersonAssurance</value>
-						</list>
-					</property>
-				</bean>
-				<bean parent="abstractStringMapper">
-					<property name="SAMLName" value="http://example.org/name/for/Attribute" />
-					<property name="id" value="otherSAMLName" />
-					<property name="attributeFormat" value="http://example.org/Format " />
-					<property name="attributeIds">
-						<list>
-							<value>eduPersonAssurance</value>
-						</list>
-					</property>
-				</bean>
-				<bean parent="abstractStringMapper">
-					<property name="sAMLName" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" />
-					<property name="id" value="eduPersonScopedAffiliation" />
-					<property name="attributeFormat" value=" format " />
-					<property name="valueMapper">
-						<bean
-							class="net.shibboleth.idp.saml.attribute.mapping.impl.ScopedStringAttributeValueMapper"
-							p:delimiter="#" />
-					</property>
-					<property name="attributeIds">
-						<list>
-							<value>eduPersonScopedAffiliation</value>
-						</list>
-					</property>
-				</bean>
-				<bean parent="abstractStringMapper">
-				    <property name="attributeFormat" value="format"/>
-					<property name="sAMLName" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.10"/>
-					<property name="id" value="eduPersonTargetedID"/>
-					<property name="valueMapper">
-						<bean
-							class="net.shibboleth.idp.saml.attribute.mapping.impl.XMLObjectAttributeValueMapper" />
-					</property>
-					<property name="attributeIds">
-						<list>
-							<value>eduPersonTargetedID</value>
-						</list>
-					</property>
-				</bean>
-			</list>
-		</property>
-	</bean>
-</beans>
diff --git a/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/attribute/mapping/requestedAttributeValues.xml b/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/attribute/mapping/requestedAttributeValues.xml
deleted file mode 100644
index f60a188..0000000
--- a/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/attribute/mapping/requestedAttributeValues.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<AttributeConsumingService xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
-	xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui"
-	xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport" xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute"
-	xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" index="1">
-
-	<ServiceName xml:lang="en">Shibboleth Federated Issue
-		Tracking
-	</ServiceName>
-	<RequestedAttribute FriendlyName="stdFormatFriendly"
-		Name="stdFormat" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
-		<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
-			xsi:type="xs:string">String</saml:AttributeValue>
-		<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
-			xsi:type="xs:boolean">1</saml:AttributeValue>
-		<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema">U2NvdCBDYW50b3IgaXMgdGhlIGdvZCBvZiBTQU1M
-		</saml:AttributeValue>
-		<saml:AttributeValue>scope at scoped.example.org</saml:AttributeValue>
-		<saml:AttributeValue>
-			<mdui:UIInfo>
-				<mdui:DisplayName xml:lang="en">Shibboleth.net
-				</mdui:DisplayName>
-				<mdui:Description xml:lang="en">An identity provider
-					hosted and used by the developers of Shibboleth.
-				</mdui:Description>
-				<mdui:Logo height="82" width="64">https://discovery.shibboleth.net/images/gryphon_64x82.png
-				</mdui:Logo>
-			</mdui:UIInfo>
-			<mdui:UIInfo>
-				<mdui:DisplayName xml:lang="en">Shibboleth.net
-				</mdui:DisplayName>
-				<mdui:Description xml:lang="en">An identity provider
-					hosted and used by the developers of Shibboleth.
-				</mdui:Description>
-				<mdui:Logo height="82" width="64">https://discovery.shibboleth.net/images/gryphon_64x82.png
-				</mdui:Logo>
-			</mdui:UIInfo>
-		</saml:AttributeValue>
-	</RequestedAttribute>
-	<RequestedAttribute FriendlyName="oddFormatFriendly"
-		Name="oddFormat" NameFormat="https://example.org/" />
-	<RequestedAttribute FriendlyName="mail"
-		Name="urn:oid:0.9.2342.19200300.100.1.3" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" />
-	<RequestedAttribute FriendlyName="displayName"
-		Name="urn:oid:2.16.840.1.113730.3.1.241" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
-		<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
-			xsi:type="xs:string">String</saml:AttributeValue>
-	</RequestedAttribute>
-</AttributeConsumingService>
diff --git a/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/AttributeQuery.xml b/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/AttributeQuery.xml
index 960a449..71d44c4 100644
--- a/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/AttributeQuery.xml
+++ b/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/AttributeQuery.xml
@@ -4,11 +4,16 @@
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="urn:oasis:names:tc:SAML:2.0:assertion" 
    Version="2.0"  ID="AQ" IssueInstant="2018-03-14T17:31:00Z">
-   <Attribute  Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" FriendlyName="eduPersonAssurance">
+   <Issuer>me</Issuer>
+   <Subject>
+    <NameID>you</NameID>
+   </Subject>
+   <Attribute Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" FriendlyName="eduPersonAssurance">
     <AttributeValue  xsi:type="xs:string">turquoise</AttributeValue>
    </Attribute>
-   <Attribute  Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" FriendlyName="eduPersonScopedAffiliation">
+   <Attribute Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" FriendlyName="eduPersonScopedAffiliation">
     <AttributeValue  xsi:type="xs:string">blue#yellow</AttributeValue>
    </Attribute>
-   <Attribute  Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" FriendlyName="eduPersonTargetedID"/>
+   <Attribute Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" FriendlyName="eduPersonTargetedID"/>
+   <Attribute Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" FriendlyName="eduPersonTargetedIDWithWrongNameFormat"/>   
 </saml2p:AttributeQuery>
diff --git a/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/AttributeQuerySaml1.xml b/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/AttributeQuerySaml1.xml
index 33ccaf8..5bc3446 100644
--- a/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/AttributeQuerySaml1.xml
+++ b/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/AttributeQuerySaml1.xml
@@ -1,15 +1,14 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<saml1p:AttributeQuery xmlns:saml1p="urn:oasis:names:tc:SAML:1.0:protocol" xmlns="urn:oasis:names:tc:SAML:1.0:assertion"
-   xmlns:xs="http://www.w3.org/2001/XMLSchema"
-   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-   Version="2.0"  ID="AQ" IssueInstant="2018-03-14T17:31:00Z">
-   <Subject/>
-   <Attribute  AttributeName="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" >
-    <AttributeValue  xsi:type="xs:string">turquoise</AttributeValue>
-   </Attribute>
-   <Attribute  AttributeName="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" >
-    <AttributeValue  xsi:type="xs:string" >blue at yellow</AttributeValue>
-   </Attribute>
-   <Attribute  AttributeName="urn:oid:1.3.6.1.4.1.5923.1.1.1.10"/>
-</saml1p:AttributeQuery>
+<saml1p:Request xmlns:saml1p="urn:oasis:names:tc:SAML:1.0:protocol" xmlns="urn:oasis:names:tc:SAML:1.0:assertion"
+    MajorVersion="1" MinorVersion="1" RequestID="ID" IssueInstant="2019-05-02T12:00:00">
+  <saml1p:AttributeQuery>
+   <Subject>
+    <NameIdentifier>you</NameIdentifier>
+   </Subject>
+   <AttributeDesignator AttributeName="flooby" AttributeNamespace="basic" />
+   <AttributeDesignator AttributeName="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" AttributeNamespace="basic" />
+   <AttributeDesignator AttributeName="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" AttributeNamespace="basic" />
+   <AttributeDesignator AttributeName="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" AttributeNamespace="basic"/>
+  </saml1p:AttributeQuery>
+</saml1p:Request>
diff --git a/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/saml1Mapper.xml b/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/saml1Mapper.xml
index a167735..6a2351d 100644
--- a/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/saml1Mapper.xml
+++ b/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/saml1Mapper.xml
@@ -1,78 +1,92 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
-	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:util="http://www.springframework.org/schema/util"
+	xmlns:p="http://www.springframework.org/schema/p"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+	   http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+	default-init-method="initialize"
+	default-destroy-method="destroy">
 
-    <!--  This is what we are trying to reverse map (note bi-directional aliasing, just to confuse things.  And me.
-    
-            <AttributeDefinition xsi:type="ad:Simple" id="eduPersonAssurance" >
-                <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:eduPersonAssurance" />
-            </AttributeDefinition>
+    <bean id="shibboleth.IdentifiableBeanPostProcessor"
+        class="net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor" />
 
-            <AttributeDefinition xsi:type="ad:Simple" id="otherPersonAssurance" >
-                <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:eduPersonAssurance" />
-            </AttributeDefinition>
-                
-            <AttributeDefinition xsi:type="ad:Scoped" id="eduPersonScopedAffiliation" scope="example.org" >
-                <AttributeEncoder xsi:type="SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonScopedAffiliation" />
-            </AttributeDefinition>
-            
-            <AttributeDefinition xsi:type="ad:SAML2NameID" id="eduPersonTargetedID" 
-                                          nameIdFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">
-                <AttributeEncoder xsi:type="SAML1XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" />
-            </AttributeDefinition> 
-    
-    
+    <bean id="SAML1StringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1StringAttributeTranscoder" />
+    <bean id="SAML1ScopedStringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1ScopedStringAttributeTranscoder" />
+   
+    <bean id="shibboleth.TranscodingRule"
+        class="net.shibboleth.idp.attribute.transcoding.TranscodingRule" abstract="true" />
+   
+    <util:list id="DefaultAttributeRegistry">
+
+        <bean parent="shibboleth.TranscodingRule">
+            <constructor-arg>    
+		        <map>
+		            <entry key="id" value="eduPersonAssurance" />
+		            <entry key="transcoder" value-ref="SAML1StringTranscoder" />
+		            <entry key="name" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" />
+		            <entry key="namespace" value="basic" />
+		        </map>
+            </constructor-arg>
+        </bean>
+        
+        <bean parent="shibboleth.TranscodingRule">
+            <constructor-arg>    
+		        <map>
+		            <entry key="id" value="eduPersonAssurance" />
+		            <entry key="transcoder" value-ref="SAML1StringTranscoder" />
+		            <entry key="name" value="http://example.org/name/for/Attribute" />
+		            <entry key="namespace" value="basic" />
+		        </map>
+            </constructor-arg>
+        </bean>
+
+        <bean parent="shibboleth.TranscodingRule">
+            <constructor-arg>    
+		        <map>
+		            <entry key="id" value="otherPersonAssurance" />
+		            <entry key="transcoder" value-ref="SAML1StringTranscoder" />
+		            <entry key="name" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" />
+		            <entry key="namespace" value="basic" />
+		        </map>
+            </constructor-arg>
+        </bean>
+
+        <bean parent="shibboleth.TranscodingRule">
+            <constructor-arg>    
+		        <map>
+		            <entry key="id" value="eduPersonScopedAffiliation" />
+		            <entry key="transcoder" value-ref="SAML1ScopedStringTranscoder" />
+		            <entry key="name" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" />
+		            <entry key="namespace" value="basic" />
+		        </map>
+            </constructor-arg>
+        </bean>
+
+        <bean parent="shibboleth.TranscodingRule">
+            <constructor-arg>    
+		        <map>
+		            <entry key="id" value="eduPersonTargetedID" />
+		            <entry key="transcoder" value-ref="SAML1ScopedStringTranscoder" />
+		            <entry key="name" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" />
+		            <entry key="namespace" value="basic" />
+		        </map>
+            </constructor-arg>
+        </bean>
+
+    </util:list>
     
-     -->
-     
-	<bean id="abstractStringMapper" init-method="initialize" abstract="true" 
-		class="net.shibboleth.idp.saml.attribute.mapping.impl.SAML1AttributeDesignatorMapper">
-	</bean>
+    <util:map id="DefaultNamingRegistry">
+        <entry key="#{T(org.opensaml.saml.saml1.core.AttributeDesignator)}">
+            <bean class="net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML1AttributeTranscoder.NamingFunction" />
+        </entry>
+    </util:map>
+
 
-	<bean id="AttributesMapper"
-		class="net.shibboleth.idp.saml.attribute.mapping.impl.SAML1AttributeDesignatorsMapper"
-		init-method="initialize" p:id="SAML1AttributesMapper">
-		<property name="mappers">
-			<list>
-				<bean parent="abstractStringMapper">
-					<property name="id" value="eduPersonAssurance" />
-					<property name="SAMLName" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" />
-					<property name="attributeIds">
-						<list>
-							<value>eduPersonAssurance</value>
-							<value>otherPersonAssurance</value>
-						</list>
-					</property>
-				</bean>
-				<bean parent="abstractStringMapper">
-					<property name="SAMLName" value="http://example.org/name/for/Attribute" />
-					<property name="id" value="otherSAMLName" />
-					<property name="attributeIds">
-						<list>
-							<value>eduPersonAssurance</value>
-						</list>
-					</property>
-				</bean>
-				<bean parent="abstractStringMapper">
-					<property name="sAMLName" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" />
-					<property name="id" value="eduPersonScopedAffiliation" />
-					<property name="attributeIds">
-						<list>
-							<value>eduPersonScopedAffiliation</value>
-						</list>
-					</property>
-				</bean>
-				<bean parent="abstractStringMapper">
-					<property name="sAMLName" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.10"/>
-					<property name="id" value="eduPersonTargetedID"/>
-					<property name="attributeIds">
-						<list>
-							<value>eduPersonTargetedID</value>
-						</list>
-					</property>
-				</bean>
-			</list>
-		</property>
-	</bean>
+    <bean id="AttributeTranscoderRegistry" class="net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl"
+        p:namingRegistry-ref="DefaultNamingRegistry"
+        p:transcoderRegistry-ref="DefaultAttributeRegistry" />
+        
 </beans>
diff --git a/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/saml2Mapper.xml b/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/saml2Mapper.xml
index 0dc5069..d7543da 100644
--- a/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/saml2Mapper.xml
+++ b/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/profile/saml2Mapper.xml
@@ -1,96 +1,91 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
-	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns:util="http://www.springframework.org/schema/util"
+    xmlns:p="http://www.springframework.org/schema/p"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+    default-init-method="initialize"
+    default-destroy-method="destroy">
 
-    <!--  This is what we are trying to reverse map (note bi-directional aliasing, just to confuse things.  And me.
-    
-            <AttributeDefinition xsi:type="ad:Simple" id="eduPersonAssurance" >
-                <AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" friendlyName="eduPersonAssurance" />
-                <AttributeEncoder xsi:type="SAML2String" nameFormat="http://example.org/Format" name="http://example.org/name/for/Attribute" friendlyName="otherSAMLName" />
-            </AttributeDefinition>
+    <bean id="shibboleth.IdentifiableBeanPostProcessor"
+        class="net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor" />
 
-            <AttributeDefinition xsi:type="ad:Simple" id="otherPersonAssurance" >
-                <AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" friendlyName="eduPersonAssurance" />
-            </AttributeDefinition>
-                
-            <AttributeDefinition xsi:type="ad:Scoped" id="eduPersonScopedAffiliation" scope="example.org" >
-                <AttributeEncoder xsi:type="SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" friendlyName="eduPersonScopedAffiliation" 
-                       scopeDelimiter='#'/>
-            </AttributeDefinition>
-            
-            <AttributeDefinition xsi:type="ad:SAML2NameID" id="eduPersonTargetedID" 
-                                          nameIdFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">
-                <AttributeEncoder xsi:type="SAML2XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" friendlyName="eduPersonTargetedID" />
-            </AttributeDefinition> 
-    
-    
+    <bean id="SAML2StringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2StringAttributeTranscoder" />
+    <bean id="SAML2ScopedStringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2ScopedStringAttributeTranscoder" />
+    <bean id="SAML2XMLObjectTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2XMLObjectAttributeTranscoder" />
+
+    <bean id="shibboleth.TranscodingRule"
+        class="net.shibboleth.idp.attribute.transcoding.TranscodingRule" abstract="true" />
+
+    <util:list id="DefaultAttributeRegistry">
     
-     -->
-     
-	<bean id="abstractStringMapper" init-method="initialize" abstract="true" 
-		class="net.shibboleth.idp.saml.attribute.mapping.impl.SAML2AttributeMapper">
-		<property name="valueMapper">
-			<bean
-				class="net.shibboleth.idp.saml.attribute.mapping.impl.StringAttributeValueMapper" />
-		</property>
-	</bean>
+        <bean parent="shibboleth.TranscodingRule">
+            <constructor-arg>
+		        <map>
+		            <entry key="id" value="eduPersonAssurance" />
+		            <entry key="transcoder" value-ref="SAML2StringTranscoder" />
+		            <entry key="name" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" />
+		        </map>
+            </constructor-arg>
+        </bean>
+        
+        <bean parent="shibboleth.TranscodingRule">
+            <constructor-arg>    
+		        <map>
+		            <entry key="id" value="eduPersonAssurance" />
+		            <entry key="transcoder" value-ref="SAML2StringTranscoder" />
+		            <entry key="name" value="http://example.org/name/for/Attribute" />
+		            <entry key="nameFormat" value="http://example.org/Format" />
+		        </map>
+            </constructor-arg>
+        </bean>
+
+        <bean parent="shibboleth.TranscodingRule">
+            <constructor-arg>    
+		        <map>
+		            <entry key="id" value="otherPersonAssurance" />
+		            <entry key="transcoder" value-ref="SAML2StringTranscoder" />
+		            <entry key="name" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" />
+		        </map>
+            </constructor-arg>
+        </bean>
+
+        <bean parent="shibboleth.TranscodingRule">
+            <constructor-arg>    
+		        <map>
+		            <entry key="id" value="eduPersonScopedAffiliation" />
+		            <entry key="transcoder" value-ref="SAML2ScopedStringTranscoder" />
+		            <entry key="scopeDelimiter" value="#" />
+		            <entry key="name" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" />
+		        </map>
+            </constructor-arg>
+        </bean>
+
+        <bean parent="shibboleth.TranscodingRule">
+            <constructor-arg>    
+		        <map>
+		            <entry key="id" value="eduPersonTargetedID" />
+		            <entry key="transcoder" value-ref="SAML2XMLObjectTranscoder" />
+		            <entry key="name" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" />
+		        </map>
+            </constructor-arg>
+        </bean>
+
+    </util:list>
+	
+    <util:map id="DefaultNamingRegistry">
+        <entry key="#{T(org.opensaml.saml.saml2.core.Attribute)}">
+            <bean class="net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder.NamingFunction" />
+        </entry>
+    </util:map>
+
 
-	<bean id="AttributesMapper"
-		class="net.shibboleth.idp.saml.attribute.mapping.impl.SAML2AttributesMapper"
-		init-method="initialize" p:id="SAML2AttributesMapper">
-		<property name="mappers">
-			<list>
-				<bean parent="abstractStringMapper">
-					<property name="id" value="eduPersonAssurance" />
-					<property name="SAMLName" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" />
-					<property name="attributeIds">
-						<list>
-							<value>eduPersonAssurance</value>
-							<value>otherPersonAssurance</value>
-						</list>
-					</property>
-				</bean>
-				<bean parent="abstractStringMapper">
-					<property name="SAMLName" value="http://example.org/name/for/Attribute" />
-					<property name="id" value="otherSAMLName" />
-					<property name="attributeFormat" value="http://example.org/Format " />
-					<property name="attributeIds">
-						<list>
-							<value>eduPersonAssurance</value>
-						</list>
-					</property>
-				</bean>
-				<bean parent="abstractStringMapper">
-					<property name="sAMLName" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" />
-					<property name="id" value="eduPersonScopedAffiliation" />
-					<property name="attributeFormat" value=" format " />
-					<property name="valueMapper">
-						<bean
-							class="net.shibboleth.idp.saml.attribute.mapping.impl.ScopedStringAttributeValueMapper"
-							p:delimiter="#" />
-					</property>
-					<property name="attributeIds">
-						<list>
-							<value>eduPersonScopedAffiliation</value>
-						</list>
-					</property>
-				</bean>
-				<bean parent="abstractStringMapper">
-				    <property name="attributeFormat" value="format"/>
-					<property name="sAMLName" value="urn:oid:1.3.6.1.4.1.5923.1.1.1.10"/>
-					<property name="id" value="eduPersonTargetedID"/>
-					<property name="valueMapper">
-						<bean
-							class="net.shibboleth.idp.saml.attribute.mapping.impl.XMLObjectAttributeValueMapper" />
-					</property>
-					<property name="attributeIds">
-						<list>
-							<value>eduPersonTargetedID</value>
-						</list>
-					</property>
-				</bean>
-			</list>
-		</property>
-	</bean>
+    <bean id="AttributeTranscoderRegistry" class="net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl"
+        p:namingRegistry-ref="DefaultNamingRegistry"
+        p:transcoderRegistry-ref="DefaultAttributeRegistry" />
+	
 </beans>
diff --git a/idp-schema/.project b/idp-schema/.project
index c89db8c..dc76e99 100644
--- a/idp-schema/.project
+++ b/idp-schema/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-schema/src/main/resources/schema/shibboleth-afp.xsd b/idp-schema/src/main/resources/schema/shibboleth-afp.xsd
index 7ca9682..dc202a7 100644
--- a/idp-schema/src/main/resources/schema/shibboleth-afp.xsd
+++ b/idp-schema/src/main/resources/schema/shibboleth-afp.xsd
@@ -751,11 +751,16 @@
         </complexContent>
     </complexType>
 
-    <complexType name="AttributeInMetadataType" abstract="true">
+    <complexType name="AttributeInMetadata">
         <annotation>
             <documentation>
-                Functions that evaluate to true if an attribute (or specific values) is specified by
-                supplied metadata.
+                A match function that evaluates to true if an attribute (or specific values) is specified by
+                the requesting entity's AttributeConsumingService SP metadata.
+                
+                It supports both explicit checking for a named SAML RequestedAttribute in metadata, or
+                comparison via the transcoding registry to compare the internal attribute form against
+                decoded objects produced from the metadata. This is the old "mapped" functionality
+                in V3 and is now collapsed into one matcher type.
             </documentation>
         </annotation>
         <complexContent>
@@ -783,29 +788,21 @@
                         </documentation>
                     </annotation>
                 </attribute>
-            </extension>
-        </complexContent>
-    </complexType>
-
-    <complexType name="AttributeInMetadata">
-        <annotation>
-            <documentation>
-                A match function that evaluates to true if an attribute (or specific values) is specified by
-                the requesting entity's AttributeConsumingService SP metadata.
-            </documentation>
-        </annotation>
-        <complexContent>
-            <extension base="afp:AttributeInMetadataType">
-                <attribute name="attributeName" type="string" use="optional">
+                <attribute name="attributeName" type="string">
                     <annotation>
                         <documentation>The name of a requested attribute to look for.</documentation>
                     </annotation>
                 </attribute>
-                <attribute name="attributeNameFormat" type="string" use="optional">
+                <attribute name="attributeNameFormat" type="string">
                     <annotation>
                         <documentation>The name format of a requested attribute to look for.</documentation>
                     </annotation>
                 </attribute>
+                <attribute name="objectStrategyRef" type="string">
+                    <annotation>
+                        <documentation>Reference to locate XMLObject source of mapped attributes.</documentation>
+                    </annotation>
+                </attribute>
             </extension>
         </complexContent>
     </complexType>
@@ -813,18 +810,11 @@
     <complexType name="MappedAttributeInMetadata">
         <annotation>
             <documentation>
-                A match function that evaluates to true if a mapped attribute (or specific values) is specified by
-                the requesting entity's AttributeConsumingService SP metadata.
+                Deprecated alias for afp:AttributeInMetadata type.
             </documentation>
         </annotation>
         <complexContent>
-            <extension base="afp:AttributeInMetadataType">
-                <attribute name="objectStrategyRef" type="string" use="optional">
-                    <annotation>
-                        <documentation>Reference to locate XMLObject source of mapped attributes.</documentation>
-                    </annotation>
-                </attribute>
-            </extension>
+            <extension base="afp:AttributeInMetadata" />
         </complexContent>
     </complexType>
 
diff --git a/idp-session-api/.project b/idp-session-api/.project
index 62a1f7f..5e64ebd 100644
--- a/idp-session-api/.project
+++ b/idp-session-api/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-session-impl/.project b/idp-session-impl/.project
index 8a48a91..eae928f 100644
--- a/idp-session-impl/.project
+++ b/idp-session-impl/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-ui/.project b/idp-ui/.project
index b903b34..a5da135 100644
--- a/idp-ui/.project
+++ b/idp-ui/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-war/.project b/idp-war/.project
index 3f1824d..5294722 100644
--- a/idp-war/.project
+++ b/idp-war/.project
@@ -16,11 +16,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.springframework.ide.eclipse.core.springbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.m2e.core.maven2Builder</name>
 			<arguments>
 			</arguments>
diff --git a/idp-war/src/main/webapp/WEB-INF/jsp/status.jsp b/idp-war/src/main/webapp/WEB-INF/jsp/status.jsp
index 7371f15..5e41cde 100644
--- a/idp-war/src/main/webapp/WEB-INF/jsp/status.jsp
+++ b/idp-war/src/main/webapp/WEB-INF/jsp/status.jsp
@@ -13,6 +13,7 @@
 <%@ page import="net.shibboleth.idp.Version" %>
 <%@ page import="net.shibboleth.idp.saml.metadata.RelyingPartyMetadataProvider" %>
 <%@ page import="net.shibboleth.idp.attribute.resolver.AttributeResolver" %>
+<%@ page import="net.shibboleth.idp.attribute.resolver.impl.AttributeResolverImpl" %>
 <%@ page import="net.shibboleth.idp.attribute.resolver.DataConnector" %>
 <%@ page import="net.shibboleth.utilities.java.support.component.IdentifiedComponent" %>
 <%@ page import="net.shibboleth.utilities.java.support.service.ReloadableService" %>

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list