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

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


The following commit(s) were added to refs/heads/master by this push:
     new a65aedf  Support for POJO builders.
a65aedf is described below

commit a65aedfdace05de4aae83e8c963bdb798a52ee72
Author: JamesBognar <[email protected]>
AuthorDate: Sun Jan 28 21:13:22 2018 -0500

    Support for POJO builders.
---
 .../a/rttests/RoundTripBeanInheritanceTest.java    |  11 +-
 .../apache/juneau/transforms/BuilderComboTest.java | 446 +++++++++++++++++++++
 .../org/apache/juneau/jena/RdfParserSession.java   |  15 +-
 .../src/main/java/org/apache/juneau/BeanMap.java   |  30 +-
 .../src/main/java/org/apache/juneau/BeanMeta.java  |  34 +-
 .../java/org/apache/juneau/BeanPropertyMeta.java   |  39 +-
 .../src/main/java/org/apache/juneau/ClassMeta.java |  25 +-
 .../java/org/apache/juneau/annotation/Builder.java |  71 ++++
 .../apache/juneau/csv/CsvSerializerSession.java    |  16 +-
 .../org/apache/juneau/html/HtmlParserSession.java  |  18 +-
 .../apache/juneau/html/HtmlSerializerSession.java  |  48 +--
 .../org/apache/juneau/internal/ClassUtils.java     |  41 +-
 .../org/apache/juneau/json/JsonParserSession.java  |  12 +-
 .../juneau/json/JsonSchemaSerializerSession.java   |   3 +-
 .../apache/juneau/json/JsonSerializerSession.java  |  28 +-
 .../juneau/msgpack/MsgPackParserSession.java       |  15 +-
 .../juneau/msgpack/MsgPackSerializerSession.java   |  20 +-
 .../main/java/org/apache/juneau/parser/Parser.java |   8 +-
 .../java/org/apache/juneau/transform/Builder.java  |  30 ++
 .../org/apache/juneau/transform/BuilderSwap.java   | 231 +++++++++++
 .../org/apache/juneau/uon/UonParserSession.java    |  14 +-
 .../apache/juneau/uon/UonSerializerSession.java    |  28 +-
 .../urlencoding/UrlEncodingParserSession.java      |  13 +-
 .../urlencoding/UrlEncodingSerializerSession.java  |  58 +--
 .../org/apache/juneau/xml/XmlParserSession.java    |  20 +-
 .../juneau/xml/XmlSchemaSerializerSession.java     | 118 +++---
 .../apache/juneau/xml/XmlSerializerSession.java    | 138 ++++---
 .../juneau/yaml/proto/YamlParserSession.java       |  12 +-
 .../juneau/yaml/proto/YamlSerializerSession.java   |  28 +-
 juneau-doc/src/main/javadoc/overview.html          | 140 ++++++-
 30 files changed, 1398 insertions(+), 312 deletions(-)

diff --git 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanInheritanceTest.java
 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanInheritanceTest.java
index dfe8dd6..925fd90 100755
--- 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanInheritanceTest.java
+++ 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanInheritanceTest.java
@@ -13,9 +13,7 @@
 package org.apache.juneau.a.rttests;
 
 import static org.apache.juneau.TestUtils.*;
-import static org.junit.Assert.*;
 
-import org.apache.juneau.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.serializer.*;
 import org.junit.*;
@@ -48,14 +46,7 @@ public class RoundTripBeanInheritanceTest extends 
RoundTripTest {
 
                A3 t3 = new A3();
                t3.init();
-               try {
-                       ClassMeta<?> cm = 
BeanContext.DEFAULT.getClassMeta(A3.class);
-                       assertEquals("No properties detected on bean class", 
cm.getNotABeanReason());
-                       roundTrip(t3, A3.class);
-                       fail("Exception expected");
-               } catch (ParseException e) {
-               } catch (SerializeException e) {
-               } catch (InvalidDataConversionException e) {}
+               roundTrip(t3, A3.class);
        }
 
 
diff --git 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/BuilderComboTest.java
 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/BuilderComboTest.java
