You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2017/05/25 22:58:15 UTC

incubator-juneau git commit: PartSerializer support.

Repository: incubator-juneau
Updated Branches:
  refs/heads/master 78dffd972 -> f2fafd3ef


PartSerializer support.

Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/f2fafd3e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/f2fafd3e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/f2fafd3e

Branch: refs/heads/master
Commit: f2fafd3ef89335d124f8a98c1c2fa7500be85619
Parents: 78dffd9
Author: JamesBognar <ja...@apache.org>
Authored: Thu May 25 18:58:11 2017 -0400
Committer: JamesBognar <ja...@apache.org>
Committed: Thu May 25 18:58:11 2017 -0400

----------------------------------------------------------------------
 .../main/java/org/apache/juneau/dto/Link.java   |  3 +-
 .../org/apache/juneau/internal/StringUtils.java |  1 +
 .../org/apache/juneau/remoteable/FormData.java  | 16 +++-
 .../apache/juneau/remoteable/FormDataIfNE.java  | 12 +++
 .../org/apache/juneau/remoteable/Header.java    | 16 +++-
 .../apache/juneau/remoteable/HeaderIfNE.java    | 12 +++
 .../java/org/apache/juneau/remoteable/Path.java | 16 +++-
 .../org/apache/juneau/remoteable/Query.java     | 16 +++-
 .../org/apache/juneau/remoteable/QueryIfNE.java | 12 +++
 .../juneau/remoteable/RemoteMethodArg.java      | 16 +++-
 .../juneau/remoteable/RemoteableMethodMeta.java | 14 +--
 .../juneau/serializer/PartSerializer.java       | 52 +++++++++++
 .../org/apache/juneau/serializer/PartType.java  | 31 +++++++
 .../urlencoding/UrlEncodingSerializer.java      | 15 ++-
 juneau-core/src/main/javadoc/overview.html      | 22 ++++-
 .../juneau/rest/client/NameValuePairs.java      | 10 +-
 .../org/apache/juneau/rest/client/RestCall.java | 98 ++++++++++++--------
 .../apache/juneau/rest/client/RestClient.java   | 47 +++++++---
 .../juneau/rest/client/RestClientBuilder.java   | 41 +++++++-
 .../rest/client/SerializedNameValuePair.java    |  9 +-
 .../rest/test/ThirdPartyProxyResource.java      | 34 +++++++
 .../juneau/rest/test/ThirdPartyProxyTest.java   | 34 +++++++
 .../java/org/apache/juneau/rest/Redirect.java   |  5 +-
 23 files changed, 441 insertions(+), 91 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/dto/Link.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/dto/Link.java b/juneau-core/src/main/java/org/apache/juneau/dto/Link.java
index 1b926aa..e1036c7 100644
--- a/juneau-core/src/main/java/org/apache/juneau/dto/Link.java
+++ b/juneau-core/src/main/java/org/apache/juneau/dto/Link.java
@@ -15,6 +15,7 @@ package org.apache.juneau.dto;
 import java.text.*;
 
 import org.apache.juneau.html.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 import org.apache.juneau.utils.*;
 
@@ -104,7 +105,7 @@ public class Link implements Comparable<Link> {
 	 */
 	public Link setHref(String href, Object...args) {
 		for (int i = 0; i < args.length; i++)
-			args[i] = UrlEncodingSerializer.DEFAULT.serializePart(args[i], null, null);
+			args[i] = UrlEncodingSerializer.DEFAULT.serialize(PartType.PATH, args[i]);
 		this.href = (args.length > 0 ? MessageFormat.format(href, args) : href);
 		return this;
 	}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
index c4bf1db..d17bc58 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
@@ -26,6 +26,7 @@ import java.util.regex.*;
 import javax.xml.bind.*;
 
 import org.apache.juneau.parser.*;
+import org.apache.juneau.parser.ParseException;
 
 /**
  * Reusable string utility methods.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java
index 23983c3..83389b8 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java
@@ -18,6 +18,7 @@ import static java.lang.annotation.RetentionPolicy.*;
 import java.lang.annotation.*;
 
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -41,12 +42,12 @@ import org.apache.juneau.urlencoding.*;
  * <p>
  * The argument can be any of the following types:
  * <ul class='spaced-list'>
- * 	<li>Any serializable POJO - Converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * 	<li>Any serializable POJO - Converted to text using {@link UrlEncodingSerializer#serialize(PartType,Object)}.
  * 	<li><code>NameValuePairs</code> - Individual name-value pairs.
  * 	<li><code>Map&lt;String,Object&gt;</code> - Individual name-value pairs.
- * 		Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * 		Values are converted to text using {@link UrlEncodingSerializer#serialize(PartType,Object)}.
  * 	<li>A bean - Individual name-value pairs.
- * 		Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * 		Values are converted to text using {@link UrlEncodingSerializer#serialize(PartType,Object)}.
  * </ul>
  * <p>
  * The annotation can also be applied to a bean property field or getter when the argument is annotated with
@@ -93,4 +94,13 @@ public @interface FormData {
 	 * </ul>
 	 */
 	String value() default "*";
+
+	/**
+	 * Specifies the {@link PartSerializer} class used for serializing values to strings.
+	 * <p>
+	 * The default serializer converters values to UON notation.
+	 * <p>
+	 * This annotation is provided to allow values to be custom serialized.
+	 */
+	Class<? extends PartSerializer> serializer() default UrlEncodingSerializer.class;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java
index d043217..2d0b3ab 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java
@@ -17,6 +17,9 @@ import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
 
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.urlencoding.*;
+
 /**
  * Identical to {@link FormData @FormData} except skips values if they're null/blank.
  */
@@ -38,4 +41,13 @@ public @interface FormDataIfNE {
 	 * </ul>
 	 */
 	String value() default "*";
+
+	/**
+	 * Specifies the {@link PartSerializer} class used for serializing values to strings.
+	 * <p>
+	 * The default serializer converters values to UON notation.
+	 * <p>
+	 * This annotation is provided to allow values to be custom serialized.
+	 */
+	Class<? extends PartSerializer> serializer() default UrlEncodingSerializer.class;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java
index 3fae979..6eaf870 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java
@@ -18,6 +18,7 @@ import static java.lang.annotation.RetentionPolicy.*;
 import java.lang.annotation.*;
 
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -39,11 +40,11 @@ import org.apache.juneau.urlencoding.*;
  * The argument can be any of the following types:
  * <ul class='spaced-list'>
  * 	<li><code>NameValuePairs</code> - Individual name-value pairs.
- * 	<li>Any serializable POJO - Converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * 	<li>Any serializable POJO - Converted to text using {@link UrlEncodingSerializer#serialize(PartType,Object)}.
  * 	<li><code>Map&lt;String,Object&gt;</code> - Individual name-value pairs.
- * 		Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * 		Values are converted to text using {@link UrlEncodingSerializer#serialize(PartType,Object)}.
  * 	<li>A bean - Individual name-value pairs.
- * 		Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * 		Values are converted to text using {@link UrlEncodingSerializer#serialize(PartType,Object)}.
  * </ul>
  * <p>
  * The annotation can also be applied to a bean property field or getter when the argument is annotated with
@@ -90,4 +91,13 @@ public @interface Header {
 	 * </ul>
 	 */
 	String value() default "*";
+
+	/**
+	 * Specifies the {@link PartSerializer} class used for serializing values to strings.
+	 * <p>
+	 * The default serializer converters values to UON notation.
+	 * <p>
+	 * This annotation is provided to allow values to be custom serialized.
+	 */
+	Class<? extends PartSerializer> serializer() default UrlEncodingSerializer.class;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java
index 2aaf711..4839ff0 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java
@@ -17,6 +17,9 @@ import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
 
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.urlencoding.*;
+
 /**
  * Identical to {@link Header @Header} except skips values if they're null/blank.
  */
@@ -38,4 +41,13 @@ public @interface HeaderIfNE {
 	 * </ul>
 	 */
 	String value() default "*";
+
+	/**
+	 * Specifies the {@link PartSerializer} class used for serializing values to strings.
+	 * <p>
+	 * The default serializer converters values to UON notation.
+	 * <p>
+	 * This annotation is provided to allow values to be custom serialized.
+	 */
+	Class<? extends PartSerializer> serializer() default UrlEncodingSerializer.class;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java
index 47c6eea..919615e 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java
@@ -18,6 +18,7 @@ import static java.lang.annotation.RetentionPolicy.*;
 import java.lang.annotation.*;
 
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -36,11 +37,11 @@ import org.apache.juneau.urlencoding.*;
  * The argument can be any of the following types:
  * <ul class='spaced-list'>
  * 	<li><code>NameValuePairs</code> - Individual name-value pairs.
- * 	<li>Any serializable POJO - Converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * 	<li>Any serializable POJO - Converted to text using {@link UrlEncodingSerializer#serialize(PartType,Object)}.
  * 	<li><code>Map&lt;String,Object&gt;</code> - Individual name-value pairs.
- * 		Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * 		Values are converted to text using {@link UrlEncodingSerializer#serialize(PartType,Object)}.
  * 	<li>A bean - Individual name-value pairs.
- * 		Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * 		Values are converted to text using {@link UrlEncodingSerializer#serialize(PartType,Object)}.
  * </ul>
  * <p>
  * The annotation can also be applied to a bean property field or getter when the argument is annotated with
@@ -84,4 +85,13 @@ public @interface Path {
 	 * </ul>
 	 */
 	String value() default "*";
+
+	/**
+	 * Specifies the {@link PartSerializer} class used for serializing values to strings.
+	 * <p>
+	 * The default serializer converters values to UON notation.
+	 * <p>
+	 * This annotation is provided to allow values to be custom serialized.
+	 */
+	Class<? extends PartSerializer> serializer() default UrlEncodingSerializer.class;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java
index 5729412..7738468 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java
@@ -18,6 +18,7 @@ import static java.lang.annotation.RetentionPolicy.*;
 import java.lang.annotation.*;
 
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -42,11 +43,11 @@ import org.apache.juneau.urlencoding.*;
  * The argument can be any of the following types:
  * <ul class='spaced-list'>
  * 	<li><code>NameValuePairs</code> - Individual name-value pairs.
- * 	<li>Any serializable POJO - Converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * 	<li>Any serializable POJO - Converted to text using {@link UrlEncodingSerializer#serialize(PartType,Object)}.
  * 	<li><code>Map&lt;String,Object&gt;</code> - Individual name-value pairs.
- * 		Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * 		Values are converted to text using {@link UrlEncodingSerializer#serialize(PartType,Object)}.
  * 	<li>A bean - Individual name-value pairs.
- * 		Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * 		Values are converted to text using {@link UrlEncodingSerializer#serialize(PartType,Object)}.
  * 	<li>{@link String} - Treated as a query string.
  * </ul>
  * <p>
@@ -95,4 +96,13 @@ public @interface Query {
 	 * </ul>
 	 */
 	String value() default "*";
+
+	/**
+	 * Specifies the {@link PartSerializer} class used for serializing values to strings.
+	 * <p>
+	 * The default serializer converters values to UON notation.
+	 * <p>
+	 * This annotation is provided to allow values to be custom serialized.
+	 */
+	Class<? extends PartSerializer> serializer() default UrlEncodingSerializer.class;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java
index c2cc2fd..8d38510 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java
@@ -17,6 +17,9 @@ import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
 
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.urlencoding.*;
+
 /**
  * Identical to {@link Query @Query} except skips values if they're null/blank.
  */
@@ -39,4 +42,13 @@ public @interface QueryIfNE {
 	 * </ul>
 	 */
 	String value() default "*";
+
+	/**
+	 * Specifies the {@link PartSerializer} class used for serializing values to strings.
+	 * <p>
+	 * The default serializer converters values to UON notation.
+	 * <p>
+	 * This annotation is provided to allow values to be custom serialized.
+	 */
+	Class<? extends PartSerializer> serializer() default UrlEncodingSerializer.class;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java
index 86d2eac..ab9a44c 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java
@@ -12,6 +12,9 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.remoteable;
 
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.urlencoding.*;
+
 /**
  * Represents the metadata about an annotated argument of a method on a remote proxy interface.
  */
@@ -26,16 +29,27 @@ public class RemoteMethodArg {
 	/** The value is skipped if it's null/empty. */
 	public final boolean skipIfNE;
 
+	/** The serializer used for converting objects to strings. */
+	public final PartSerializer serializer;
+
 	/**
 	 * Constructor.
 	 *
 	 * @param name The argument name.  Can be blank.
 	 * @param index The zero-based index of the argument on the Java method.
 	 * @param skipIfNE The value is skipped if it's null/empty.
+	 * @param serializer The class to use for serializing headers, query paramters, form-data parameters, and
+	 * 	path variables.
+	 * 	If {@link UrlEncodingSerializer}, then the url-encoding serializer defined on the client will be used.
 	 */
-	protected RemoteMethodArg(String name, int index, boolean skipIfNE) {
+	protected RemoteMethodArg(String name, int index, boolean skipIfNE, Class<? extends PartSerializer> serializer) {
 		this.name = name;
 		this.index = index;
 		this.skipIfNE = skipIfNE;
+		try {
+			this.serializer = (serializer == UrlEncodingSerializer.class ? null : serializer.newInstance());
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
index bad4686..13b96b4 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
@@ -90,25 +90,25 @@ public class RemoteableMethodMeta {
 					Class<?> ca = a.annotationType();
 					if (ca == Path.class) {
 						Path p = (Path)a;
-						annotated = pathArgs.add(new RemoteMethodArg(p.value(), index, false));
+						annotated = pathArgs.add(new RemoteMethodArg(p.value(), index, false, p.serializer()));
 					} else if (ca == Query.class) {
 						Query q = (Query)a;
-						annotated = queryArgs.add(new RemoteMethodArg(q.value(), index, false));
+						annotated = queryArgs.add(new RemoteMethodArg(q.value(), index, false, q.serializer()));
 					} else if (ca == QueryIfNE.class) {
 						QueryIfNE q = (QueryIfNE)a;
-						annotated = queryArgs.add(new RemoteMethodArg(q.value(), index, true));
+						annotated = queryArgs.add(new RemoteMethodArg(q.value(), index, true, q.serializer()));
 					} else if (ca == FormData.class) {
 						FormData f = (FormData)a;
-						annotated = formDataArgs.add(new RemoteMethodArg(f.value(), index, false));
+						annotated = formDataArgs.add(new RemoteMethodArg(f.value(), index, false, f.serializer()));
 					} else if (ca == FormDataIfNE.class) {
 						FormDataIfNE f = (FormDataIfNE)a;
-						annotated = formDataArgs.add(new RemoteMethodArg(f.value(), index, true));
+						annotated = formDataArgs.add(new RemoteMethodArg(f.value(), index, true, f.serializer()));
 					} else if (ca == Header.class) {
 						Header h = (Header)a;
-						annotated = headerArgs.add(new RemoteMethodArg(h.value(), index, false));
+						annotated = headerArgs.add(new RemoteMethodArg(h.value(), index, false, h.serializer()));
 					} else if (ca == HeaderIfNE.class) {
 						HeaderIfNE h = (HeaderIfNE)a;
-						annotated = headerArgs.add(new RemoteMethodArg(h.value(), index, true));
+						annotated = headerArgs.add(new RemoteMethodArg(h.value(), index, true, h.serializer()));
 					} else if (ca == RequestBean.class) {
 						annotated = true;
 						requestBeanArgs.add(index);

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/serializer/PartSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/PartSerializer.java b/juneau-core/src/main/java/org/apache/juneau/serializer/PartSerializer.java
new file mode 100644
index 0000000..0063b9e
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/PartSerializer.java
@@ -0,0 +1,52 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the License.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.serializer;
+
+import org.apache.juneau.remoteable.*;
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Interface used to convert POJOs to simple strings in HTTP headers, query parameters, form-data parameters, and URI
+ * path variables.
+ * <p>
+ * By default, the {@link UrlEncodingSerializer} class implements this interface so that it can be used to serialize
+ * these HTTP parts.
+ * However, the interface is provided to allow custom serialization of these objects by providing your own implementation
+ * class and using it in any of the following locations:
+ * <ul>
+ * 	<li>{@link FormData#serializer()}
+ * 	<li>{@link FormDataIfNE#serializer()}
+ * 	<li>{@link Query#serializer()}
+ * 	<li>{@link QueryIfNE#serializer()}
+ * 	<li>{@link Header#serializer()}
+ * 	<li>{@link HeaderIfNE#serializer()}
+ * 	<li>{@link Path#serializer()}
+ * 	<li><code>RestClientBuilder.partSerializer(Class)</code>
+ * </ul>
+ * <p>
+ * Implementations must include a no-arg constructor.
+ */
+public interface PartSerializer {
+
+	/**
+	 * Converts the specified value to a string that can be used as an HTTP header value, query parameter value,
+	 * form-data parameter, or URI path variable.
+	 * <p>
+	 * Returned values should NOT be URL-encoded.  This will happen automatically.
+	 *
+	 * @param type The category of value being serialized.
+	 * @param value The value being serialized.
+	 * @return The serialized value.
+	 */
+	public String serialize(PartType type, Object value);
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/serializer/PartType.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/PartType.java b/juneau-core/src/main/java/org/apache/juneau/serializer/PartType.java
new file mode 100644
index 0000000..498c49f
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/PartType.java
@@ -0,0 +1,31 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the License.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.serializer;
+
+/**
+ * Represents possible enum values that can be passed to the {@link PartSerializer#serialize(PartType, Object)} method.
+ */
+public enum PartType {
+
+	/** A URI path variable */
+	PATH,
+
+	/** A URI query parameter */
+	QUERY,
+
+	/** A form-data parameter */
+	FORM_DATA,
+
+	/** An HTTP header */
+	HEADER
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
index 1814833..2ab697a 100644
--- a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
@@ -132,7 +132,7 @@ import org.apache.juneau.uon.*;
  */
 @Produces("application/x-www-form-urlencoded")
 @SuppressWarnings("hiding")
-public class UrlEncodingSerializer extends UonSerializer {
+public class UrlEncodingSerializer extends UonSerializer implements PartSerializer {
 
 	/** Reusable instance of {@link UrlEncodingSerializer}, all default settings. */
 	public static final UrlEncodingSerializer DEFAULT = new UrlEncodingSerializer(PropertyStore.create());
@@ -396,7 +396,7 @@ public class UrlEncodingSerializer extends UonSerializer {
 	 * If <jk>null</jk>, then uses the value from the {@link UrlEncodingSerializerContext#URLENC_paramFormat} setting.
 	 * @return The serialized object.
 	 */
-	public String serializePart(Object o, Boolean urlEncode, Boolean plainTextParams) {
+	private String serializePart(Object o, Boolean urlEncode, Boolean plainTextParams) {
 		try {
 			// Shortcut for simple types.
 			ClassMeta<?> cm = getBeanContext().getClassMetaForObject(o);
@@ -435,4 +435,15 @@ public class UrlEncodingSerializer extends UonSerializer {
 		UrlEncodingSerializerSession s = (UrlEncodingSerializerSession)session;
 		serializeAnything(s, s.getWriter(), o);
 	}
+
+	@Override /* PartSerializer */
+	public String serialize(PartType type, Object value) {
+		switch(type) {
+			case HEADER: return serializePart(value, false, true);
+			case FORM_DATA: return serializePart(value, false, null);
+			case PATH: return serializePart(value, false, null);
+			case QUERY: return serializePart(value, false, null);
+			default: return StringUtils.toString(value);
+		}
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/javadoc/overview.html b/juneau-core/src/main/javadoc/overview.html
index f5b4f86..fe24402 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -6136,6 +6136,20 @@
 					<li>{@link org.apache.juneau.Session#getProperty(Class,String)}
 					<li>{@link org.apache.juneau.Session#getProperty(Class,String,Object)}
 				</ul>	
+			<li>New {@link org.apache.juneau.serializer.PartSerializer} interface particularly tailored to HTTP
+				headers, query parameters, form-data parameters, and path variables.  
+				<br>Allows easy user-defined serialization of these objects.
+				<br>The interface can be used in the following locations:
+				<ul>
+					<li>{@link org.apache.juneau.rest.client.RestClientBuilder#partSerializer(Class)}
+					<li>{@link org.apache.juneau.remoteable.Path#serializer} 
+					<li>{@link org.apache.juneau.remoteable.Query#serializer} 
+					<li>{@link org.apache.juneau.remoteable.QueryIfNE#serializer} 
+					<li>{@link org.apache.juneau.remoteable.FormData#serializer} 
+					<li>{@link org.apache.juneau.remoteable.FormDataIfNE#serializer} 
+					<li>{@link org.apache.juneau.remoteable.Header#serializer} 
+					<li>{@link org.apache.juneau.remoteable.HeaderIfNE#serializer} 
+				</ul>
 		</ul>
 
 		<h6 class='topic'>org.apache.juneau.rest</h6>
@@ -6504,18 +6518,18 @@
 			<li>New methods on {@link org.apache.juneau.rest.client.RestCall}:
 				<ul>
 					<li>{@link org.apache.juneau.rest.client.RestCall#uri(Object) uri(Object)}
-					<li>{@link org.apache.juneau.rest.client.RestCall#query(String,Object,boolean) query(String,Object,boolean)}
+					<li>{@link org.apache.juneau.rest.client.RestCall#query(String,Object,boolean,PartSerializer) query(String,Object,boolean,PartSerializer)}
 					<li>{@link org.apache.juneau.rest.client.RestCall#query(String,Object) query(String,Object)}
 					<li>{@link org.apache.juneau.rest.client.RestCall#queryIfNE(String,Object) queryIfNE(String,Object)}
 					<li>{@link org.apache.juneau.rest.client.RestCall#query(Map) query(Map)}
 					<li>{@link org.apache.juneau.rest.client.RestCall#queryIfNE(Map) queryIfNE(Map)}
 					<li>{@link org.apache.juneau.rest.client.RestCall#query(String) query(String)}
-					<li>{@link org.apache.juneau.rest.client.RestCall#formData(String,Object,boolean) formData(String,Object,boolean)}
+					<li>{@link org.apache.juneau.rest.client.RestCall#formData(String,Object,boolean,PartSerializer) formData(String,Object,boolean,PartSerializer)}
 					<li>{@link org.apache.juneau.rest.client.RestCall#formData(String,Object) formData(String,Object)}
 					<li>{@link org.apache.juneau.rest.client.RestCall#formDataIfNE(String,Object) formDataIfNE(String,Object)}
 					<li>{@link org.apache.juneau.rest.client.RestCall#formData(Map) formData(Map)}
 					<li>{@link org.apache.juneau.rest.client.RestCall#formDataIfNE(Map) formDataIfNE(Map)}
-					<li>{@link org.apache.juneau.rest.client.RestCall#header(String,Object,boolean) header(String,Object,boolean)}
+					<li>{@link org.apache.juneau.rest.client.RestCall#header(String,Object,boolean,PartSerializer) header(String,Object,boolean,PartSerializer)}
 					<li>{@link org.apache.juneau.rest.client.RestCall#header(String,Object) header(String,Object)}
 					<li>{@link org.apache.juneau.rest.client.RestCall#headerIfNE(String,Object) headerIfNE(String,Object)}
 					<li>{@link org.apache.juneau.rest.client.RestCall#headers(Map) headers(Map)}
@@ -6555,7 +6569,7 @@
 			<li>New methods added to {@link org.apache.juneau.rest.client.NameValuePairs}:
 				<ul>
 					<li>{@link org.apache.juneau.rest.client.NameValuePairs#append(String,Object) append(String,Object)}
-					<li>{@link org.apache.juneau.rest.client.NameValuePairs#append(String,Object,UrlEncodingSerializer) append(String,Object,UrlEncodingSerializer)}
+					<li>{@link org.apache.juneau.rest.client.NameValuePairs#append(String,Object,PartSerializer) append(String,Object,PartSerializer)}
 				</ul>
 			<li>{@link org.apache.juneau.rest.client.RetryOn} is now an abstract class with an additional method:
 				<ul>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/NameValuePairs.java
----------------------------------------------------------------------
diff --git a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/NameValuePairs.java b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/NameValuePairs.java
index 845f520..25908de 100644
--- a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/NameValuePairs.java
+++ b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/NameValuePairs.java
@@ -18,6 +18,7 @@ import org.apache.http.*;
 import org.apache.http.client.entity.*;
 import org.apache.http.message.*;
 import org.apache.juneau.internal.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -67,16 +68,15 @@ public final class NameValuePairs extends LinkedList<NameValuePair> {
 	/**
 	 * Appends the specified name/value pair to the end of this list.
 	 * <p>
-	 * The value is converted to UON notation using the {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)} method
-	 * of the specified serializer.
+	 * The value is converted to UON notation using the {@link UrlEncodingSerializer} defined on the client.
 	 *
 	 * @param name The pair name.
 	 * @param value The pair value.
-	 * @param serializer The serializer to use to convert the value to a string.
+	 * @param partSerializer The serializer to use for converting values to simple strings.
 	 * @return This object (for method chaining).
 	 */
-	public NameValuePairs append(String name, Object value, UrlEncodingSerializer serializer) {
-		super.add(new SerializedNameValuePair(name, value, serializer));
+	public NameValuePairs append(String name, Object value, PartSerializer partSerializer) {
+		super.add(new SerializedNameValuePair(name, value, partSerializer));
 		return this;
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
----------------------------------------------------------------------
diff --git a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
index f06a172..ca1f541 100644
--- a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
+++ b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
@@ -58,7 +58,7 @@ import org.apache.juneau.utils.*;
  * 	<li><a class="doclink" href="package-summary.html#RestClient">org.apache.juneau.rest.client &gt; REST client API</a> for more information and code examples.
  * </ul>
  */
-@SuppressWarnings("hiding")
+@SuppressWarnings({ "hiding", "unchecked" })
 public final class RestCall {
 
 	private final RestClient client;                       // The client that created this call.
@@ -177,28 +177,31 @@ public final class RestCall {
 	 * 	Can also be {@link Map}, {@link String}, {@link NameValuePairs}, or bean if the name is null/blank/*.
 	 * 	If a {@link String} and the name is null/blank/*, then calls {@link URIBuilder#setCustomQuery(String)}.
 	 * @param skipIfEmpty Don't add the pair if the value is empty.
+	 * @param partSerializer The part serializer to use to convert the value to a string.
+	 * 	If <jk>null</jk>, then the URL-encoding serializer defined on the client is used.
 	 * @return This object (for method chaining).
 	 * @throws RestCallException
 	 */
-	@SuppressWarnings("unchecked")
-	public RestCall query(String name, Object value, boolean skipIfEmpty) throws RestCallException {
+	public RestCall query(String name, Object value, boolean skipIfEmpty, PartSerializer partSerializer) throws RestCallException {
+		if (partSerializer == null)
+			partSerializer = client.getPartSerializer();
 		if (! ("*".equals(name) || isEmpty(name))) {
 			if (value != null && ! (isEmpty(value) && skipIfEmpty))
-				uriBuilder.addParameter(name, client.getUrlEncodingSerializer().serializePart(value, false, null));
+				uriBuilder.addParameter(name, partSerializer.serialize(PartType.QUERY, value));
 		} else if (value instanceof NameValuePairs) {
 			for (NameValuePair p : (NameValuePairs)value)
-				query(p.getName(), p.getValue(), skipIfEmpty);
+				query(p.getName(), p.getValue(), skipIfEmpty, partSerializer);
 		} else if (value instanceof String) {
 			String s = value.toString();
 			if (! isEmpty(s))
 				uriBuilder.setCustomQuery(s);
 		} else if (value instanceof Map) {
 			for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet())
-				query(p.getKey(), p.getValue(), skipIfEmpty);
+				query(p.getKey(), p.getValue(), skipIfEmpty, partSerializer);
 		} else if (isBean(value)){
-			return query(name, toBeanMap(value), skipIfEmpty);
+			return query(name, toBeanMap(value), skipIfEmpty, partSerializer);
 		} else {
-			throw new RuntimeException("Invalid name passed to query(name,value,skipIfEmpty): ("+name+","+value+","+skipIfEmpty+")");
+			throw new FormattedRuntimeException("Invalid name ''{0}'' passed to query(name,value,skipIfEmpty) for data type ''{1}''", name, ClassUtils.getReadableClassNameForObject(value));
 		}
 		return this;
 	}
@@ -212,7 +215,7 @@ public final class RestCall {
 	 * @throws RestCallException
 	 */
 	public RestCall query(String name, Object value) throws RestCallException {
-		return query(name, value, false);
+		return query(name, value, false, null);
 	}
 
 	/**
@@ -237,7 +240,7 @@ public final class RestCall {
 	 * @throws RestCallException
 	 */
 	public RestCall queryIfNE(String name, Object value) throws RestCallException {
-		return query(name, value, true);
+		return query(name, value, true, null);
 	}
 
 	/**
@@ -250,7 +253,7 @@ public final class RestCall {
 	 * @throws RestCallException
 	 */
 	public RestCall queryIfNE(Map<String,Object> params) throws RestCallException {
-		return query(null, params, true);
+		return query(null, params, true, null);
 	}
 
 	/**
@@ -272,27 +275,30 @@ public final class RestCall {
 	 * @param value The parameter value converted to a string using UON notation.
 	 * 	Can also be {@link Map}, {@link NameValuePairs}, or bean if the name is null/blank/*.
 	 * @param skipIfEmpty Don't add the pair if the value is empty.
+	 * @param partSerializer The part serializer to use to convert the value to a string.
+	 * 	If <jk>null</jk>, then the URL-encoding serializer defined on the client is used.
 	 * @return This object (for method chaining).
 	 * @throws RestCallException
 	 */
-	@SuppressWarnings("unchecked")
-	public RestCall formData(String name, Object value, boolean skipIfEmpty) throws RestCallException {
+	public RestCall formData(String name, Object value, boolean skipIfEmpty, PartSerializer partSerializer) throws RestCallException {
 		if (formData == null)
 			formData = new NameValuePairs();
+		if (partSerializer == null)
+			partSerializer = client.getPartSerializer();
 		if (! ("*".equals(name) || isEmpty(name))) {
 			if (value != null && ! (isEmpty(value) && skipIfEmpty))
-				formData.add(new SerializedNameValuePair(name, value, client.getUrlEncodingSerializer()));
+				formData.add(new SerializedNameValuePair(name, value, partSerializer));
 		} else if (value instanceof NameValuePairs) {
 			for (NameValuePair p : (NameValuePairs)value)
 				if (! (isEmpty(p.getValue()) && skipIfEmpty))
 					formData.add(p);
 		} else if (value instanceof Map) {
 			for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet())
-				formData(p.getKey(), p.getValue(), skipIfEmpty);
+				formData(p.getKey(), p.getValue(), skipIfEmpty, partSerializer);
 		} else if (isBean(value)) {
-			return formData(name, toBeanMap(value), skipIfEmpty);
+			return formData(name, toBeanMap(value), skipIfEmpty, partSerializer);
 		} else {
-			throw new RuntimeException("Invalid name passed to formData(name,value,skipIfEmpty).");
+			throw new FormattedRuntimeException("Invalid name ''{0}'' passed to formData(name,value,skipIfEmpty) for data type ''{1}''", name, ClassUtils.getReadableClassNameForObject(value));
 		}
 		return this;
 	}
@@ -308,7 +314,7 @@ public final class RestCall {
 	 * @throws RestCallException If name was null/blank and value wasn't a {@link Map} or {@link NameValuePairs}.
 	 */
 	public RestCall formData(String name, Object value) throws RestCallException {
-		return formData(name, value, false);
+		return formData(name, value, false, null);
 	}
 
 	/**
@@ -344,7 +350,7 @@ public final class RestCall {
 	 * @throws RestCallException
 	 */
 	public RestCall formDataIfNE(String name, Object value) throws RestCallException {
-		return formData(name, value, true);
+		return formData(name, value, true, null);
 	}
 
 	/**
@@ -357,25 +363,28 @@ public final class RestCall {
 	 * @throws RestCallException
 	 */
 	public RestCall formDataIfNE(Map<String,Object> params) throws RestCallException {
-		return formData(null, params, true);
+		return formData(null, params, true, null);
 	}
 
 	/**
 	 * Replaces a variable of the form <js>"{name}"</js> in the URL path with the specified value.
+	 *
 	 * @param name The path variable name.
 	 * @param value The replacement value.
-	 *
+	 * @param partSerializer The part serializer to use to convert the value to a string.
+	 * 	If <jk>null</jk>, then the URL-encoding serializer defined on the client is used.
 	 * @return This object (for method chaining).
 	 * @throws RestCallException If variable could not be found in path.
 	 */
-	@SuppressWarnings("unchecked")
-	public RestCall path(String name, Object value) throws RestCallException {
+	public RestCall path(String name, Object value, PartSerializer partSerializer) throws RestCallException {
 		String path = uriBuilder.getPath();
+		if (partSerializer == null)
+			partSerializer = client.getPartSerializer();
 		if (! ("*".equals(name) || isEmpty(name))) {
 			String var = "{" + name + "}";
 			if (path.indexOf(var) == -1)
 				throw new RestCallException("Path variable {"+name+"} was not found in path.");
-			String newPath = path.replace(var, client.getUrlEncodingSerializer().serializePart(value, false, null));
+			String newPath = path.replace(var, partSerializer.serialize(PartType.PATH, value));
 			uriBuilder.setPath(newPath);
 		} else if (value instanceof NameValuePairs) {
 			for (NameValuePair p : (NameValuePairs)value)
@@ -386,12 +395,24 @@ public final class RestCall {
 		} else if (isBean(value)) {
 			return path(name, toBeanMap(value));
 		} else {
-			throw new RuntimeException("Invalid name passed to path(name,value).");
+			throw new FormattedRuntimeException("Invalid name ''{0}'' passed to path(name,value) for data type ''{1}''", name, ClassUtils.getReadableClassNameForObject(value));
 		}
 		return this;
 	}
 
 	/**
+	 * Replaces a variable of the form <js>"{name}"</js> in the URL path with the specified value.
+	 *
+	 * @param name The path variable name.
+	 * @param value The replacement value.
+	 * @return This object (for method chaining).
+	 * @throws RestCallException If variable could not be found in path.
+	 */
+	public RestCall path(String name, Object value) throws RestCallException {
+		return path(name, value, null);
+	}
+
+	/**
 	 * Sets the URI user info.
 	 *
 	 * @param userInfo The new URI user info.
@@ -473,24 +494,27 @@ public final class RestCall {
 	 * The name can be null/empty if the value is a {@link Map}.
 	 * @param value The header value.
 	 * @param skipIfEmpty Don't add the header if the name is null/empty.
+	 * @param partSerializer The part serializer to use to convert the value to a string.
+	 * 	If <jk>null</jk>, then the URL-encoding serializer defined on the client is used.
 	 * @return This object (for method chaining).
 	 * @throws RestCallException
 	 */
-	@SuppressWarnings("unchecked")
-	public RestCall header(String name, Object value, boolean skipIfEmpty) throws RestCallException {
+	public RestCall header(String name, Object value, boolean skipIfEmpty, PartSerializer partSerializer) throws RestCallException {
+		if (partSerializer == null)
+			partSerializer = client.getPartSerializer();
 		if (! ("*".equals(name) || isEmpty(name))) {
 			if (value != null && ! (isEmpty(value) && skipIfEmpty))
-				request.setHeader(name, client.getUrlEncodingSerializer().serializePart(value, false, true));
+				request.setHeader(name, partSerializer.serialize(PartType.HEADER, value));
 		} else if (value instanceof NameValuePairs) {
 			for (NameValuePair p : (NameValuePairs)value)
-				header(p.getName(), p.getValue(), skipIfEmpty);
+				header(p.getName(), p.getValue(), skipIfEmpty, partSerializer);
 		} else if (value instanceof Map) {
 			for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet())
-				header(p.getKey(), p.getValue(), skipIfEmpty);
+				header(p.getKey(), p.getValue(), skipIfEmpty, partSerializer);
 		} else if (isBean(value)) {
-			return header(name, toBeanMap(value), skipIfEmpty);
+			return header(name, toBeanMap(value), skipIfEmpty, partSerializer);
 		} else {
-			throw new RuntimeException("Invalid name passed to header(name,value,skipIfEmpty).");
+			throw new FormattedRuntimeException("Invalid name ''{0}'' passed to header(name,value,skipIfEmpty) for data type ''{1}''", name, ClassUtils.getReadableClassNameForObject(value));
 		}
 		return this;
 	}
@@ -506,7 +530,7 @@ public final class RestCall {
 	 * @throws RestCallException
 	 */
 	public RestCall header(String name, Object value) throws RestCallException {
-		return header(name, value, false);
+		return header(name, value, false, null);
 	}
 
 	/**
@@ -517,7 +541,7 @@ public final class RestCall {
 	 * @throws RestCallException
 	 */
 	public RestCall headers(Map<String,Object> values) throws RestCallException {
-		return header(null, values, false);
+		return header(null, values, false, null);
 	}
 
 	/**
@@ -532,7 +556,7 @@ public final class RestCall {
 	 * @throws RestCallException
 	 */
 	public RestCall headerIfNE(String name, Object value) throws RestCallException {
-		return header(name, value, true);
+		return header(name, value, true, null);
 	}
 
 	/**
@@ -545,7 +569,7 @@ public final class RestCall {
 	 * @throws RestCallException
 	 */
 	public RestCall headersIfNE(Map<String,Object> values) throws RestCallException {
-		return header(null, values, true);
+		return header(null, values, true, null);
 	}
 
 	/**
@@ -1735,7 +1759,6 @@ public final class RestCall {
 	 * @throws IOException If a connection error occurred.
 	 * @see BeanSession#getClassMeta(Class) for argument syntax for maps and collections.
 	 */
-	@SuppressWarnings("unchecked")
 	public <T> T getResponse(Type type, Type...args) throws IOException, ParseException {
 		BeanContext bc = getParser().getBeanContext();
 		if (bc == null)
@@ -1795,7 +1818,6 @@ public final class RestCall {
 		return getResponsePojoRest(ObjectMap.class);
 	}
 
-	@SuppressWarnings("unchecked")
 	<T> T getResponse(ClassMeta<T> type) throws IOException, ParseException {
 		try {
 			if (type.getInnerClass().equals(HttpResponse.class))

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
----------------------------------------------------------------------
diff --git a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
index dbcc294..f863487 100644
--- a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
+++ b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
@@ -52,12 +52,16 @@ import org.apache.juneau.urlencoding.*;
  * 	<li><a class="doclink" href="package-summary.html#RestClient">org.apache.juneau.rest.client &gt; REST client API</a> for more information and code examples.
  * </ul>
  */
+@SuppressWarnings("rawtypes")
 public class RestClient extends CoreObject {
 
+	private static final ConcurrentHashMap<Class,PartSerializer> partSerializerCache = new ConcurrentHashMap<Class,PartSerializer>();
+
 	private final Map<String,String> headers;
 	private final CloseableHttpClient httpClient;
 	private final boolean keepHttpClientOpen;
 	private final UrlEncodingSerializer urlEncodingSerializer;  // Used for form posts only.
+	private final PartSerializer partSerializer;
 	private final String rootUrl;
 	private volatile boolean isClosed = false;
 	private final StackTraceElement[] creationStack;
@@ -84,6 +88,7 @@ public class RestClient extends CoreObject {
 			Serializer serializer,
 			Parser parser,
 			UrlEncodingSerializer urlEncodingSerializer,
+			PartSerializer partSerializer,
 			Map<String,String> headers,
 			List<RestCallInterceptor> interceptors,
 			String rootUri,
@@ -99,6 +104,7 @@ public class RestClient extends CoreObject {
 		this.serializer = serializer;
 		this.parser = parser;
 		this.urlEncodingSerializer = urlEncodingSerializer;
+		this.partSerializer = partSerializer;
 
 		Map<String,String> h2 = new ConcurrentHashMap<String,String>(headers);
 
@@ -538,16 +544,16 @@ public class RestClient extends CoreObject {
 							rc.serializer(serializer).parser(parser);
 
 							for (RemoteMethodArg a : rmm.getPathArgs())
-								rc.path(a.name, args[a.index]);
+								rc.path(a.name, args[a.index], a.serializer);
 
 							for (RemoteMethodArg a : rmm.getQueryArgs())
-								rc.query(a.name, args[a.index], a.skipIfNE);
+								rc.query(a.name, args[a.index], a.skipIfNE, a.serializer);
 
 							for (RemoteMethodArg a : rmm.getFormDataArgs())
-								rc.formData(a.name, args[a.index], a.skipIfNE);
+								rc.formData(a.name, args[a.index], a.skipIfNE, a.serializer);
 
 							for (RemoteMethodArg a : rmm.getHeaderArgs())
-								rc.header(a.name, args[a.index], a.skipIfNE);
+								rc.header(a.name, args[a.index], a.skipIfNE, a.serializer);
 
 							if (rmm.getBodyArg() != null)
 								rc.input(args[rmm.getBodyArg()]);
@@ -563,31 +569,31 @@ public class RestClient extends CoreObject {
 
 										Path p = pMeta.getAnnotation(Path.class);
 										if (p != null)
-											rc.path(getName(p.value(), pMeta), val);
+											rc.path(getName(p.value(), pMeta), val, getPartSerializer(p.serializer()));
 
 										Query q1 = pMeta.getAnnotation(Query.class);
 										if (q1 != null)
-											rc.query(getName(q1.value(), pMeta), val, false);
+											rc.query(getName(q1.value(), pMeta), val, false, getPartSerializer(q1.serializer()));
 
 										QueryIfNE q2 = pMeta.getAnnotation(QueryIfNE.class);
 										if (q2 != null)
-											rc.query(getName(q2.value(), pMeta), val, true);
+											rc.query(getName(q2.value(), pMeta), val, true, getPartSerializer(q2.serializer()));
 
 										FormData f1 = pMeta.getAnnotation(FormData.class);
 										if (f1 != null)
-											rc.formData(getName(f1.value(), pMeta), val, false);
+											rc.formData(getName(f1.value(), pMeta), val, false, getPartSerializer(f1.serializer()));
 
 										FormDataIfNE f2 = pMeta.getAnnotation(FormDataIfNE.class);
 										if (f2 != null)
-											rc.formData(getName(f2.value(), pMeta), val, true);
+											rc.formData(getName(f2.value(), pMeta), val, true, getPartSerializer(f2.serializer()));
 
 										org.apache.juneau.remoteable.Header h1 = pMeta.getAnnotation(org.apache.juneau.remoteable.Header.class);
 										if (h1 != null)
-											rc.header(getName(h1.value(), pMeta), val, false);
+											rc.header(getName(h1.value(), pMeta), val, false, getPartSerializer(h1.serializer()));
 
 										HeaderIfNE h2 = pMeta.getAnnotation(HeaderIfNE.class);
 										if (h2 != null)
-											rc.header(getName(h2.value(), pMeta), val, true);
+											rc.header(getName(h2.value(), pMeta), val, true, getPartSerializer(h2.serializer()));
 									}
 								}
 							}
@@ -622,10 +628,25 @@ public class RestClient extends CoreObject {
 		return name;
 	}
 
+	private static PartSerializer getPartSerializer(Class c) {
+		if (c == UrlEncodingSerializer.class)
+			return null;
+		PartSerializer pf = partSerializerCache.get(c);
+		if (pf == null) {
+			try {
+				partSerializerCache.putIfAbsent(c, (PartSerializer)c.newInstance());
+			} catch (Exception e) {
+				throw new RuntimeException(e);
+			}
+			pf = partSerializerCache.get(c);
+		}
+		return pf;
+	}
+
 	private Pattern absUrlPattern = Pattern.compile("^\\w+\\:\\/\\/.*");
 
-	UrlEncodingSerializer getUrlEncodingSerializer() {
-		return urlEncodingSerializer;
+	PartSerializer getPartSerializer() {
+		return partSerializer;
 	}
 
 	URI toURI(Object url) throws URISyntaxException {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
index 83961bc..76bec4f 100644
--- a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
+++ b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
@@ -51,7 +51,7 @@ import org.apache.juneau.urlencoding.*;
 /**
  * Builder class for the {@link RestClient} class.
  */
-@SuppressWarnings("hiding")
+@SuppressWarnings({"hiding"})
 public class RestClientBuilder extends CoreObjectBuilder {
 
 	private HttpClientConnectionManager httpClientConnectionManager;
@@ -61,8 +61,10 @@ public class RestClientBuilder extends CoreObjectBuilder {
 
 	private Class<? extends Serializer> serializerClass = JsonSerializer.class;
 	private Class<? extends Parser> parserClass = JsonParser.class;
+	private Class<? extends PartSerializer> partSerializerClass = UrlEncodingSerializer.class;
 	private Serializer serializer;
 	private Parser parser;
+	private PartSerializer partSerializer;
 
 	private Map<String,String> headers = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
 
@@ -140,7 +142,17 @@ public class RestClientBuilder extends CoreObjectBuilder {
 
 			UrlEncodingSerializer us = new SerializerBuilder(propertyStore).build(UrlEncodingSerializer.class);
 
-			return new RestClient(propertyStore, httpClient, keepHttpClientOpen, s, p, us, headers, interceptors, rootUrl, retryOn, retries, retryInterval, debug, executorService, executorServiceShutdownOnClose);
+			PartSerializer pf = null;
+			if (partSerializer != null)
+				pf = partSerializer;
+			else if (partSerializerClass != null) {
+				if (partSerializerClass == UrlEncodingSerializer.class)
+					pf = us;
+				else
+					pf = partSerializerClass.newInstance();
+			}
+
+			return new RestClient(propertyStore, httpClient, keepHttpClientOpen, s, p, us, pf, headers, interceptors, rootUrl, retryOn, retries, retryInterval, debug, executorService, executorServiceShutdownOnClose);
 		} catch (Exception e) {
 			throw new RuntimeException(e);
 		}
@@ -383,6 +395,31 @@ public class RestClientBuilder extends CoreObjectBuilder {
 	}
 
 	/**
+	 * Sets the part serializer to use for converting POJOs to headers, query parameters, form-data parameters, and
+	 * path variables.
+	 *
+	 * @param partSerializer The part serializer instance.
+	 * @return This object (for method chaining).
+	 */
+	public RestClientBuilder partSerializer(PartSerializer partSerializer) {
+		this.partSerializer = partSerializer;
+		return this;
+	}
+
+	/**
+	 * Sets the part formatter to use for converting POJOs to headers, query parameters, form-data parameters, and
+	 * path variables.
+	 *
+	 * @param partSerializerClass The part serializer class.
+	 * 	The class must have a no-arg constructor.
+	 * @return This object (for method chaining).
+	 */
+	public RestClientBuilder partSerializer(Class<? extends PartSerializer> partSerializerClass) {
+		this.partSerializerClass = partSerializerClass;
+		return this;
+	}
+
+	/**
 	 * Set up this client to use BASIC auth.
 	 *
 	 * @param host The auth scope hostname.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/SerializedNameValuePair.java
----------------------------------------------------------------------
diff --git a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/SerializedNameValuePair.java b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/SerializedNameValuePair.java
index 11382a6..0e6be73 100644
--- a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/SerializedNameValuePair.java
+++ b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/SerializedNameValuePair.java
@@ -13,6 +13,7 @@
 package org.apache.juneau.rest.client;
 
 import org.apache.http.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -30,7 +31,7 @@ import org.apache.juneau.urlencoding.*;
 public final class SerializedNameValuePair implements NameValuePair {
 	private String name;
 	private Object value;
-	private UrlEncodingSerializer serializer;
+	private PartSerializer serializer;
 
 	/**
 	 * Constructor.
@@ -39,7 +40,7 @@ public final class SerializedNameValuePair implements NameValuePair {
 	 * @param value The POJO to serialize to the parameter value.
 	 * @param serializer The serializer to use to convert the value to a string.
 	 */
-	public SerializedNameValuePair(String name, Object value, UrlEncodingSerializer serializer) {
+	public SerializedNameValuePair(String name, Object value, PartSerializer serializer) {
 		this.name = name;
 		this.value = value;
 		this.serializer = serializer;
@@ -47,11 +48,11 @@ public final class SerializedNameValuePair implements NameValuePair {
 
 	@Override /* NameValuePair */
 	public String getName() {
-		return serializer.serializePart(name, false, null);
+		return name;
 	}
 
 	@Override /* NameValuePair */
 	public String getValue() {
-		return serializer.serializePart(value, false, null);
+		return serializer.serialize(PartType.FORM_DATA, value);
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ThirdPartyProxyResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ThirdPartyProxyResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ThirdPartyProxyResource.java
index 532f292..d613422 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ThirdPartyProxyResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ThirdPartyProxyResource.java
@@ -1686,4 +1686,38 @@ public class ThirdPartyProxyResource extends ResourceJena {
 		assertClass(TestEnum.class, e.getKey());
 		assertClass(TestEnum[][][].class, e.getValue().get(0));
 	}
+
+	//--------------------------------------------------------------------------------
+	// PartFormatter tests
+	//--------------------------------------------------------------------------------
+
+	@RestMethod(name="POST", path="/partFormatters/{p1}")
+	public String partFormatter(
+		@Path("p1") String p1,
+		@Header("h1") String h1,
+		@Header("h2") String h2,
+		@Header("h3") String h3,
+		@Query("q1") String q1,
+		@Query("q2") String q2,
+		@Query("q3") String q3,
+		@FormData("f1") String f1,
+		@FormData("f2") String f2,
+		@FormData("f3") String f3
+	) throws Exception {
+
+		assertEquals("dummy-1", p1);
+
+		assertEquals("dummy-2", h1);
+		assertEquals("dummy-3", h2);
+		assertNull(h3);
+		assertEquals("dummy-4", q1);
+		assertEquals("dummy-5", q2);
+		assertNull(q3);
+		assertEquals("dummy-6", f1);
+		assertEquals("dummy-7", f2);
+		assertNull(f3);
+
+		return "OK";
+	}
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ThirdPartyProxyTest.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ThirdPartyProxyTest.java b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ThirdPartyProxyTest.java
index 59868e6..6cc497d 100644
--- a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ThirdPartyProxyTest.java
+++ b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ThirdPartyProxyTest.java
@@ -2137,6 +2137,15 @@ public class ThirdPartyProxyTest extends RestTestcase {
 		assertEquals("OK", r);
 	}
 
+	//--------------------------------------------------------------------------------
+	// PartFormatters
+	//--------------------------------------------------------------------------------
+	@Test
+	public void h01() throws Exception {
+		String r = proxy.partFormatters("1", "2", "3", "", "4", "5", "", "6", "7", "");
+		assertEquals("OK", r);
+	}
+
 
 	//--------------------------------------------------------------------------------
 	// Proxy class
@@ -3373,6 +3382,24 @@ public class ThirdPartyProxyTest extends RestTestcase {
 		}
 
 		//--------------------------------------------------------------------------------
+		// PartFormatters
+		//--------------------------------------------------------------------------------
+
+		@RemoteMethod(httpMethod="POST", path="/partFormatters/{p1}")
+		String partFormatters(
+			@Path(value="p1", serializer=DummyPartSerializer.class) String p1,
+			@Header(value="h1", serializer=DummyPartSerializer.class) String h1,
+			@HeaderIfNE(value="h2", serializer=DummyPartSerializer.class) String h2,
+			@HeaderIfNE(value="h3", serializer=DummyPartSerializer.class) String h3,
+			@Query(value="q1", serializer=DummyPartSerializer.class) String q1,
+			@QueryIfNE(value="q2", serializer=DummyPartSerializer.class) String q2,
+			@QueryIfNE(value="q3", serializer=DummyPartSerializer.class) String q3,
+			@FormData(value="f1", serializer=DummyPartSerializer.class) String f1,
+			@FormDataIfNE(value="f2", serializer=DummyPartSerializer.class) String f2,
+			@FormDataIfNE(value="f3", serializer=DummyPartSerializer.class) String f3
+		);
+
+		//--------------------------------------------------------------------------------
 		// Test return types.
 		//--------------------------------------------------------------------------------
 
@@ -3701,4 +3728,11 @@ public class ThirdPartyProxyTest extends RestTestcase {
 			return this;
 		}
 	}
+
+	public static class DummyPartSerializer implements PartSerializer {
+		@Override
+		public String serialize(PartType type, Object value) {
+			return "dummy-"+value;
+		}
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java b/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
index 973452a..dfff199 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
@@ -15,6 +15,7 @@ package org.apache.juneau.rest;
 import java.net.*;
 import java.text.*;
 
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -132,13 +133,13 @@ public final class Redirect {
 	/**
 	 * Calculates the URL to redirect to.
 	 *
-	 * @param s Use this serializer to encode arguments using the {@link UrlEncodingSerializer#serializePart(Object,Boolean,Boolean)} method.
+	 * @param s Use this serializer to encode arguments using the {@link UrlEncodingSerializer#serialize(PartType,Object)} method.
 	 * @return The URL to redirect to.
 	 */
 	public String toUrl(UrlEncodingSerializer s) {
 		if (url != null && args != null && args.length > 0) {
 			for (int i = 0; i < args.length; i++)
-				args[i] = s.serializePart(args[i], null, true);
+				args[i] = s.serialize(PartType.PATH, args[i]);
 			return MessageFormat.format(url, args);
 		}
 		return url;