new file mode 100644
index 0000000..e0a14cf
--- /dev/null
+++ 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/BuilderComboTest.java
@@ -0,0 +1,446 @@
+// 
***************************************************************************************************************************
+// * 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.transforms;
+
+import static org.apache.juneau.TestUtils.*;
+import static org.junit.Assert.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.serializer.*;
+import org.junit.runner.*;
+import org.junit.runners.*;
+
+/**
+ * Exhaustive serialization tests for the CalendarSwap class.
+ */
+@RunWith(Parameterized.class)
+@SuppressWarnings({"javadoc"})
+public class BuilderComboTest extends ComboRoundTripTest {
+
+       @Parameterized.Parameters
+       public static Collection<Object[]> getParameters() {
+               return Arrays.asList(new Object[][] {
+                       {       /* 0 */
+                               new ComboInput<A>(
+                                       "A",
+                                       A.class,
+                                       new A(null).init(),
+                                       /* Json */              "{a:1}",
+                                       /* JsonT */             "{a:1}",
+                                       /* JsonR */             "{\n\ta: 1\n}",
+                                       /* Xml */               
"<object><a>1</a></object>",
+                                       /* XmlT */              
"<object><a>1</a></object>",
+                                       /* XmlR */              
"<object>\n\t<a>1</a>\n</object>\n",
+                                       /* XmlNs */             
"<object><a>1</a></object>",
+                                       /* Html */              
"<table><tr><td>a</td><td>1</td></tr></table>",
+                                       /* HtmlT */             
"<table><tr><td>a</td><td>1</td></tr></table>",
+                                       /* HtmlR */             
"<table>\n\t<tr>\n\t\t<td>a</td>\n\t\t<td>1</td>\n\t</tr>\n</table>\n",
+                                       /* Uon */               "(a=1)",
+                                       /* UonT */              "(a=1)",
+                                       /* UonR */              "(\n\ta=1\n)",
+                                       /* UrlEnc */            "a=1",
+                                       /* UrlEncT */   "a=1",
+                                       /* UrlEncR */   "a=1",
+                                       /* MsgPack */   "81A16101",
+                                       /* MsgPackT */  "81A16101",
+                                       /* RdfXml */            
"<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+                                       /* RdfXmlT */   
"<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+                                       /* RdfXmlR */   "<rdf:RDF>\n  
<rdf:Description>\n    <jp:a>1</jp:a>\n  </rdf:Description>\n</rdf:RDF>\n"
+                               )
+                               {
+                                       @Override
+                                       public void verify(A o) {
+                                               assertType(A.class, o);
+                                               assertTrue(o.createdByBuilder);
+                                       }
+                               }
+                       },
+                       {       /* 1 */
+                               new ComboInput<B>(
+                                       "B",
+                                       B.class,
+                                       new B(null).init(),
+                                       /* Json */              "{a:1}",
+                                       /* JsonT */             "{a:1}",
+                                       /* JsonR */             "{\n\ta: 1\n}",
+                                       /* Xml */               
"<object><a>1</a></object>",
+                                       /* XmlT */              
"<object><a>1</a></object>",
+                                       /* XmlR */              
"<object>\n\t<a>1</a>\n</object>\n",
+                                       /* XmlNs */             
"<object><a>1</a></object>",
+                                       /* Html */              
"<table><tr><td>a</td><td>1</td></tr></table>",
+                                       /* HtmlT */             
"<table><tr><td>a</td><td>1</td></tr></table>",
+                                       /* HtmlR */             
"<table>\n\t<tr>\n\t\t<td>a</td>\n\t\t<td>1</td>\n\t</tr>\n</table>\n",
+                                       /* Uon */               "(a=1)",
+                                       /* UonT */              "(a=1)",
+                                       /* UonR */              "(\n\ta=1\n)",
+                                       /* UrlEnc */            "a=1",
+                                       /* UrlEncT */   "a=1",
+                                       /* UrlEncR */   "a=1",
+                                       /* MsgPack */   "81A16101",
+                                       /* MsgPackT */  "81A16101",
+                                       /* RdfXml */            
"<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+                                       /* RdfXmlT */   
"<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+                                       /* RdfXmlR */   "<rdf:RDF>\n  
<rdf:Description>\n    <jp:a>1</jp:a>\n  </rdf:Description>\n</rdf:RDF>\n"
+                               )
+                               {
+                                       @Override
+                                       public void verify(B o) {
+                                               assertType(B.class, o);
+                                               assertTrue(o.createdByBuilder);
+                                       }
+                               }
+                       },
+                       {       /* 2 */
+                               new ComboInput<C>(
+                                       "C",
+                                       C.class,
+                                       new C(null).init(),
+                                       /* Json */              "{a:1}",
+                                       /* JsonT */             "{a:1}",
+                                       /* JsonR */             "{\n\ta: 1\n}",
+                                       /* Xml */               
"<object><a>1</a></object>",
+                                       /* XmlT */              
"<object><a>1</a></object>",
+                                       /* XmlR */              
"<object>\n\t<a>1</a>\n</object>\n",
+                                       /* XmlNs */             
"<object><a>1</a></object>",
+                                       /* Html */              
"<table><tr><td>a</td><td>1</td></tr></table>",
+                                       /* HtmlT */             
"<table><tr><td>a</td><td>1</td></tr></table>",
+                                       /* HtmlR */             
"<table>\n\t<tr>\n\t\t<td>a</td>\n\t\t<td>1</td>\n\t</tr>\n</table>\n",
+                                       /* Uon */               "(a=1)",
+                                       /* UonT */              "(a=1)",
+                                       /* UonR */              "(\n\ta=1\n)",
+                                       /* UrlEnc */            "a=1",
+                                       /* UrlEncT */   "a=1",
+                                       /* UrlEncR */   "a=1",
+                                       /* MsgPack */   "81A16101",
+                                       /* MsgPackT */  "81A16101",
+                                       /* RdfXml */            
"<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+                                       /* RdfXmlT */   
"<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+                                       /* RdfXmlR */   "<rdf:RDF>\n  
<rdf:Description>\n    <jp:a>1</jp:a>\n  </rdf:Description>\n</rdf:RDF>\n"
+                               )
+                               {
+                                       @Override
+                                       public void verify(C o) {
+                                               assertType(C.class, o);
+                                               assertTrue(o.createdByBuilder);
+                                       }
+                               }
+                       },
+                       {       /* 3 */
+                               new ComboInput<D>(
+                                       "D",
+                                       D.class,
+                                       new D(null).init(),
+                                       /* Json */              "{a:1}",
+                                       /* JsonT */             "{a:1}",
+                                       /* JsonR */             "{\n\ta: 1\n}",
+                                       /* Xml */               
"<object><a>1</a></object>",
+                                       /* XmlT */              
"<object><a>1</a></object>",
+                                       /* XmlR */              
"<object>\n\t<a>1</a>\n</object>\n",
+                                       /* XmlNs */             
"<object><a>1</a></object>",
+                                       /* Html */              
"<table><tr><td>a</td><td>1</td></tr></table>",
+                                       /* HtmlT */             
"<table><tr><td>a</td><td>1</td></tr></table>",
+                                       /* HtmlR */             
"<table>\n\t<tr>\n\t\t<td>a</td>\n\t\t<td>1</td>\n\t</tr>\n</table>\n",
+                                       /* Uon */               "(a=1)",
+                                       /* UonT */              "(a=1)",
+                                       /* UonR */              "(\n\ta=1\n)",
+                                       /* UrlEnc */            "a=1",
+                                       /* UrlEncT */   "a=1",
+                                       /* UrlEncR */   "a=1",
+                                       /* MsgPack */   "81A16101",
+                                       /* MsgPackT */  "81A16101",
+                                       /* RdfXml */            
"<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+                                       /* RdfXmlT */   
"<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+                                       /* RdfXmlR */   "<rdf:RDF>\n  
<rdf:Description>\n    <jp:a>1</jp:a>\n  </rdf:Description>\n</rdf:RDF>\n"
+                               )
+                               {
+                                       @Override
+                                       public void verify(D o) {
+                                               assertType(D.class, o);
+                                               assertTrue(o.createdByBuilder);
+                                       }
+                               }
+                       },
+                       {       /* 4 */
+                               new ComboInput<E>(
+                                       "E",
+                                       E.class,
+                                       new E(null).init(),
+                                       /* Json */              "{a:1}",
+                                       /* JsonT */             "{a:1}",
+                                       /* JsonR */             "{\n\ta: 1\n}",
+                                       /* Xml */               
"<object><a>1</a></object>",
+                                       /* XmlT */              
"<object><a>1</a></object>",
+                                       /* XmlR */              
"<object>\n\t<a>1</a>\n</object>\n",
+                                       /* XmlNs */             
"<object><a>1</a></object>",
+                                       /* Html */              
"<table><tr><td>a</td><td>1</td></tr></table>",
+                                       /* HtmlT */             
"<table><tr><td>a</td><td>1</td></tr></table>",
+                                       /* HtmlR */             
"<table>\n\t<tr>\n\t\t<td>a</td>\n\t\t<td>1</td>\n\t</tr>\n</table>\n",
+                                       /* Uon */               "(a=1)",
+                                       /* UonT */              "(a=1)",
+                                       /* UonR */              "(\n\ta=1\n)",
+                                       /* UrlEnc */            "a=1",
+                                       /* UrlEncT */   "a=1",
+                                       /* UrlEncR */   "a=1",
+                                       /* MsgPack */   "81A16101",
+                                       /* MsgPackT */  "81A16101",
+                                       /* RdfXml */            
"<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+                                       /* RdfXmlT */   
"<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+                                       /* RdfXmlR */   "<rdf:RDF>\n  
<rdf:Description>\n    <jp:a>1</jp:a>\n  </rdf:Description>\n</rdf:RDF>\n"
+                               )
+                               {
+                                       @Override
+                                       public void verify(E o) {
+                                               assertType(E.class, o);
+                                               assertTrue(o.createdByBuilder);
+                                       }
+                               }
+                       },
+                       {       /* 5 */
+                               new ComboInput<H>(
+                                       "H",
+                                       H.class,
+                                       new H(null).init(),
+                                       /* Json */              "{fooBar:1}",
+                                       /* JsonT */             "{fooBar:1}",
+                                       /* JsonR */             "{\n\tfooBar: 
1\n}",
+                                       /* Xml */               
"<object><fooBar>1</fooBar></object>",
+                                       /* XmlT */              
"<object><fooBar>1</fooBar></object>",
+                                       /* XmlR */              
"<object>\n\t<fooBar>1</fooBar>\n</object>\n",
+                                       /* XmlNs */             
"<object><fooBar>1</fooBar></object>",
+                                       /* Html */              
"<table><tr><td>fooBar</td><td>1</td></tr></table>",
+                                       /* HtmlT */             
"<table><tr><td>fooBar</td><td>1</td></tr></table>",
+                                       /* HtmlR */             
"<table>\n\t<tr>\n\t\t<td>fooBar</td>\n\t\t<td>1</td>\n\t</tr>\n</table>\n",
+                                       /* Uon */               "(fooBar=1)",
+                                       /* UonT */              "(fooBar=1)",
+                                       /* UonR */              
"(\n\tfooBar=1\n)",
+                                       /* UrlEnc */            "fooBar=1",
+                                       /* UrlEncT */   "fooBar=1",
+                                       /* UrlEncR */   "fooBar=1",
+                                       /* MsgPack */   "81A6666F6F42617201",
+                                       /* MsgPackT */  "81A6666F6F42617201",
+                                       /* RdfXml */            
"<rdf:RDF>\n<rdf:Description>\n<jp:fooBar>1</jp:fooBar>\n</rdf:Description>\n</rdf:RDF>\n",
+                                       /* RdfXmlT */   
"<rdf:RDF>\n<rdf:Description>\n<jp:fooBar>1</jp:fooBar>\n</rdf:Description>\n</rdf:RDF>\n",
+                                       /* RdfXmlR */   "<rdf:RDF>\n  
<rdf:Description>\n    <jp:fooBar>1</jp:fooBar>\n  
</rdf:Description>\n</rdf:RDF>\n"
+                               )
+                               {
+                                       @Override
+                                       public void verify(H o) {
+                                               assertType(H.class, o);
+                                               assertTrue(o.createdByBuilder);
+                                       }
+                               }
+                       },
+               });
+       }
+
+       public BuilderComboTest(ComboInput<?> comboInput) {
+               super(comboInput);
+       }
+
+       @Override
+       protected Serializer applySettings(Serializer s) throws Exception {
+               return s.builder().trimNullProperties(false).build();
+       }
+
+       @Override
+       protected Parser applySettings(Parser p) throws Exception {
+               return (Parser) p.builder().build();
+       }
+
+       
//--------------------------------------------------------------------------------
+       // Typical builder scenario
+       
//--------------------------------------------------------------------------------
+
+       public static class A {
+               public int a;
+               boolean createdByBuilder;
+               
+               private A(ABuilder x) {
+                       if (x != null)
+                               this.a = x.a;
+               }
+
+               public A init() {
+                       a = 1;
+                       return this;
+               }
+               
+               public static ABuilder create() {
+                       return new ABuilder();
+               }
+       }
+
+       public static class ABuilder {
+               public int a;
+               
+               public A build() {
+                       A x = new A(this);
+                       x.createdByBuilder = true;
+                       return x;
+               }
+       }
+
+       
//--------------------------------------------------------------------------------
+       // Builder detected through POJO constructor.
+       
//--------------------------------------------------------------------------------
+       public static class B {
+               public int a;
+               boolean createdByBuilder;
+               
+               public B(BBuilder x) {
+                       if (x != null) {
+                               this.a = x.a;
+                               createdByBuilder = true;
+                       }
+               }
+
+               public B init() {
+                       a = 1;
+                       return this;
+               }
+       }
+
+       public static class BBuilder implements 
org.apache.juneau.transform.Builder<B> {
+               public int a;
+       }
+
+       
//--------------------------------------------------------------------------------
+       // Same as B, but should Builder.build() method.
+       
//--------------------------------------------------------------------------------
+       
+       public static class C {
+               public int a;
+               boolean createdByBuilder;
+               
+               public C(CBuilder x) {
+                       if (x != null) {
+                               this.a = x.a;
+                       }
+               }
+
+               public C init() {
+                       a = 1;
+                       return this;
+               }
+       }
+
+       public static class CBuilder implements 
org.apache.juneau.transform.Builder<B> {
+               public int a;
+               
+               public C build() {
+                       C x = new C(this);
+                       x.createdByBuilder = true;
+                       return x;
+               }
+       }
+
+       
//--------------------------------------------------------------------------------
+       // @Builder annotation on POJO class.
+       
//--------------------------------------------------------------------------------
+       
+       @org.apache.juneau.annotation.Builder(DBuilder.class)
+       public static class D {
+               public int a;
+               boolean createdByBuilder;
+               
+               public D(DBuilder x) {
+                       if (x != null) {
+                               this.a = x.a;
+                               createdByBuilder = true;
+                       }
+               }
+
+               public D init() {
+                       a = 1;
+                       return this;
+               }
+       }
+
+       public static class DBuilder {
+               public int a;
+       }
+
+       
//--------------------------------------------------------------------------------
+       // @Builder annotation on POJO class, but uses build() method on 
builder.
+       
//--------------------------------------------------------------------------------
+       
+       @org.apache.juneau.annotation.Builder(EBuilder.class)
+       public static class E {
+               public int a;
+               boolean createdByBuilder;
+               
+               public E(EBuilder x) {
+                       if (x != null) {
+                               this.a = x.a;
+                       }
+               }
+
+               public E init() {
+                       a = 1;
+                       return this;
+               }
+       }
+
+       public static class EBuilder {
+               public int a;
+
+               public E build() {
+                       E x = new E(this);
+                       x.createdByBuilder = true;
+                       return x;
+               }
+       }
+
+       
//--------------------------------------------------------------------------------
+       // Builder with typical method setters.
+       
//--------------------------------------------------------------------------------
+
+       public static class H {
+               public int fooBar;
+               boolean createdByBuilder;
+               
+               private H(HBuilder x) {
+                       if (x != null)
+                               this.fooBar = x.fooBar;
+               }
+
+               public H init() {
+                       fooBar = 1;
+                       return this;
+               }
+               
+               public static HBuilder create() {
+                       return new HBuilder();
+               }
+       }
+
+       public static class HBuilder {
+               private int fooBar;
+               
+               public H build() {
+                       H x = new H(this);
+                       x.createdByBuilder = true;
+                       return x;
+               }
+               
+               @BeanProperty
+               public HBuilder fooBar(int fooBar) {
+                       this.fooBar = fooBar;
+                       return this;
+               }
+       }
+}
diff --git 
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
 
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
index bf43e7b..c779c57 100644
--- 
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
+++ 
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
@@ -231,7 +231,14 @@ public class RdfParserSession extends ReaderParserSession {
                if (eType == null)
                        eType = object();
                PojoSwap<T,Object> swap = 
(PojoSwap<T,Object>)eType.getPojoSwap(this);
-               ClassMeta<?> sType = swap == null ? eType : 
swap.getSwapClassMeta(this);
+               BuilderSwap<T,Object> builder = 
(BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+               ClassMeta<?> sType = null;
+               if (builder != null)
+                       sType = builder.getBuilderClassMeta(this);
+               else if (swap != null)
+                       sType = swap.getSwapClassMeta(this);
+               else
+                       sType = eType;
                setCurrentClass(sType);
 
                if (! sType.canCreateNewInstance(outer)) {
@@ -319,6 +326,12 @@ public class RdfParserSession extends ReaderParserSession {
                        }
                        if (sType.isArray() || sType.isArgs())
                                o = toArray(sType, (Collection)o);
+               } else if (builder != null) {
+                       Resource r = n.asResource();
+                       if (! urisVisited.add(r))
+                               return null;
+                       BeanMap<?> bm = toBeanMap(builder.create(this, eType));
+                       o = builder.build(this, parseIntoBeanMap(r, 
bm).getBean(), eType);
                } else if (sType.canCreateNewBean(outer)) {
                        Resource r = n.asResource();
                        if (! urisVisited.add(r))
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
index 611f6e8..7e74345 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
@@ -447,23 +447,25 @@ public class BeanMap<T> extends 
AbstractMap<String,Object> implements Delegate<T
                        if (v != null)
                                l.add(v);
                for (BeanPropertyMeta bpm : properties) {
-                       try {
-                               if (bpm.isDyna()) {
-                                       for (String pName : 
bpm.getDynaMap(bean).keySet()) {
-                                               Object val = bpm.get(this, 
pName);
+                       if (bpm.canRead()) {
+                               try {
+                                       if (bpm.isDyna()) {
+                                               for (String pName : 
bpm.getDynaMap(bean).keySet()) {
+                                                       Object val = 
bpm.get(this, pName);
+                                                       if (val != null || ! 
ignoreNulls)
+                                                               l.add(new 
BeanPropertyValue(bpm, pName, val, null));
+                                               }
+                                       } else {
+                                               Object val = bpm.get(this, 
null);
                                                if (val != null || ! 
ignoreNulls)
-                                                       l.add(new 
BeanPropertyValue(bpm, pName, val, null));
+                                                       l.add(new 
BeanPropertyValue(bpm, bpm.getName(), val, null));
                                        }
-                               } else {
-                                       Object val = bpm.get(this, null);
-                                       if (val != null || ! ignoreNulls)
-                                               l.add(new 
BeanPropertyValue(bpm, bpm.getName(), val, null));
+                               } catch (Error e) {
+                                       // Errors should always be uncaught.
+                                       throw e;
+                               } catch (Throwable t) {
+                                       l.add(new BeanPropertyValue(bpm, 
bpm.getName(), null, t));
                                }
-                       } catch (Error e) {
-                               // Errors should always be uncaught.
-                               throw e;
-                       } catch (Throwable t) {
-                               l.add(new BeanPropertyValue(bpm, bpm.getName(), 
null, t));
                        }
                }
                if (meta.sortProperties && meta.dynaProperty != null)
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
index c9d86b9..4b8bdd8 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
@@ -129,7 +129,7 @@ public class BeanMeta<T> {
                this.extMeta = b.extMeta;
                this.beanRegistry = b.beanRegistry;
                this.typePropertyName = b.typePropertyName;
-               this.typeProperty = BeanPropertyMeta.builder(this, 
typePropertyName).rawMetaType(ctx.string()).beanRegistry(beanRegistry).build();
+               this.typeProperty = BeanPropertyMeta.builder(this, 
typePropertyName).canRead().canWrite().rawMetaType(ctx.string()).beanRegistry(beanRegistry).build();
                this.sortProperties = b.sortProperties;
        }
 
@@ -443,7 +443,7 @@ public class BeanMeta<T> {
                private String findPropertyName(Field f, Set<String> 
fixedBeanProps) {
                        BeanProperty bp = f.getAnnotation(BeanProperty.class);
                        String name = bpName(bp);
-                       if (! name.isEmpty()) {
+                       if (name != null && ! name.isEmpty()) {
                                if (fixedBeanProps.isEmpty() || 
fixedBeanProps.contains(name))
                                        return name;
                                return null;  // Could happen if filtered via 
BEAN_includeProperties/BEAN_excludeProperties.
@@ -522,9 +522,9 @@ public class BeanMeta<T> {
                        else if (b.field != null)
                                pt = b.field.getType();
 
-                       // Doesn't match if no getter/field defined.
+                       // Matches if only a setter is defined.
                        if (pt == null)
-                               return false;
+                               return true;
 
                        // Doesn't match if not same type or super type as 
getter/field.
                        if (! isParentClass(type, pt))
@@ -585,17 +585,31 @@ public class BeanMeta<T> {
                                        } else if (n.startsWith("is") && 
(rt.equals(Boolean.TYPE) || rt.equals(Boolean.class))) {
                                                isGetter = true;
                                                n = n.substring(2);
-                                       } else if (! bpName.isEmpty()) {
+                                       } else if (bpName != null) {
                                                isGetter = true;
-                                               n = bpName;
+                                               if (bpName.isEmpty()) {
+                                                       if (n.startsWith("get"))
+                                                               n = 
n.substring(3);
+                                                       else if 
(n.startsWith("is"))
+                                                               n = 
n.substring(2);
+                                                       bpName = n;
+                                               } else {
+                                                       n = bpName;
+                                               }
                                        }
                                } else if (pt.length == 1) {
                                        if (n.startsWith("set") && 
(isParentClass(rt, c) || rt.equals(Void.TYPE))) {
                                                isSetter = true;
                                                n = n.substring(3);
-                                       } else if (! bpName.isEmpty()) {
+                                       } else if (bpName != null) {
                                                isSetter = true;
-                                               n = bpName;
+                                               if (bpName.isEmpty()) {
+                                                       if (n.startsWith("set"))
+                                                               n = 
n.substring(3);
+                                                       bpName = n;
+                                               } else {
+                                                       n = bpName;
+                                               }
                                        }
                                } else if (pt.length == 2) {
                                        if ("*".equals(bpName)) {
@@ -605,7 +619,7 @@ public class BeanMeta<T> {
                                }
                                n = pn.getPropertyName(n);
                                if (isGetter || isSetter) {
-                                       if (! bpName.isEmpty()) {
+                                       if (bpName != null && ! 
bpName.isEmpty()) {
                                                n = bpName;
                                                if (! fixedBeanProps.isEmpty())
                                                        if (! 
fixedBeanProps.contains(n))
@@ -789,7 +803,7 @@ public class BeanMeta<T> {
 
        static final String bpName(BeanProperty bp) {
                if (bp == null)
-                       return "";
+                       return null;
                if (! bp.name().isEmpty())
                        return bp.name();
                return bp.value();
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
index 56f360d..1f9c64a 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
@@ -68,7 +68,8 @@ public final class BeanPropertyMeta {
 
        private final Object overrideValue;                       // The bean 
property value (if it's an overridden delegate).
        private final BeanPropertyMeta delegateFor;               // The bean 
property that this meta is a delegate for.
-
+       private final boolean canRead, canWrite;
+       
        /**
         * Creates a builder for {@link #BeanPropertyMeta} objects.
         * 
@@ -97,6 +98,7 @@ public final class BeanPropertyMeta {
                Object overrideValue;
                BeanPropertyMeta delegateFor;
                MetadataMap extMeta = new MetadataMap();
+               boolean canRead, canWrite;
 
                Builder(BeanMeta<?> beanMeta, String name) {
                        this.beanMeta = beanMeta;
@@ -148,16 +150,29 @@ public final class BeanPropertyMeta {
                        this.delegateFor = delegateFor;
                        return this;
                }
+               
+               Builder canRead() {
+                       this.canRead = true;
+                       return this;
+               }
+
+               Builder canWrite() {
+                       this.canWrite = true;
+                       return this;
+               }
 
                boolean validate(BeanContext f, BeanRegistry 
parentBeanRegistry, Map<Class<?>,Class<?>[]> typeVarImpls) throws Exception {
 
                        List<Class<?>> bdClasses = new ArrayList<>();
 
-                       if (field == null && getter == null)
+                       if (field == null && getter == null && setter == null)
                                return false;
 
                        if (field == null && setter == null && 
f.beansRequireSettersForGetters && ! isConstructorArg)
                                return false;
+                       
+                       canRead |= (field != null || getter != null);
+                       canWrite |= (field != null || setter != null);
 
                        if (field != null) {
                                BeanProperty p = 
field.getAnnotation(BeanProperty.class);
@@ -340,6 +355,8 @@ public final class BeanPropertyMeta {
                this.delegateFor = b.delegateFor;
                this.extMeta = b.extMeta;
                this.isDyna = b.isDyna;
+               this.canRead = b.canRead;
+               this.canWrite = b.canWrite;
        }
 
        /**
@@ -1095,4 +1112,22 @@ public final class BeanPropertyMeta {
        public String toString() {
                return name + ": " + this.rawTypeMeta.getInnerClass().getName() 
+ ", field=["+field+"], getter=["+getter+"], setter=["+setter+"]";
        }
+
+       /**
+        * Returns <jk>true</jk> if this property can be read.
+        * 
+        * @return <jk>true</jk> if this property can be read.
+        */
+       public boolean canRead() {
+               return canRead;
+       }
+
+       /**
+        * Returns <jk>true</jk> if this property can be written.
+        * 
+        * @return <jk>true</jk> if this property can be written.
+        */
+       public boolean canWrite() {
+               return canWrite;
+       }
 }
\ No newline at end of file
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
index bf53232..f5576bb 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
@@ -97,6 +97,7 @@ public final class ClassMeta<T> implements Type {
                childUnswapMap;                                      // Maps 
swap subclasses to PojoSwaps.
        private final PojoSwap<T,?>[] pojoSwaps;                // The object 
POJO swaps associated with this bean (if it has any).
        private final BeanFilter beanFilter;                    // The bean 
filter associated with this bean (if it has one).
+       private final BuilderSwap<T,?> builderSwap;             // The builder 
swap associated with this bean (if it has one).
        private final MetadataMap extMeta;                      // Extended 
metadata
        private final BeanContext beanContext;                  // The bean 
context that created this object.
        private final ClassMeta<?>
@@ -169,6 +170,7 @@ public final class ClassMeta<T> implements Type {
                        this.remoteableMethods = builder.remoteableMethods;
                        this.beanFilter = beanFilter;
                        this.pojoSwaps = builder.pojoSwaps.isEmpty() ? null : 
builder.pojoSwaps.toArray(new PojoSwap[builder.pojoSwaps.size()]);
+                       this.builderSwap = builder.builderSwap;
                        this.extMeta = new MetadataMap();
                        this.keyType = builder.keyType;
                        this.valueType = builder.valueType;
@@ -240,6 +242,7 @@ public final class ClassMeta<T> implements Type {
                this.dictionaryName = mainType.dictionaryName;
                this.notABeanReason = mainType.notABeanReason;
                this.pojoSwaps = mainType.pojoSwaps;
+               this.builderSwap = mainType.builderSwap;
                this.beanFilter = mainType.beanFilter;
                this.extMeta = mainType.extMeta;
                this.initException = mainType.initException;
@@ -286,6 +289,7 @@ public final class ClassMeta<T> implements Type {
                this.dictionaryName = null;
                this.notABeanReason = null;
                this.pojoSwaps = null;
+               this.builderSwap = null;
                this.beanFilter = null;
                this.extMeta = new MetadataMap();
                this.initException = null;
@@ -324,8 +328,7 @@ public final class ClassMeta<T> implements Type {
                ClassMeta<?>
                        keyType = null,
                        valueType = null,
-                       elementType = null,
-                       serializedClassMeta = null;
+                       elementType = null;
                String
                        typePropertyName = null,
                        notABeanReason = null,
@@ -333,6 +336,7 @@ public final class ClassMeta<T> implements Type {
                Throwable initException = null;
                BeanMeta beanMeta = null;
                List<PojoSwap> pojoSwaps = new ArrayList<>();
+               BuilderSwap builderSwap;
                InvocationHandler invocationHandler = null;
                BeanRegistry beanRegistry = null;
                PojoSwap<?,?>[] childPojoSwaps;
@@ -583,6 +587,9 @@ public final class ClassMeta<T> implements Type {
 
                        if (pojoSwaps != null)
                                this.pojoSwaps.addAll(Arrays.asList(pojoSwaps));
+                       
+                       if (beanContext != null)
+                               this.builderSwap = 
BuilderSwap.findSwapFromPojoClass(c, beanContext.beanConstructorVisibility, 
beanContext.beanMethodVisibility);
 
                        findPojoSwaps(this.pojoSwaps);
 
@@ -645,10 +652,6 @@ public final class ClassMeta<T> implements Type {
                        if (beanMeta != null)
                                dictionaryName = beanMeta.getDictionaryName();
 
-                       serializedClassMeta = (this.pojoSwaps.isEmpty() ? 
ClassMeta.this : findClassMeta(this.pojoSwaps.get(0).getSwapClass()));
-                       if (serializedClassMeta == null)
-                               serializedClassMeta = ClassMeta.this;
-
                        if (beanMeta != null && beanContext != null && 
beanContext.useInterfaceProxies && innerClass.isInterface())
                                invocationHandler = new 
BeanProxyInvocationHandler<T>(beanMeta);
 
@@ -1278,6 +1281,16 @@ public final class ClassMeta<T> implements Type {
                }
                return null;
        }
+       
+       /**
+        * Returns the builder swap associated with this class.
+        * 
+        * @param session The current bean session.
+        * @return The builder swap associated with this class, or 
<jk>null</jk> if it doesn't exist.
+        */
+       public BuilderSwap<T,?> getBuilderSwap(BeanSession session) {
+               return builderSwap;
+       }
 
        /**
         * Returns the {@link BeanMeta} associated with this class.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Builder.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Builder.java
new file mode 100644
index 0000000..a5c1fea
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Builder.java
@@ -0,0 +1,71 @@
+// 
***************************************************************************************************************************
+// * 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.annotation;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+/**
+ * Identifies a class as a builder for a POJO class.
+ * 
+ * 
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ *     
+ *     <jc>// POJO class.</jc>
+ *     <ja>@Builder</ja>(MyBeanBuilder.<jk>class</jk>)
+ *     <jk>public class</jk> MyBean {
+ * 
+ *             <jc>// Read-only properties.</jc>
+ *             <jk>public final</jk> String <jf>foo</jf>;
+ *             <jk>public final int</jk> <jf>bar</jf>;
+ * 
+ *             <jc>// Constructor that takes in a builder.</jc>
+ *             <jk>public</jk> MyBean(MyBeanBuilder b) {
+ *                     <jk>this</jk>.<jf>foo</jf> = b.foo;
+ *                     <jk>this</jk>.<jf>bar</jf> = b.bar;
+ *             }
+ *     }
+ *     
+ *     <jc>// Builder class.</jc>
+ *     <jk>public class</jk> MyBeanBuilder {
+ *             <jk>public</jk> String <jf>foo</jf>;
+ *             <jk>public int</jk> <jf>bar</jf>;
+ *                     
+ *             <jc>// Method that creates the bean.</jc>
+ *             <jk>public</jk> MyBean build() {
+ *                     <jk>return new</jk> MyBean(<jk>this</jk>);
+ *             }
+ *             
+ *             <jc>// Bean property setters.</jc>
+ *     }
+ * </p>
+ * 
+ * <h5 class='topic'>Documentation</h5>
+ * <ul>
+ *     <li><a class="doclink" 
href="../../../../overview-summary.html#juneau-marshall.PojoBuilders">Overview 
&gt; POJO Builders</a>
+ * </ul>
+ */
+@Documented
+@Target({TYPE})
+@Retention(RUNTIME)
+@Inherited
+public @interface Builder {
+
+       /**
+        * The builder for this class.
+        */
+       Class<?> value() default Null.class;
+}
\ No newline at end of file
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java
index fcac49a..76dcda8 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java
@@ -60,18 +60,22 @@ public final class CsvSerializerSession extends 
WriterSerializerSession {
                                        BeanMeta<?> bm = 
entryType.getBeanMeta();
                                        int i = 0;
                                        for (BeanPropertyMeta pm : 
bm.getPropertyMetas()) {
-                                               if (i++ > 0)
-                                                       w.append(',');
-                                               append(w, pm.getName());
+                                               if (pm.canRead()) {
+                                                       if (i++ > 0)
+                                                               w.append(',');
+                                                       append(w, pm.getName());
+                                               }
                                        }
                                        w.append('\n');
                                        for (Object o2 : l) {
                                                i = 0;
                                                BeanMap<?> bean = toBeanMap(o2);
                                                for (BeanPropertyMeta pm : 
bm.getPropertyMetas()) {
-                                                       if (i++ > 0)
-                                                               w.append(',');
-                                                       append(w, pm.get(bean, 
pm.getName()));
+                                                       if (pm.canRead()) {
+                                                               if (i++ > 0)
+                                                                       
w.append(',');
+                                                               append(w, 
pm.get(bean, pm.getName()));
+                                                       }
                                                }
                                                w.append('\n');
                                        }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java
index f7d5d84..b1d2779 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java
@@ -83,8 +83,15 @@ public final class HtmlParserSession extends 
XmlParserSession {
 
                if (eType == null)
                        eType = (ClassMeta<T>)object();
-               PojoSwap<T,Object> transform = 
(PojoSwap<T,Object>)eType.getPojoSwap(this);
-               ClassMeta<?> sType = transform == null ? eType : 
transform.getSwapClassMeta(this);
+               PojoSwap<T,Object> swap = 
(PojoSwap<T,Object>)eType.getPojoSwap(this);
+               BuilderSwap<T,Object> builder = 
(BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+               ClassMeta<?> sType = null;
+               if (builder != null)
+                       sType = builder.getBuilderClassMeta(this);
+               else if (swap != null)
+                       sType = swap.getSwapClassMeta(this);
+               else
+                       sType = eType;
                setCurrentClass(sType);
 
                int event = r.getEventType();
@@ -208,6 +215,9 @@ public final class HtmlParserSession extends 
XmlParserSession {
                                } else if (sType.isMap()) {
                                        o = parseIntoMap(r, 
(Map)(sType.canCreateNewInstance(outer) ? sType.newInstance(outer)
                                                : new ObjectMap(this)), 
sType.getKeyType(), sType.getValueType(), pMeta);
+                               } else if (builder != null) {
+                                       BeanMap m = 
toBeanMap(builder.create(this, eType));
+                                       o = builder.build(this, 
parseIntoBean(r, m).getBean(), eType);
                                } else if (sType.canCreateNewBean(outer)) {
                                        BeanMap m = newBeanMap(outer, 
sType.getInnerClass());
                                        o = parseIntoBean(r, m).getBean();
@@ -256,8 +266,8 @@ public final class HtmlParserSession extends 
XmlParserSession {
                if (! isValid)
                        throw new XmlParseException(r.getLocation(), 
"Unexpected tag ''{0}'' for type ''{1}''", tag, eType);
 
-               if (transform != null && o != null)
-                       o = transform.unswap(this, o, eType);
+               if (swap != null && o != null)
+                       o = swap.unswap(this, o, eType);
 
                if (outer != null)
                        setParent(eType, o, outer);
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
index 067f12b..75cfbf5 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
@@ -600,30 +600,32 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                                        for (Object k : th) {
                                                BeanMapEntry p = 
m2.getProperty(toString(k));
                                                BeanPropertyMeta pMeta = 
p.getMeta();
-                                               Object value = p.getValue();
-
-                                               String link = null, anchorText 
= null;
-                                               if (! 
pMeta.getClassMeta().isCollectionOrArray()) {
-                                                       link = 
m2.resolveVars(getLink(pMeta));
-                                                       anchorText = 
m2.resolveVars(getAnchorText(pMeta));
+                                               if (pMeta.canRead()) {
+                                                       Object value = 
p.getValue();
+
+                                                       String link = null, 
anchorText = null;
+                                                       if (! 
pMeta.getClassMeta().isCollectionOrArray()) {
+                                                               link = 
m2.resolveVars(getLink(pMeta));
+                                                               anchorText = 
m2.resolveVars(getAnchorText(pMeta));
+                                                       }
+
+                                                       if (anchorText != null)
+                                                               value = 
anchorText;
+
+                                                       String style = 
getStyle(this, pMeta, value);
+                                                       out.oTag(i+2, "td");
+                                                       if (style != null)
+                                                               
out.attr("style", style);
+                                                       out.cTag();
+                                                       if (link != null)
+                                                               
out.oTag("a").attrUri("href", link).cTag();
+                                                       ContentResult cr = 
serializeAnything(out, value, pMeta.getClassMeta(), p.getKey().toString(), 2, 
pMeta, false);
+                                                       if (cr == CR_NORMAL)
+                                                               out.i(i+2);
+                                                       if (link != null)
+                                                               out.eTag("a");
+                                                       out.eTag("td").nl(i+2);
                                                }
-
-                                               if (anchorText != null)
-                                                       value = anchorText;
-
-                                               String style = getStyle(this, 
pMeta, value);
-                                               out.oTag(i+2, "td");
-                                               if (style != null)
-                                                       out.attr("style", 
style);
-                                               out.cTag();
-                                               if (link != null)
-                                                       
out.oTag("a").attrUri("href", link).cTag();
-                                               ContentResult cr = 
serializeAnything(out, value, pMeta.getClassMeta(), p.getKey().toString(), 2, 
pMeta, false);
-                                               if (cr == CR_NORMAL)
-                                                       out.i(i+2);
-                                               if (link != null)
-                                                       out.eTag("a");
-                                               out.eTag("td").nl(i+2);
                                        }
                                }
                                out.ie(i+1).eTag("tr").nl(i+1);
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
index 936f80d..3b7c237 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
@@ -584,20 +584,39 @@ public final class ClassUtils {
         *      Can be subtypes of the actual constructor argument types.
         * @return The matching constructor, or <jk>null</jk> if constructor 
could not be found.
         */
-       @SuppressWarnings("unchecked")
        public static <T> Constructor<T> findPublicConstructor(Class<T> c, 
boolean fuzzyArgs, Class<?>...argTypes) {
+               return findConstructor(c, Visibility.PUBLIC, fuzzyArgs, 
argTypes);
+       }
+       
+       /**
+        * Finds a constructor with the specified parameters without throwing 
an exception.
+        * 
+        * @param c The class to search for a constructor.
+        * @param vis The minimum visibility.
+        * @param fuzzyArgs 
+        *      Use fuzzy-arg matching.
+        *      Find a constructor that best matches the specified args.
+        * @param argTypes
+        *      The argument types in the constructor.
+        *      Can be subtypes of the actual constructor argument types.
+        * @return The matching constructor, or <jk>null</jk> if constructor 
could not be found.
+        */
+       @SuppressWarnings("unchecked")
+       public static <T> Constructor<T> findConstructor(Class<T> c, Visibility 
vis, boolean fuzzyArgs, Class<?>...argTypes) {
                ConstructorCacheEntry cce = CONSTRUCTOR_CACHE.get(c);
-               if (cce != null && argsMatch(cce.paramTypes, argTypes)) 
+               if (cce != null && argsMatch(cce.paramTypes, argTypes) && 
cce.isVisible(vis)) 
                        return (Constructor<T>)cce.constructor;
        
                if (fuzzyArgs) {
                        int bestCount = -1;
                        Constructor<?> bestMatch = null;
-                       for (Constructor<?> n : c.getConstructors()) {
-                               int m = fuzzyArgsMatch(n.getParameterTypes(), 
argTypes);
-                               if (m > bestCount) {
-                                       bestCount = m;
-                                       bestMatch = n;
+                       for (Constructor<?> n : c.getDeclaredConstructors()) {
+                               if (vis.isVisible(n)) {
+                                       int m = 
fuzzyArgsMatch(n.getParameterTypes(), argTypes);
+                                       if (m > bestCount) {
+                                               bestCount = m;
+                                               bestMatch = n;
+                                       }
                                }
                        }
                        if (bestCount >= 0) 
@@ -606,7 +625,7 @@ public final class ClassUtils {
                } 
                
                for (Constructor<?> n : c.getConstructors()) {
-                       if (argsMatch(n.getParameterTypes(), argTypes)) {
+                       if (argsMatch(n.getParameterTypes(), argTypes) && 
vis.isVisible(n)) {
                                CONSTRUCTOR_CACHE.put(c, new 
ConstructorCacheEntry(c, n));
                                return (Constructor<T>)n;
                        }
@@ -615,6 +634,8 @@ public final class ClassUtils {
                return null;
        }
        
+       
+       
        private static final class ConstructorCacheEntry {
                final Constructor<?> constructor;
                final Class<?>[] paramTypes;
@@ -623,6 +644,10 @@ public final class ClassUtils {
                        this.constructor = constructor;
                        this.paramTypes = constructor.getParameterTypes();
                }
+               
+               boolean isVisible(Visibility vis) {
+                       return vis.isVisible(constructor);
+               }
        }
        
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
index a42b712..b3fc575 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
@@ -117,7 +117,14 @@ public final class JsonParserSession extends 
ReaderParserSession {
                if (eType == null)
                        eType = object();
                PojoSwap<T,Object> swap = 
(PojoSwap<T,Object>)eType.getPojoSwap(this);
-               ClassMeta<?> sType = swap == null ? eType : 
swap.getSwapClassMeta(this);
+               BuilderSwap<T,Object> builder = 
(BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+               ClassMeta<?> sType = null;
+               if (builder != null)
+                       sType = builder.getBuilderClassMeta(this);
+               else if (swap != null)
+                       sType = swap.getSwapClassMeta(this);
+               else
+                       sType = eType;
                setCurrentClass(sType);
                String wrapperAttr = 
sType.getExtendedMeta(JsonClassMeta.class).getWrapperAttr();
 
@@ -178,6 +185,9 @@ public final class JsonParserSession extends 
ReaderParserSession {
                                Collection l = 
(sType.canCreateNewInstance(outer) ? (Collection)sType.newInstance() : new 
ObjectList(this));
                                o = parseIntoCollection2(r, l, sType, pMeta);
                        }
+               } else if (builder != null) {
+                       BeanMap m = toBeanMap(builder.create(this, eType));
+                       o = builder.build(this, parseIntoBeanMap2(r, 
m).getBean(), eType);
                } else if (sType.canCreateNewBean(outer)) {
                        BeanMap m = newBeanMap(outer, sType.getInnerClass());
                        o = parseIntoBeanMap2(r, m).getBean();
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java
index 0054fbf..95aab8b 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java
@@ -109,7 +109,8 @@ public class JsonSchemaSerializerSession extends 
JsonSerializerSession {
                                        bm = new BeanMetaFiltered(bm, pNames);
                                for (Iterator<BeanPropertyMeta> i = 
bm.getPropertyMetas().iterator(); i.hasNext();) {
                                        BeanPropertyMeta p = i.next();
-                                       properties.put(p.getName(), 
getSchema(p.getClassMeta(), p.getName(), p.getProperties()));
+                                       if (p.canRead())
+                                               properties.put(p.getName(), 
getSchema(p.getClassMeta(), p.getName(), p.getProperties()));
                                }
                                out.put("properties", properties);
                        }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
index 3396055..d792f6c 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
@@ -189,24 +189,26 @@ public class JsonSerializerSession extends 
WriterSerializerSession {
                boolean addComma = false;
                for (BeanPropertyValue p : m.getValues(isTrimNulls(), typeName 
!= null ? createBeanTypeNameProperty(m, typeName) : null)) {
                        BeanPropertyMeta pMeta = p.getMeta();
-                       ClassMeta<?> cMeta = p.getClassMeta();
-                       String key = p.getName();
-                       Object value = p.getValue();
-                       Throwable t = p.getThrown();
-                       if (t != null)
-                               onBeanGetterException(pMeta, t);
+                       if (pMeta.canRead()) {
+                               ClassMeta<?> cMeta = p.getClassMeta();
+                               String key = p.getName();
+                               Object value = p.getValue();
+                               Throwable t = p.getThrown();
+                               if (t != null)
+                                       onBeanGetterException(pMeta, t);
 
-                       if (canIgnoreValue(cMeta, key, value))
-                               continue;
+                               if (canIgnoreValue(cMeta, key, value))
+                                       continue;
 
-                       if (addComma)
-                               out.append(',').smi(i);
+                               if (addComma)
+                                       out.append(',').smi(i);
 
-                       out.cr(i).attr(key).append(':').s(i);
+                               out.cr(i).attr(key).append(':').s(i);
 
-                       serializeAnything(out, value, cMeta, key, pMeta);
+                               serializeAnything(out, value, cMeta, key, 
pMeta);
 
-                       addComma = true;
+                               addComma = true;
+                       }
                }
                out.cre(i-1).append('}');
                return out;
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java
index 400818c..e537bb9 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java
@@ -58,7 +58,14 @@ public final class MsgPackParserSession extends 
InputStreamParserSession {
                if (eType == null)
                        eType = object();
                PojoSwap<T,Object> swap = 
(PojoSwap<T,Object>)eType.getPojoSwap(this);
-               ClassMeta<?> sType = swap == null ? eType : 
swap.getSwapClassMeta(this);
+               BuilderSwap<T,Object> builder = 
(BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+               ClassMeta<?> sType = null;
+               if (builder != null)
+                       sType = builder.getBuilderClassMeta(this);
+               else if (swap != null)
+                       sType = swap.getSwapClassMeta(this);
+               else
+                       sType = eType;
                setCurrentClass(sType);
 
                Object o = null;
@@ -110,9 +117,9 @@ public final class MsgPackParserSession extends 
InputStreamParserSession {
                                } else {
                                        throw new ParseException(loc(is), 
"Invalid data type {0} encountered for parse type {1}", dt, sType);
                                }
-                       } else if (sType.canCreateNewBean(outer)) {
+                       } else if (builder != null || 
sType.canCreateNewBean(outer)) {
                                if (dt == MAP) {
-                                       BeanMap m = newBeanMap(outer, 
sType.getInnerClass());
+                                       BeanMap m = builder == null ? 
newBeanMap(outer, sType.getInnerClass()) : toBeanMap(builder.create(this, 
eType));
                                        for (int i = 0; i < length; i++) {
                                                String pName = 
parseAnything(string(), is, m.getBean(false), null);
                                                BeanPropertyMeta bpm = 
m.getPropertyMeta(pName);
@@ -128,7 +135,7 @@ public final class MsgPackParserSession extends 
InputStreamParserSession {
                                                        bpm.set(m, pName, 
value);
                                                }
                                        }
-                                       o = m.getBean();
+                                       o = builder == null ? m.getBean() : 
builder.build(this, m.getBean(), eType);
                                } else {
                                        throw new ParseException(loc(is), 
"Invalid data type {0} encountered for parse type {1}", dt, sType);
                                }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java
index a836d8c..a60aa5b 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java
@@ -195,15 +195,17 @@ public final class MsgPackSerializerSession extends 
OutputStreamSerializerSessio
 
                for (BeanPropertyValue p : values) {
                        BeanPropertyMeta pMeta = p.getMeta();
-                       ClassMeta<?> cMeta = p.getClassMeta();
-                       String key = p.getName();
-                       Object value = p.getValue();
-                       Throwable t = p.getThrown();
-                       if (t != null)
-                               onBeanGetterException(pMeta, t);
-                       else {
-                               serializeAnything(out, key, null, null, null);
-                               serializeAnything(out, value, cMeta, key, 
pMeta);
+                       if (pMeta.canRead()) {
+                               ClassMeta<?> cMeta = p.getClassMeta();
+                               String key = p.getName();
+                               Object value = p.getValue();
+                               Throwable t = p.getThrown();
+                               if (t != null)
+                                       onBeanGetterException(pMeta, t);
+                               else {
+                                       serializeAnything(out, key, null, null, 
null);
+                                       serializeAnything(out, value, cMeta, 
key, pMeta);
+                               }
                        }
                }
        }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/Parser.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/Parser.java
index aa36e1e..2b7be72 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/Parser.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/Parser.java
@@ -475,10 +475,14 @@ public abstract class Parser extends BeanContext {
         *              .set(<jsf>PARSER_unbuffered</jsf>, <jk>true</jk>)
         *              .build();
         * 
+        *      <jc>// If you're calling parse on the same input multiple 
times, use a session instead of the parser directly.</jc>
+        *      <jc>// It's more efficient because we don't need to recalc the 
session settings again. </jc>
+        *      ReaderParserSession s = p.createSession();
+        *      
         *      <jc>// Read input with multiple POJOs</jc>
         *      Reader json = <jk>new</jk> 
StringReader(<js>"{foo:'bar'}{foo:'baz'}"</js>);
-        *      MyBean myBean1 = p.parse(json, MyBean.<jk>class</jk>);
-        *      MyBean myBean2 = p.parse(json, MyBean.<jk>class</jk>);
+        *      MyBean myBean1 = s.parse(json, MyBean.<jk>class</jk>);
+        *      MyBean myBean2 = s.parse(json, MyBean.<jk>class</jk>);
         * </p>
         * 
         * <h5 class='section'>Notes:</h5>
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/Builder.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/Builder.java
new file mode 100644
index 0000000..fd0e4fa
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/Builder.java
@@ -0,0 +1,30 @@
+// 
***************************************************************************************************************************
+// * 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.transform;
+
+/**
+ * Identifies a class as being a builder for a bean class.
+ * 
+ * <p>
+ * This interface has no methods to implement.
+ * <br>It's purpose is to identify a class as a builder when it's used on a 
constructor of the built class.
+ * 
+ * <h5 class='topic'>Documentation</h5>
+ * <ul>
+ *     <li><a class="doclink" 
href="../../../../overview-summary.html#juneau-marshall.PojoBuilders">Overview 
&gt; POJO Builders</a>
+ * </ul>
+ * 
+ * @param <T> The type of objects that this builder creates. 
+ */
+public interface Builder<T> {
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BuilderSwap.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BuilderSwap.java
new file mode 100644
index 0000000..645cfbb
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BuilderSwap.java
@@ -0,0 +1,231 @@
+// 
***************************************************************************************************************************
+// * 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.transform;
+
+import static org.apache.juneau.internal.ClassUtils.*;
+
+import java.lang.reflect.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+
+/**
+ * Specialized transform for builder classes.
+ * 
+ * <h5 class='topic'>Documentation</h5>
+ * <ul>
+ *     <li><a class="doclink" 
href="../../../../overview-summary.html#juneau-marshall.PojoBuilders">Overview 
&gt; POJO Builders</a>
+ * </ul>
+ * 
+ * @param <T> The bean class.
+ * @param <B> The builder class.
+ */
+@SuppressWarnings("unchecked")
+public class BuilderSwap<T,B> {
+
+       private final Class<T> pojoClass;
+       private final Class<B> builderClass;
+       private final Constructor<T> pojoConstructor;      // public 
Pojo(Builder);
+       private final Constructor<B> builderConstructor;   // public Builder();
+       private final Method createBuilderMethod;          // Builder create();
+       private final Method createPojoMethod;             // Pojo build();
+       private ClassMeta<?> builderClassMeta;
+
+       /**
+        * Constructor.
+        * 
+        * @param pojoClass The POJO class created by the builder class. 
+        * @param builderClass The builder class.
+        * @param pojoConstructor The POJO constructor that takes in a builder 
as a parameter.
+        * @param builderConstructor The builder no-arg constructor.
+        * @param createBuilderMethod The static create() method on the POJO 
class.
+        * @param createPojoMethod The build() method on the builder class. 
+        */
+       protected BuilderSwap(Class<T> pojoClass, Class<B> builderClass, 
Constructor<T> pojoConstructor, Constructor<B> builderConstructor, Method 
createBuilderMethod, Method createPojoMethod) {
+               this.pojoClass = pojoClass;
+               this.builderClass = builderClass;
+               this.pojoConstructor = pojoConstructor;
+               this.builderConstructor = builderConstructor;
+               this.createBuilderMethod = createBuilderMethod;
+               this.createPojoMethod = createPojoMethod;
+       }
+       
+       /**
+        * The POJO class.
+        * 
+        * @return The POJO class.
+        */
+       public Class<T> getPojoClass() {
+               return pojoClass;
+       }
+
+       /**
+        * The builder class.
+        * 
+        * @return The builder class.
+        */
+       public Class<B> getBuilderClass() {
+               return builderClass;
+       }
+       
+       /**
+        * Returns the {@link ClassMeta} of the transformed class type.
+        * 
+        * <p>
+        * This value is cached for quick lookup.
+        * 
+        * @param session
+        *      The bean context to use to get the class meta.
+        *      This is always going to be the same bean context that created 
this swap.
+        * @return The {@link ClassMeta} of the transformed class type.
+        */
+       public ClassMeta<?> getBuilderClassMeta(BeanSession session) {
+               if (builderClassMeta == null)
+                       builderClassMeta = 
session.getClassMeta(getBuilderClass());
+               return builderClassMeta;
+       }
+
+       /**
+        * Creates a new builder object.
+        * 
+        * @param session The current bean session.
+        * @param hint A hint about the class type.
+        * @return A new POJO.
+        * @throws Exception
+        */
+       public B create(BeanSession session, ClassMeta<?> hint) throws 
Exception {
+               if (createBuilderMethod != null)
+                       return (B)createBuilderMethod.invoke(null);
+               return builderConstructor.newInstance();
+       }
+       
+       /**
+        * Creates a new POJO from the specified builder.
+        * 
+        * @param session The current bean session.
+        * @param builder The POJO builder.
+        * @param hint A hint about the class type.
+        * @return A new POJO.
+        * @throws Exception
+        */
+       public T build(BeanSession session, B builder, ClassMeta<?> hint) 
throws Exception {
+               if (createPojoMethod != null)
+                       return (T)createPojoMethod.invoke(builder);
+               return pojoConstructor.newInstance(builder);
+       }
+       
+       /**
+        * Creates a BuilderSwap from the specified builder class if it 
qualifies as one.
+        * 
+        * @param builderClass The potential builder class.
+        * @param cVis Minimum constructor visibility.
+        * @param mVis Minimum method visibility.
+        * @return A new swap instance, or <jk>null</jk> if class wasn't a 
builder class.
+        */
+       @SuppressWarnings("rawtypes")
+       public static BuilderSwap<?,?> findSwapFromBuilderClass(Class<?> 
builderClass, Visibility cVis, Visibility mVis) {
+               if (! isPublic(builderClass))
+                       return null;
+               
+               Class<?> pojoClass = resolveParameterType(Builder.class, 0, 
builderClass);
+
+               Method createPojoMethod, createBuilderMethod;
+               Constructor<?> pojoConstructor, builderConstructor;
+               
+               createPojoMethod = findCreatePojoMethod(builderClass);
+               if (createPojoMethod != null)
+                       pojoClass = createPojoMethod.getReturnType();
+               
+               if (pojoClass == null)
+                       return null;
+
+               pojoConstructor = findConstructor(pojoClass, cVis, false, 
builderClass);
+               if (pojoConstructor == null)
+                       return null;
+               
+               builderConstructor = findNoArgConstructor(builderClass, cVis);
+               createBuilderMethod = findBuilderCreateMethod(pojoClass);
+               if (builderConstructor == null && createBuilderMethod == null)
+                       return null;
+               
+               return new BuilderSwap(pojoClass, builderClass, 
pojoConstructor, builderConstructor, createBuilderMethod, createPojoMethod);
+       }
+       
+
+       /**
+        * Creates a BuilderSwap from the specified POJO class if it has one.
+        * 
+        * @param pojoClass The POJO class to check.
+        * @param cVis Minimum constructor visibility.
+        * @param mVis Minimum method visibility.
+        * @return A new swap instance, or <jk>null</jk> if class didn't have a 
builder class.
+        */
+       @SuppressWarnings("rawtypes")
+       public static BuilderSwap<?,?> findSwapFromPojoClass(Class<?> 
pojoClass, Visibility cVis, Visibility mVis) {
+               Class<?> builderClass = null;
+               Method pojoCreateMethod, builderCreateMethod;
+               Constructor<?> pojoConstructor = null, builderConstructor;
+
+               org.apache.juneau.annotation.Builder b = 
pojoClass.getAnnotation(org.apache.juneau.annotation.Builder.class);
+               
+               if (b != null && b.value() != Null.class) 
+                       builderClass = b.value();
+               
+               builderCreateMethod = findBuilderCreateMethod(pojoClass);
+
+               if (builderClass == null && builderCreateMethod != null) 
+                       builderClass = builderCreateMethod.getReturnType();
+               
+               if (builderClass == null) {
+                       for (Constructor cc : pojoClass.getConstructors()) {
+                               if (cVis.isVisible(cc)) {
+                                       Class<?>[] pt = cc.getParameterTypes();
+                                       if (pt.length == 1 && 
isParentClass(Builder.class, pt[0])) {
+                                               pojoConstructor = cc;
+                                               builderClass = pt[0];
+                                       }
+                               }
+                       }
+               }
+               
+               if (builderClass == null)
+                       return null;
+               
+               builderConstructor = findNoArgConstructor(builderClass, cVis);
+               if (builderConstructor == null && builderCreateMethod == null)
+                       return null;
+
+               pojoCreateMethod = findCreatePojoMethod(builderClass);
+               if (pojoConstructor == null)
+                       pojoConstructor = findConstructor(pojoClass, cVis, 
false, builderClass);
+               
+               if (pojoConstructor == null && pojoCreateMethod == null)
+                       return null;
+
+               return new BuilderSwap(pojoClass, builderClass, 
pojoConstructor, builderConstructor, builderCreateMethod, pojoCreateMethod);
+       }
+
+       private static Method findBuilderCreateMethod(Class<?> pojoClass) {
+               for (Method m : pojoClass.getDeclaredMethods()) 
+                       if (isPublic(m) && isStatic(m) && 
m.getName().equals("create") && m.getReturnType() != Void.class)
+                               return m;
+               return null;
+       }
+               
+       private static Method findCreatePojoMethod(Class<?> builderClass) {
+               for (Method m : builderClass.getDeclaredMethods()) 
+                       if ("build".equals(m.getName()) && ! (isStatic(m) || 
m.getReturnType() == Void.class)) 
+                               return m;
+               return null;
+       }
+}
\ No newline at end of file
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonParserSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonParserSession.java
index ad7906a..6f9e9ad 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonParserSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonParserSession.java
@@ -131,7 +131,15 @@ public class UonParserSession extends ReaderParserSession {
                if (eType == null)
                        eType = object();
                PojoSwap<T,Object> swap = 
(PojoSwap<T,Object>)eType.getPojoSwap(this);
-               ClassMeta<?> sType = swap == null ? eType : 
swap.getSwapClassMeta(this);
+               BuilderSwap<T,Object> builder = 
(BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+               ClassMeta<?> sType = null;
+               if (builder != null)
+                       sType = builder.getBuilderClassMeta(this);
+               else if (swap != null)
+                       sType = swap.getSwapClassMeta(this);
+               else
+                       sType = eType;
+               setCurrentClass(sType);
 
                Object o = null;
 
@@ -210,6 +218,10 @@ public class UonParserSession extends ReaderParserSession {
                                );
                                o = parseIntoCollection(r, l, sType, 
isUrlParamValue, pMeta);
                        }
+               } else if (builder != null) {
+                       BeanMap m = toBeanMap(builder.create(this, eType));
+                       m = parseIntoBeanMap(r, m);
+                       o = m == null ? null : builder.build(this, m.getBean(), 
eType);
                } else if (sType.canCreateNewBean(outer)) {
                        BeanMap m = newBeanMap(outer, sType.getInnerClass());
                        m = parseIntoBeanMap(r, m);
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonSerializerSession.java
index 7730c9c..28e7b89 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonSerializerSession.java
@@ -221,25 +221,27 @@ public class UonSerializerSession extends 
WriterSerializerSession {
 
                for (BeanPropertyValue p : m.getValues(isTrimNulls(), typeName 
!= null ? createBeanTypeNameProperty(m, typeName) : null)) {
                        BeanPropertyMeta pMeta = p.getMeta();
-                       ClassMeta<?> cMeta = p.getClassMeta();
+                       if (pMeta.canRead()) {
+                               ClassMeta<?> cMeta = p.getClassMeta();
 
-                       String key = p.getName();
-                       Object value = p.getValue();
-                       Throwable t = p.getThrown();
-                       if (t != null)
-                               onBeanGetterException(pMeta, t);
+                               String key = p.getName();
+                               Object value = p.getValue();
+                               Throwable t = p.getThrown();
+                               if (t != null)
+                                       onBeanGetterException(pMeta, t);
 
-                       if (canIgnoreValue(cMeta, key, value))
-                               continue;
+                               if (canIgnoreValue(cMeta, key, value))
+                                       continue;
 
-                       if (addComma)
-                               out.append(',');
+                               if (addComma)
+                                       out.append(',');
 
-                       out.cr(indent).appendObject(key, false).append('=');
+                               out.cr(indent).appendObject(key, 
false).append('=');
 
-                       serializeAnything(out, value, cMeta, key, pMeta);
+                               serializeAnything(out, value, cMeta, key, 
pMeta);
 
-                       addComma = true;
+                               addComma = true;
+                       }
                }
 
                if (m.size() > 0)
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java
index 2207577..0134cea 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java
@@ -96,7 +96,14 @@ public class UrlEncodingParserSession extends 
UonParserSession {
                if (eType == null)
                        eType = (ClassMeta<T>)object();
                PojoSwap<T,Object> swap = 
(PojoSwap<T,Object>)eType.getPojoSwap(this);
-               ClassMeta<?> sType = swap == null ? eType : 
swap.getSwapClassMeta(this);
+               BuilderSwap<T,Object> builder = 
(BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+               ClassMeta<?> sType = null;
+               if (builder != null)
+                       sType = builder.getBuilderClassMeta(this);
+               else if (swap != null)
+                       sType = swap.getSwapClassMeta(this);
+               else
+                       sType = eType;
 
                int c = r.peekSkipWs();
                if (c == '?')
@@ -114,6 +121,10 @@ public class UrlEncodingParserSession extends 
UonParserSession {
                } else if (sType.isMap()) {
                        Map m = (sType.canCreateNewInstance() ? 
(Map)sType.newInstance() : new ObjectMap(this));
                        o = parseIntoMap2(r, m, sType, m);
+               } else if (builder != null) {
+                       BeanMap m = toBeanMap(builder.create(this, eType));
+                       m = parseIntoBeanMap(r, m);
+                       o = m == null ? null : builder.build(this, m.getBean(), 
eType);
                } else if (sType.canCreateNewBean(outer)) {
                        BeanMap m = newBeanMap(outer, sType.getInnerClass());
                        m = parseIntoBeanMap(r, m);
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java
index f0d3bfc..39b95a3 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java
@@ -222,43 +222,45 @@ public class UrlEncodingSerializerSession extends 
UonSerializerSession {
 
                for (BeanPropertyValue p : m.getValues(isTrimNulls(), typeName 
!= null ? createBeanTypeNameProperty(m, typeName) : null)) {
                        BeanPropertyMeta pMeta = p.getMeta();
-                       ClassMeta<?> cMeta = p.getClassMeta();
-                       ClassMeta<?> sMeta = cMeta.getSerializedClassMeta(this);
-
-                       String key = p.getName();
-                       Object value = p.getValue();
-                       Throwable t = p.getThrown();
-                       if (t != null)
-                               onBeanGetterException(pMeta, t);
-
-                       if (canIgnoreValue(sMeta, key, value))
-                               continue;
-
-                       if (value != null && shouldUseExpandedParams(pMeta)) {
-                               // Transformed object array bean properties may 
be transformed resulting in ArrayLists,
-                               // so we need to check type if we think it's an 
array.
-                               Iterator i = (sMeta.isCollection() || value 
instanceof Collection) ? ((Collection)value).iterator() : iterator(value);
-                               while (i.hasNext()) {
+                       if (pMeta.canRead()) {
+                               ClassMeta<?> cMeta = p.getClassMeta();
+                               ClassMeta<?> sMeta = 
cMeta.getSerializedClassMeta(this);
+
+                               String key = p.getName();
+                               Object value = p.getValue();
+                               Throwable t = p.getThrown();
+                               if (t != null)
+                                       onBeanGetterException(pMeta, t);
+
+                               if (canIgnoreValue(sMeta, key, value))
+                                       continue;
+
+                               if (value != null && 
shouldUseExpandedParams(pMeta)) {
+                                       // Transformed object array bean 
properties may be transformed resulting in ArrayLists,
+                                       // so we need to check type if we think 
it's an array.
+                                       Iterator i = (sMeta.isCollection() || 
value instanceof Collection) ? ((Collection)value).iterator() : iterator(value);
+                                       while (i.hasNext()) {
+                                               if (addAmp)
+                                                       
out.cr(indent).append('&');
+
+                                               out.appendObject(key, 
true).append('=');
+
+                                               super.serializeAnything(out, 
i.next(), cMeta.getElementType(), key, pMeta);
+
+                                               addAmp = true;
+                                       }
+                               } else {
                                        if (addAmp)
                                                out.cr(indent).append('&');
 
                                        out.appendObject(key, true).append('=');
 
-                                       super.serializeAnything(out, i.next(), 
cMeta.getElementType(), key, pMeta);
+                                       super.serializeAnything(out, value, 
cMeta, key, pMeta);
 
                                        addAmp = true;
                                }
-                       } else {
-                               if (addAmp)
-                                       out.cr(indent).append('&');
-
-                               out.appendObject(key, true).append('=');
-
-                               super.serializeAnything(out, value, cMeta, key, 
pMeta);
-
-                               addAmp = true;
+                               
                        }
-
                }
                return out;
        }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParserSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParserSession.java
index 630bce6..f1bc15e 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParserSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParserSession.java
@@ -286,7 +286,14 @@ public class XmlParserSession extends ReaderParserSession {
                if (eType == null)
                        eType = (ClassMeta<T>)object();
                PojoSwap<T,Object> swap = 
(PojoSwap<T,Object>)eType.getPojoSwap(this);
-               ClassMeta<?> sType = swap == null ? eType : 
swap.getSwapClassMeta(this);
+               BuilderSwap<T,Object> builder = 
(BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+               ClassMeta<?> sType = null;
+               if (builder != null)
+                       sType = builder.getBuilderClassMeta(this);
+               else if (swap != null)
+                       sType = swap.getSwapClassMeta(this);
+               else
+                       sType = eType;
                setCurrentClass(sType);
 
                String wrapperAttr = (isRoot && preserveRootElement) ? 
r.getName().getLocalPart() : null;
@@ -352,19 +359,20 @@ public class XmlParserSession extends ReaderParserSession 
{
                        o = parseIntoCollection(r, l, sType, pMeta);
                } else if (sType.isNumber()) {
                        o = parseNumber(getElementText(r), (Class<? extends 
Number>)sType.getInnerClass());
-               } else if (sType.canCreateNewBean(outer)) {
+               } else if (builder != null || sType.canCreateNewBean(outer)) {
                        if 
(sType.getExtendedMeta(XmlClassMeta.class).getFormat() == COLLAPSED) {
                                String fieldName = r.getLocalName();
-                               BeanMap<?> m = newBeanMap(outer, 
sType.getInnerClass());
+                               BeanMap<?> m = builder != null ? 
toBeanMap(builder.create(this, eType)) : newBeanMap(outer, 
sType.getInnerClass());
                                BeanPropertyMeta bpm = 
m.getMeta().getExtendedMeta(XmlBeanMeta.class).getPropertyMeta(fieldName);
                                ClassMeta<?> cm = m.getMeta().getClassMeta();
                                Object value = parseAnything(cm, currAttr, r, 
m.getBean(false), false, null);
                                setName(cm, value, currAttr);
                                bpm.set(m, currAttr, value);
-                               o = m.getBean();
+                               o = builder != null ? builder.build(this, 
m.getBean(), eType) : m.getBean();
                        } else {
-                               BeanMap m = newBeanMap(outer, 
sType.getInnerClass());
-                               o = parseIntoBean(r, m).getBean();
+                               BeanMap m = builder != null ? 
toBeanMap(builder.create(this, eType)) : newBeanMap(outer, 
sType.getInnerClass());
+                               m = parseIntoBean(r, m);
+                               o = builder != null ? builder.build(this, 
m.getBean(), eType) : m.getBean();
                        }
                } else if (sType.isArray() || sType.isArgs()) {
                        ArrayList l = (ArrayList)parseIntoCollection(r, new 
ArrayList(), sType, pMeta);
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerSession.java
index 9a3762f..86c4f2b 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerSession.java
@@ -349,9 +349,11 @@ public class XmlSchemaSerializerSession extends 
XmlSerializerSession {
                                        boolean hasChildElements = false;
 
                                        for (BeanPropertyMeta pMeta : 
bm.getPropertyMetas()) {
-                                               XmlFormat pMetaFormat = 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat();
-                                               if (pMetaFormat != 
XmlFormat.ATTR)
-                                                       hasChildElements = true;
+                                               if (pMeta.canRead()) {
+                                                       XmlFormat pMetaFormat = 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat();
+                                                       if (pMetaFormat != 
XmlFormat.ATTR)
+                                                               
hasChildElements = true;
+                                               }
                                        }
 
                                        XmlBeanMeta xbm2 = 
bm.getExtendedMeta(XmlBeanMeta.class);
@@ -369,17 +371,20 @@ public class XmlSchemaSerializerSession extends 
XmlSerializerSession {
                                                boolean hasCollapsed = false;
 
                                                for (BeanPropertyMeta pMeta : 
bm.getPropertyMetas()) {
-                                                       XmlBeanPropertyMeta 
xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
-                                                       if 
(xmlMeta.getXmlFormat() != ATTR) {
-                                                               if 
(xmlMeta.getNamespace() != null) {
-                                                                       
ClassMeta<?> ct2 = pMeta.getClassMeta();
-                                                                       
Namespace cNs = first(xmlMeta.getNamespace(), 
ct2.getExtendedMeta(XmlClassMeta.class).getNamespace(), 
cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
-                                                                       // 
Child element is in another namespace.
-                                                                       
schemas.queueElement(cNs, pMeta.getName(), ct2);
-                                                                       
hasOtherNsElement = true;
+                                                       if (pMeta.canRead()) {
+                                                               
XmlBeanPropertyMeta xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
+                                                               if 
(xmlMeta.getXmlFormat() != ATTR) {
+                                                                       if 
(xmlMeta.getNamespace() != null) {
+                                                                               
ClassMeta<?> ct2 = pMeta.getClassMeta();
+                                                                               
Namespace cNs = first(xmlMeta.getNamespace(), 
ct2.getExtendedMeta(XmlClassMeta.class).getNamespace(), 
cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
+                                                                               
// Child element is in another namespace.
+                                                                               
schemas.queueElement(cNs, pMeta.getName(), ct2);
+                                                                               
hasOtherNsElement = true;
+                                                                       }
+                                                                       if 
(xmlMeta.getXmlFormat() == COLLAPSED)
+                                                                               
hasCollapsed = true;
                                                                }
-                                                               if 
(xmlMeta.getXmlFormat() == COLLAPSED)
-                                                                       
hasCollapsed = true;
+                                                               
                                                        }
                                                }
 
@@ -396,30 +401,31 @@ public class XmlSchemaSerializerSession extends 
XmlSerializerSession {
                                                } else {
                                                        w.sTag(i+1, 
"all").nl(i+1);
                                                        for (BeanPropertyMeta 
pMeta : bm.getPropertyMetas()) {
-                                                               
XmlBeanPropertyMeta xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
-                                                               if 
(xmlMeta.getXmlFormat() != ATTR) {
-                                                                       boolean 
isCollapsed = xmlMeta.getXmlFormat() == COLLAPSED;
-                                                                       
ClassMeta<?> ct2 = pMeta.getClassMeta();
-                                                                       String 
childName = pMeta.getName();
-                                                                       if 
(isCollapsed) {
-                                                                               
if (xmlMeta.getChildName() != null)
-                                                                               
        childName = xmlMeta.getChildName();
-                                                                               
ct2 = pMeta.getClassMeta().getElementType();
+                                                               if 
(pMeta.canRead()) {
+                                                                       
XmlBeanPropertyMeta xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
+                                                                       if 
(xmlMeta.getXmlFormat() != ATTR) {
+                                                                               
boolean isCollapsed = xmlMeta.getXmlFormat() == COLLAPSED;
+                                                                               
ClassMeta<?> ct2 = pMeta.getClassMeta();
+                                                                               
String childName = pMeta.getName();
+                                                                               
if (isCollapsed) {
+                                                                               
        if (xmlMeta.getChildName() != null)
+                                                                               
                childName = xmlMeta.getChildName();
+                                                                               
        ct2 = pMeta.getClassMeta().getElementType();
+                                                                               
}
+                                                                               
Namespace cNs = first(xmlMeta.getNamespace(), 
ct2.getExtendedMeta(XmlClassMeta.class).getNamespace(), 
cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
+                                                                               
if (xmlMeta.getNamespace() == null) {
+                                                                               
        w.oTag(i+2, "element")
+                                                                               
                .attr("name", XmlUtils.encodeElementName(childName), false)
+                                                                               
                .attr("type", getXmlType(cNs, ct2))
+                                                                               
                .attr("minOccurs", 0);
+
+                                                                               
        w.ceTag().nl(i+2);
+                                                                               
} else {
+                                                                               
        // Child element is in another namespace.
+                                                                               
        schemas.queueElement(cNs, pMeta.getName(), ct2);
+                                                                               
        hasOtherNsElement = true;
+                                                                               
}
                                                                        }
-                                                                       
Namespace cNs = first(xmlMeta.getNamespace(), 
ct2.getExtendedMeta(XmlClassMeta.class).getNamespace(), 
cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
-                                                                       if 
(xmlMeta.getNamespace() == null) {
-                                                                               
w.oTag(i+2, "element")
-                                                                               
        .attr("name", XmlUtils.encodeElementName(childName), false)
-                                                                               
        .attr("type", getXmlType(cNs, ct2))
-                                                                               
        .attr("minOccurs", 0);
-
-                                                                               
w.ceTag().nl(i+2);
-                                                                       } else {
-                                                                               
// Child element is in another namespace.
-                                                                               
schemas.queueElement(cNs, pMeta.getName(), ct2);
-                                                                               
hasOtherNsElement = true;
-                                                                       }
-
                                                                }
                                                        }
                                                        w.eTag(i+1, 
"all").nl(i+1);
@@ -428,26 +434,28 @@ public class XmlSchemaSerializerSession extends 
XmlSerializerSession {
                                        }
 
                                        for (BeanPropertyMeta pMeta : 
bm.getExtendedMeta(XmlBeanMeta.class).getAttrProperties().values()) {
-                                               Namespace pNs = 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
-                                               if (pNs == null)
-                                                       pNs = defaultNs;
-
-                                               // If the bean attribute has a 
different namespace than the bean, then it needs to
-                                               // be added as a top-level 
entry in the appropriate schema file.
-                                               if (pNs != targetNs) {
-                                                       
schemas.queueAttribute(pNs, pMeta.getName(), pMeta.getClassMeta());
-                                                       w.oTag(i+1, "attribute")
-                                                               //.attr("name", 
pMeta.getName(), true)
-                                                               .attr("ref", 
pNs.getName() + ':' + pMeta.getName())
-                                                               
.ceTag().nl(i+1);
-                                               }
+                                               if (pMeta.canRead()) {
+                                                       Namespace pNs = 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
+                                                       if (pNs == null)
+                                                               pNs = defaultNs;
+
+                                                       // If the bean 
attribute has a different namespace than the bean, then it needs to
+                                                       // be added as a 
top-level entry in the appropriate schema file.
+                                                       if (pNs != targetNs) {
+                                                               
schemas.queueAttribute(pNs, pMeta.getName(), pMeta.getClassMeta());
+                                                               w.oTag(i+1, 
"attribute")
+                                                                       
//.attr("name", pMeta.getName(), true)
+                                                                       
.attr("ref", pNs.getName() + ':' + pMeta.getName())
+                                                                       
.ceTag().nl(i+1);
+                                                       }
 
-                                               // Otherwise, it's just a plain 
attribute of this bean.
-                                               else {
-                                                       w.oTag(i+1, "attribute")
-                                                               .attr("name", 
pMeta.getName(), true)
-                                                               .attr("type", 
getXmlAttrType(pMeta.getClassMeta()))
-                                                               
.ceTag().nl(i+1);
+                                                       // Otherwise, it's just 
a plain attribute of this bean.
+                                                       else {
+                                                               w.oTag(i+1, 
"attribute")
+                                                                       
.attr("name", pMeta.getName(), true)
+                                                                       
.attr("type", getXmlAttrType(pMeta.getClassMeta()))
+                                                                       
.ceTag().nl(i+1);
+                                                       }
                                                }
                                        }
 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
index 935dfd4..81583ea 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
@@ -210,9 +210,11 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
 
                                if (innerType.isBean()) {
                                        for (BeanPropertyMeta bpm : 
innerType.getBeanMeta().getPropertyMetas()) {
-                                               ns = 
bpm.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
-                                               if (ns != null && ns.uri != 
null)
-                                                       addNamespace(ns);
+                                               if (bpm.canRead()) {
+                                                       ns = 
bpm.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
+                                                       if (ns != null && 
ns.uri != null)
+                                                               
addNamespace(ns);
+                                               }
                                        }
 
                                } else if (innerType.isMap()) {
@@ -562,40 +564,42 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
                        String n = p.getName();
                        if (attrs.contains(n) || attrs.contains("*") || 
n.equals(attrsProperty)) {
                                BeanPropertyMeta pMeta = p.getMeta();
-                               ClassMeta<?> cMeta = p.getClassMeta();
-
-                               String key = p.getName();
-                               Object value = p.getValue();
-                               Throwable t = p.getThrown();
-                               if (t != null)
-                                       onBeanGetterException(pMeta, t);
-
-                               if (canIgnoreValue(cMeta, key, value))
-                                       continue;
-
-                               Namespace ns = (enableNamespaces && 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace() != elementNs ? 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace() : null);
-
-                               if (pMeta.isUri()  ) {
-                                       out.attrUri(ns, key, value);
-                               } else if (n.equals(attrsProperty)) {
-                                       if (value instanceof BeanMap) {
-                                               BeanMap<?> bm2 = (BeanMap)value;
-                                               for (BeanPropertyValue p2 : 
bm2.getValues(true)) {
-                                                       String key2 = 
p2.getName();
-                                                       Object value2 = 
p2.getValue();
-                                                       Throwable t2 = 
p2.getThrown();
-                                                       if (t2 != null)
-                                                               
onBeanGetterException(pMeta, t);
-                                                       out.attr(ns, key2, 
value2);
-                                               }
-                                       } else /* Map */ {
-                                               Map m2 = (Map)value;
-                                               for (Map.Entry e : 
(Set<Map.Entry>)(m2.entrySet())) {
-                                                       out.attr(ns, 
toString(e.getKey()), e.getValue());
+                               if (pMeta.canRead()) {
+                                       ClassMeta<?> cMeta = p.getClassMeta();
+
+                                       String key = p.getName();
+                                       Object value = p.getValue();
+                                       Throwable t = p.getThrown();
+                                       if (t != null)
+                                               onBeanGetterException(pMeta, t);
+
+                                       if (canIgnoreValue(cMeta, key, value))
+                                               continue;
+
+                                       Namespace ns = (enableNamespaces && 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace() != elementNs ? 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace() : null);
+
+                                       if (pMeta.isUri()  ) {
+                                               out.attrUri(ns, key, value);
+                                       } else if (n.equals(attrsProperty)) {
+                                               if (value instanceof BeanMap) {
+                                                       BeanMap<?> bm2 = 
(BeanMap)value;
+                                                       for (BeanPropertyValue 
p2 : bm2.getValues(true)) {
+                                                               String key2 = 
p2.getName();
+                                                               Object value2 = 
p2.getValue();
+                                                               Throwable t2 = 
p2.getThrown();
+                                                               if (t2 != null)
+                                                                       
onBeanGetterException(pMeta, t);
+                                                               out.attr(ns, 
key2, value2);
+                                                       }
+                                               } else /* Map */ {
+                                                       Map m2 = (Map)value;
+                                                       for (Map.Entry e : 
(Set<Map.Entry>)(m2.entrySet())) {
+                                                               out.attr(ns, 
toString(e.getKey()), e.getValue());
+                                                       }
                                                }
+                                       } else {
+                                               out.attr(ns, key, value);
                                        }
-                               } else {
-                                       out.attr(ns, key, value);
                                }
                        }
                }
@@ -607,39 +611,41 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
 
                for (BeanPropertyValue p : lp) {
                        BeanPropertyMeta pMeta = p.getMeta();
-                       ClassMeta<?> cMeta = p.getClassMeta();
+                       if (pMeta.canRead()) {
+                               ClassMeta<?> cMeta = p.getClassMeta();
 
-                       String n = p.getName();
-                       if (n.equals(contentProperty)) {
-                               content = p.getValue();
-                               contentType = p.getClassMeta();
-                               hasContent = true;
-                               cf = xbm.getContentFormat();
-                               if 
(cf.isOneOf(MIXED,MIXED_PWS,TEXT,TEXT_PWS,XMLTEXT))
-                                       isMixed = true;
-                               if (cf.isOneOf(MIXED_PWS, TEXT_PWS))
-                                       preserveWhitespace = true;
-                               if (contentType.isCollection() && 
((Collection)content).isEmpty())
-                                       hasContent = false;
-                               else if (contentType.isArray() && 
Array.getLength(content) == 0)
-                                       hasContent = false;
-                       } else if (elements.contains(n) || 
collapsedElements.contains(n) || elements.contains("*") || 
collapsedElements.contains("*") ) {
-                               String key = p.getName();
-                               Object value = p.getValue();
-                               Throwable t = p.getThrown();
-                               if (t != null)
-                                       onBeanGetterException(pMeta, t);
-
-                               if (canIgnoreValue(cMeta, key, value))
-                                       continue;
-
-                               if (! hasChildren) {
-                                       hasChildren = true;
-                                       out.appendIf(! isCollapsed, '>').nlIf(! 
isMixed, indent);
-                               }
+                               String n = p.getName();
+                               if (n.equals(contentProperty)) {
+                                       content = p.getValue();
+                                       contentType = p.getClassMeta();
+                                       hasContent = true;
+                                       cf = xbm.getContentFormat();
+                                       if 
(cf.isOneOf(MIXED,MIXED_PWS,TEXT,TEXT_PWS,XMLTEXT))
+                                               isMixed = true;
+                                       if (cf.isOneOf(MIXED_PWS, TEXT_PWS))
+                                               preserveWhitespace = true;
+                                       if (contentType.isCollection() && 
((Collection)content).isEmpty())
+                                               hasContent = false;
+                                       else if (contentType.isArray() && 
Array.getLength(content) == 0)
+                                               hasContent = false;
+                               } else if (elements.contains(n) || 
collapsedElements.contains(n) || elements.contains("*") || 
collapsedElements.contains("*") ) {
+                                       String key = p.getName();
+                                       Object value = p.getValue();
+                                       Throwable t = p.getThrown();
+                                       if (t != null)
+                                               onBeanGetterException(pMeta, t);
+
+                                       if (canIgnoreValue(cMeta, key, value))
+                                               continue;
+
+                                       if (! hasChildren) {
+                                               hasChildren = true;
+                                               out.appendIf(! isCollapsed, 
'>').nlIf(! isMixed, indent);
+                                       }
 
-                               XmlBeanPropertyMeta xbpm = 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
-                               serializeAnything(out, value, cMeta, key, 
xbpm.getNamespace(), false, xbpm.getXmlFormat(), isMixed, false, pMeta);
+                                       XmlBeanPropertyMeta xbpm = 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
+                                       serializeAnything(out, value, cMeta, 
key, xbpm.getNamespace(), false, xbpm.getXmlFormat(), isMixed, false, pMeta);
+                               }
                        }
                }
                if (! hasContent)
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserSession.java
index b494a41..2f33098 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserSession.java
@@ -113,7 +113,14 @@ public final class YamlParserSession extends 
ReaderParserSession {
                if (eType == null)
                        eType = object();
                PojoSwap<T,Object> swap = 
(PojoSwap<T,Object>)eType.getPojoSwap(this);
-               ClassMeta<?> sType = swap == null ? eType : 
swap.getSwapClassMeta(this);
+               BuilderSwap<T,Object> builder = 
(BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+               ClassMeta<?> sType = null;
+               if (builder != null)
+                       sType = builder.getBuilderClassMeta(this);
+               else if (swap != null)
+                       sType = swap.getSwapClassMeta(this);
+               else
+                       sType = eType;
                setCurrentClass(sType);
 
                Object o = null;
@@ -171,6 +178,9 @@ public final class YamlParserSession extends 
ReaderParserSession {
                                Collection l = 
(sType.canCreateNewInstance(outer) ? (Collection)sType.newInstance() : new 
ObjectList(this));
                                o = parseIntoCollection2(r, l, sType, pMeta);
                        }
+               } else if (builder != null) {
+                       BeanMap m = toBeanMap(builder.create(this, eType));
+                       o = builder.build(this, parseIntoBeanMap2(r, 
m).getBean(), eType);
                } else if (sType.canCreateNewBean(outer)) {
                        BeanMap m = newBeanMap(outer, sType.getInnerClass());
                        o = parseIntoBeanMap2(r, m).getBean();
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlSerializerSession.java
index f299a7a..21436d8 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlSerializerSession.java
@@ -174,24 +174,26 @@ public class YamlSerializerSession extends 
WriterSerializerSession {
                boolean addComma = false;
                for (BeanPropertyValue p : m.getValues(isTrimNulls(), typeName 
!= null ? createBeanTypeNameProperty(m, typeName) : null)) {
                        BeanPropertyMeta pMeta = p.getMeta();
-                       ClassMeta<?> cMeta = p.getClassMeta();
-                       String key = p.getName();
-                       Object value = p.getValue();
-                       Throwable t = p.getThrown();
-                       if (t != null)
-                               onBeanGetterException(pMeta, t);
+                       if (pMeta.canRead()) {
+                               ClassMeta<?> cMeta = p.getClassMeta();
+                               String key = p.getName();
+                               Object value = p.getValue();
+                               Throwable t = p.getThrown();
+                               if (t != null)
+                                       onBeanGetterException(pMeta, t);
 
-                       if (canIgnoreValue(cMeta, key, value))
-                               continue;
+                               if (canIgnoreValue(cMeta, key, value))
+                                       continue;
 
-                       if (addComma)
-                               out.append(',').smi(i);
+                               if (addComma)
+                                       out.append(',').smi(i);
 
-                       out.cr(i).attr(key).append(':').s(i);
+                               out.cr(i).attr(key).append(':').s(i);
 
-                       serializeAnything(out, value, cMeta, key, pMeta);
+                               serializeAnything(out, value, cMeta, key, 
pMeta);
 
-                       addComma = true;
+                               addComma = true;
+                       }
                }
                out.cre(i-1).append('}');
                return out;
diff --git a/juneau-doc/src/main/javadoc/overview.html 
b/juneau-doc/src/main/javadoc/overview.html
index de1b32f..404ba88 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -105,6 +105,7 @@
                                <li><p><a class='doclink' 
href='#juneau-marshall.BeanIgnoreAnnotation'>@BeanIgnore Annotation</a></p>
                                <li><p><a class='doclink' 
href='#juneau-marshall.NamePropertyAnnotation'>@NameProperty Annotation</a></p>
                                <li><p><a class='doclink' 
href='#juneau-marshall.ParentPropertyAnnotation'>@ParentProperty 
Annotation</a></p>
+                               <li><p><a class='doclink' 
href='#juneau-marshall.PojoBuilders'>POJO Builders</a></p>
                                <li><p><a class='doclink' 
href='#juneau-marshall.URIs'>URIs</a></p>
                                <li><p><a class='doclink' 
href='#juneau-marshall.BeanFilters'>BeanFilters</a></p>
                                <li><p><a class='doclink' 
href='#juneau-marshall.InterfaceFilters'>Interface filters</a></p>
@@ -2347,8 +2348,117 @@
                        </div>
 
                        <!-- 
===================================================================================================
 -->
+                       <a id="juneau-marshall.PojoBuilders"></a>
+                       <h4 class='topic' onclick='toggle(this)'>2.1.7.14 - 
POJO Builders</h4>
+                       <div class='topic'>
+                               <p>
+                                       Juneau parsers can use builders to 
instantiate POJOs.
+                                       <br>This is useful in cases where you 
want to create beans with read-only properties.
+                                       <br>Note that while it's possible to do 
this using the {@link org.apache.juneau.annotation.BeanConstructor 
@BeanConstructor}
+                                       annotation, using builders can often be 
cleaner.
+                               </p>
+                               <p>
+                                       A typical builder usage is shown below:
+                               </p>
+                               <p class='bcode'>
+       MyBean b = 
MyBean.<jsm>create</jsm>().foo(<js>"foo"</js>).bar(123).build();
+                               </p>                                            
                
+                               <p>
+                                       The code for such a builder is shown 
below:
+                               </p>
+                               <p class='bcode'>
+       <jk>public class</jk> MyBean {
+       
+               <jc>// Read-only properties.</jc>
+               <jk>public final</jk> String <jf>foo</jf>;
+               <jk>public final int</jk> <jf>bar</jf>;
+               
+               <jc>// Private constructor.</jc>
+               <jk>private</jk> MyBean(MyBeanBuilder b) {
+                       <jk>this</jk>.<jf>foo</jf> = b.foo;
+                       <jk>this</jk>.<jf>bar</jf> = b.bar;
+               }
+
+               <jc>// Static method that creates a builder.</jc>
+               <jk>public static</jk> MyBeanBuilder <jsm>create</jsm>() {
+                       <jk>return new</jk> MyBeanBuilder();
+               }
+
+               <jc>// Builder class.</jc>
+               <jk>public static class</jk> MyBeanBuilder {
+                       <jk>private</jk> String <jf>foo</jf>;
+                       <jk>private int</jk> <jf>bar</jf>;
+                       
+                       <jc>// Method that creates the bean.</jc>
+                       <jk>public</jk> MyBean build() {
+                               <jk>return new</jk> MyBean(<jk>this</jk>);
+                       }
+                       
+                       <jc>// Bean property setters.</jc>
+                       
+                       <ja>@BeanProperty</ja>
+                       <jk>public</jk> MyBeanBuilder foo(String foo) {
+                               <jk>this</jk>.<jf>foo</jf> = foo;
+                               <jk>return this</jk>;
+                       }
+                       
+                       <ja>@BeanProperty</ja>
+                       <jk>public</jk> MyBeanBuilder bar(<jk>int</jk> bar) {
+                               <jk>this</jk>.<jf>bar</jf> = bar;
+                               <jk>return this</jk>;
+                       }
+               }
+       }
+                               </p>
+                               <p>
+                                       Builders MUST be beans with one or more 
writable properties.
+                                       <br>The bean properties themselves do 
not need to be readable (e.g. setters without getters).
+                               </p>
+                               <p>
+                                       Builders require two parts:
+                               </p>
+                               <ol>
+                                       <li>A way to detect and instantiate a 
builder using reflection.
+                                       <li>A way to instantiate a POJO from a 
builder.
+                               </ol>
+                               <p>
+                                       The first can be accomplished through 
any of the following:
+                               </p>
+                               <ul class='spaced-list'>
+                                       <li>A static <code>create()</code> 
method on the POJO class that returns a builder instance.
+                                               <p class='bcode'>
+       <jk>public static</jk> MyBuilder <jsm>create</jsm>() {...}
+                                               </p>
+                                       <li>A public constructor on the POJO 
class that takes in a single parameter that implements the {@link 
org.apache.juneau.transform.Builder} interface.
+                                               <br>The builder class must have 
a public no-arg constructor.
+                                               <p class='bcode'>
+       <jk>public</jk> MyPojo(MyBuilder b) {...}
+                                               </p>
+                                       <li>A {@link 
org.apache.juneau.annotation.Builder @Builder} annotation on the POJO class.
+                                               <br>The builder class must have 
a public no-arg constructor.
+                                               <p class='bcode'>
+       <ja>@Builder</ja>(MyBuilder.<jk>class</jk>)
+       <jk>public class</jk> MyPojo {...}
+                                               </p>
+                               </ul>
+                               <p>
+                                       The second can be accomplished through 
any of the following:
+                               </p>
+                               <ul class='spaced-list'>
+                                       <li>The existence of a 
<code>build()</code> method on the builder class.
+                                               <p class='bcode'>
+       <jk>public</jk> MyPojo build() {...}
+                                               </p>
+                                       <li>The existence of a public 
constructor on the POJO class that takes in the builder instance.
+                                               <p class='bcode'>
+       <jk>public</jk> MyPojo(MyBuilder b) {...}
+                                               </p>
+                               </ul>
+                       </div>
+                       
+                       <!-- 
===================================================================================================
 -->
                        <a id="juneau-marshall.URIs"></a>
-                       <h4 class='topic' onclick='toggle(this)'>2.1.7.14 - 
URIs</h4>
+                       <h4 class='topic' onclick='toggle(this)'>2.1.7.15 - 
URIs</h4>
                        <div class='topic'>
                                <p>
                                        Juneau serializers have sophisticated 
support for transforming relative URIs to absolute form.
@@ -2477,7 +2587,7 @@
 
                        <!-- 
=======================================================================================================
 -->
                        <a id="juneau-marshall.BeanFilters"></a>
-                       <h4 class='topic' onclick='toggle(this)'>2.1.7.15 - 
BeanFilter Class</h4>
+                       <h4 class='topic' onclick='toggle(this)'>2.1.7.16 - 
BeanFilter Class</h4>
                        <div class='topic'>
                                <p>
                                        The {@link 
org.apache.juneau.transform.BeanFilter} class is the programmatic equivalent to 
the
@@ -2566,7 +2676,7 @@
 
                        <!-- 
===================================================================================================
 -->
                        <a id="juneau-marshall.InterfaceFilters"></a>
-                       <h4 class='topic' onclick='toggle(this)'>2.1.7.16 - 
Interface Filters</h4>
+                       <h4 class='topic' onclick='toggle(this)'>2.1.7.17 - 
Interface Filters</h4>
                        <div class='topic'>
                                <p>
                                        Occasionally, you may want to limit 
bean properties to only those defined on a parent class or interface.  
@@ -2670,7 +2780,7 @@
                        
                        <!-- 
===================================================================================================
 -->
                        <a id="juneau-marshall.StopClasses"></a>
-                       <h4 class='topic' onclick='toggle(this)'>2.1.7.17 - 
Stop Classes</h4>
+                       <h4 class='topic' onclick='toggle(this)'>2.1.7.18 - 
Stop Classes</h4>
                        <div class='topic'>
                                <p>
                                        Whereas interface filters limit 
properties defined on child classes, stop filters 
@@ -2712,7 +2822,7 @@
 
                        <!-- 
===================================================================================================
 -->
                        <a id="juneau-marshall.BypassSerialization"></a>
-                       <h4 class='topic' onclick='toggle(this)'>2.1.7.18 - 
Bypass Serialization using Readers and InputStreams</h4>
+                       <h4 class='topic' onclick='toggle(this)'>2.1.7.19 - 
Bypass Serialization using Readers and InputStreams</h4>
                        <div class='topic'>
                                <p>
                                        Juneau serializers treat instances of 
<code>Readers</code> and <code>InputStreams</code> special by 
@@ -3054,7 +3164,8 @@
                        </p>
                        <h6 class='figure'>Examples:</h6>
                        <p class='bcode'>
-       ReaderParser p = JsonParser.<jsm>create</jsm>().unbuffered().build();
+       <jc>// If you're calling parse on the same input multiple times, use a 
session instead of the parser directly.</jc>
+       ReaderParserSession p = 
JsonParser.<jsm>create</jsm>().unbuffered().build().createSession();
        Object x;
        Reader r;
 
@@ -12595,9 +12706,17 @@
                        <li>
                                New {@link 
org.apache.juneau.parser.Parser#PARSER_autoCloseStreams} setting allows input 
streams and
                                readers passed into parsers to be automatically 
closed after parsing.
+                       <li>
+                               Syntax changed on unswap method on {@link 
org.apache.juneau.transform.Surrogate} classes.
+                               <br>It's now a regular method instead of a 
static method.
+                       <li>
+                               {@link org.apache.juneau.annotation.Swap @Swap} 
annotation can now be used with 
+                               {@link org.apache.juneau.transform.Surrogate} 
classes.
+                       <li>
+                               New support for <a class='doclink' 
href='#juneau-marshall.PojoBuilders'>POJO Builders</a>.      
                </ul>
 
-               <h6 class='topic'>juneau-marshall</h6>
+               <h6 class='topic'>juneau-dto</h6>
                <ul class='spaced-list'>
                        <li>
                                Enhancements to Swagger DTO:
@@ -12614,12 +12733,6 @@
                                                Setter methods that take in 
beans and collections of beans can now take in 
                                                JSON strings.
                                </ul>
-                       <li>
-                               Syntax changed on unswap method on {@link 
org.apache.juneau.transform.Surrogate} classes.
-                               <br>It's now a regular method instead of a 
static method.
-                       <li>
-                               {@link org.apache.juneau.annotation.Swap @Swap} 
annotation can now be used with 
-                               {@link org.apache.juneau.transform.Surrogate} 
classes.
                </ul>
                
                <h6 class='topic'>juneau-rest-server</h6>
@@ -17738,4 +17851,3 @@
 
 </div>
 </body>                                                                
-       

-- 
To stop receiving notification emails like this one, please contact
[email protected].

Reply via email to