Better representation of null bean content properties.
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java
index 4ee4b0b..918c67c 100755
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java
@@ -16,11 +16,13 @@
 import static org.junit.Assert.*;

 

 import java.util.*;

+import java.util.Map;

 

 import javax.xml.datatype.*;

 

 import org.apache.juneau.*;

 import org.apache.juneau.annotation.*;

+import org.apache.juneau.dto.html5.*;

 import org.apache.juneau.json.*;

 import org.apache.juneau.json.annotation.*;

 import org.apache.juneau.parser.*;

@@ -959,4 +961,33 @@
 			return m;

 		}

 	}

+

+	//====================================================================================================

+	// testBeanPropertyWithBeanWithAttrsField

+	//====================================================================================================

+

+	@Test

+	public void testBeanPropertyWithBeanWithAttrsField() throws Exception {

+		N t = N.create();

+		t = roundTrip(t, N.class);

+

+		t.f1.type("foo");

+		t = roundTrip(t, N.class);

+

+		t.f1.attr("foo", "bar").attrUri("href", "http://foo");

+		t = roundTrip(t, N.class);

+

+		Head h = new Head().child(new Style());

+		h = roundTrip(h, Head.class);

+	}

+

+	public static class N {

+		public Style f1;

+

+		static N create() {

+			N n = new N();

+			n.f1 = new Style();

+			return n;

+		}

+	}

 }

diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/dto/html5/Html5ComboTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/dto/html5/Html5ComboTest.java
index 9091240..2a3af7d 100755
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/dto/html5/Html5ComboTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/dto/html5/Html5ComboTest.java
@@ -177,13 +177,13 @@
 					/* Json */		"{_type:'a',a:{href:'http://foo'}}",

 					/* JsonT */		"{t:'a',a:{href:'http://foo'}}",

 					/* JsonR */		"{\n\t_type: 'a',\n\ta: {\n\t\thref: 'http://foo'\n\t}\n}",

-					/* Xml */		"<a href='http://foo'/>",

-					/* XmlT */		"<a href='http://foo'/>",

-					/* XmlR */		"<a href='http://foo'/>\n",

-					/* XmlNs */		"<a href='http://foo'/>",

-					/* Html */		"<a href='http://foo'></a>",

-					/* HtmlT */		"<a href='http://foo'></a>",

-					/* HtmlR */		"<a href='http://foo'></a>\n",

+					/* Xml */		"<a href='http://foo' nil='true'></a>",

+					/* XmlT */		"<a href='http://foo' nil='true'></a>",

+					/* XmlR */		"<a href='http://foo' nil='true'>\n</a>\n",

+					/* XmlNs */		"<a href='http://foo' nil='true'></a>",

+					/* Html */		"<a href='http://foo' nil='true'></a>",

+					/* HtmlT */		"<a href='http://foo' nil='true'></a>",

+					/* HtmlR */		"<a href='http://foo' nil='true'>\n</a>\n",

 					/* Uon */		"(_type=a,a=(href=http://foo))",

 					/* UonT */		"(t=a,a=(href=http://foo))",

 					/* UonR */		"(\n\t_type=a,\n\ta=(\n\t\thref=http://foo\n\t)\n)",

@@ -279,13 +279,13 @@
 					/* Json */		"{_type:'address'}",

 					/* JsonT */		"{t:'address'}",

 					/* JsonR */		"{\n\t_type: 'address'\n}",

-					/* Xml */		"<address/>",

-					/* XmlT */		"<address/>",

-					/* XmlR */		"<address/>\n",

-					/* XmlNs */		"<address/>",

-					/* Html */		"<address></address>",

-					/* HtmlT */		"<address></address>",

-					/* HtmlR */		"<address></address>\n",

+					/* Xml */		"<address nil='true'></address>",

+					/* XmlT */		"<address nil='true'></address>",

+					/* XmlR */		"<address nil='true'>\n</address>\n",

+					/* XmlNs */		"<address nil='true'></address>",

+					/* Html */		"<address nil='true'></address>",

+					/* HtmlT */		"<address nil='true'></address>",

+					/* HtmlR */		"<address nil='true'>\n</address>\n",

 					/* Uon */		"(_type=address)",

 					/* UonT */		"(t=address)",

 					/* UonR */		"(\n\t_type=address\n)",

@@ -667,13 +667,13 @@
 					/* Json */		"{_type:'canvas',a:{width:100,height:200}}",

 					/* JsonT */		"{t:'canvas',a:{width:100,height:200}}",

 					/* JsonR */		"{\n\t_type: 'canvas',\n\ta: {\n\t\twidth: 100,\n\t\theight: 200\n\t}\n}",

-					/* Xml */		"<canvas width='100' height='200'/>",

-					/* XmlT */		"<canvas width='100' height='200'/>",

-					/* XmlR */		"<canvas width='100' height='200'/>\n",

-					/* XmlNs */		"<canvas width='100' height='200'/>",

-					/* Html */		"<canvas width='100' height='200'></canvas>",

-					/* HtmlT */		"<canvas width='100' height='200'></canvas>",

-					/* HtmlR */		"<canvas width='100' height='200'></canvas>\n",

+					/* Xml */		"<canvas width='100' height='200' nil='true'></canvas>",

+					/* XmlT */		"<canvas width='100' height='200' nil='true'></canvas>",

+					/* XmlR */		"<canvas width='100' height='200' nil='true'>\n</canvas>\n",

+					/* XmlNs */		"<canvas width='100' height='200' nil='true'></canvas>",

+					/* Html */		"<canvas width='100' height='200' nil='true'></canvas>",

+					/* HtmlT */		"<canvas width='100' height='200' nil='true'></canvas>",

+					/* HtmlR */		"<canvas width='100' height='200' nil='true'>\n</canvas>\n",

 					/* Uon */		"(_type=canvas,a=(width=100,height=200))",

 					/* UonT */		"(t=canvas,a=(width=100,height=200))",

 					/* UonR */		"(\n\t_type=canvas,\n\ta=(\n\t\twidth=100,\n\t\theight=200\n\t)\n)",

@@ -1657,13 +1657,13 @@
 					/* Json */		"{_type:'form',a:{action:'testform',oninput:'x.value=parseInt(a.value)+parseInt(b.value)'},c:[0,{_type:'input',a:{type:'range',id:'a',value:50}},'+',{_type:'input',a:{type:'number',id:'b',value:50}},'=',{_type:'output',a:{name:'x','for':'a b'}}]}",

 					/* JsonT */		"{t:'form',a:{action:'testform',oninput:'x.value=parseInt(a.value)+parseInt(b.value)'},c:[0,{t:'input',a:{type:'range',id:'a',value:50}},'+',{t:'input',a:{type:'number',id:'b',value:50}},'=',{t:'output',a:{name:'x','for':'a b'}}]}",

 					/* JsonR */		"{\n\t_type: 'form',\n\ta: {\n\t\taction: 'testform',\n\t\toninput: 'x.value=parseInt(a.value)+parseInt(b.value)'\n\t},\n\tc: [\n\t\t0,\n\t\t{\n\t\t\t_type: 'input',\n\t\t\ta: {\n\t\t\t\ttype: 'range',\n\t\t\t\tid: 'a',\n\t\t\t\tvalue: 50\n\t\t\t}\n\t\t},\n\t\t'+',\n\t\t{\n\t\t\t_type: 'input',\n\t\t\ta: {\n\t\t\t\ttype: 'number',\n\t\t\t\tid: 'b',\n\t\t\t\tvalue: 50\n\t\t\t}\n\t\t},\n\t\t'=',\n\t\t{\n\t\t\t_type: 'output',\n\t\t\ta: {\n\t\t\t\tname: 'x',\n\t\t\t\t'for': 'a b'\n\t\t\t}\n\t\t}\n\t]\n}",

-					/* Xml */		"<form action='testform' oninput='x.value=parseInt(a.value)+parseInt(b.value)'>0<input type='range' id='a' value='50'/>+<input type='number' id='b' value='50'/>=<output name='x' for='a b'/></form>",

-					/* XmlT */		"<form action='testform' oninput='x.value=parseInt(a.value)+parseInt(b.value)'>0<input type='range' id='a' value='50'/>+<input type='number' id='b' value='50'/>=<output name='x' for='a b'/></form>",

-					/* XmlR */		"<form action='testform' oninput='x.value=parseInt(a.value)+parseInt(b.value)'>0<input type='range' id='a' value='50'/>+<input type='number' id='b' value='50'/>=<output name='x' for='a b'/></form>\n",

-					/* XmlNs */		"<form action='testform' oninput='x.value=parseInt(a.value)+parseInt(b.value)'>0<input type='range' id='a' value='50'/>+<input type='number' id='b' value='50'/>=<output name='x' for='a b'/></form>",

-					/* Html */		"<form action='testform' oninput='x.value=parseInt(a.value)+parseInt(b.value)'>0<input type='range' id='a' value='50'/>+<input type='number' id='b' value='50'/>=<output name='x' for='a b'></output></form>",

-					/* HtmlT */		"<form action='testform' oninput='x.value=parseInt(a.value)+parseInt(b.value)'>0<input type='range' id='a' value='50'/>+<input type='number' id='b' value='50'/>=<output name='x' for='a b'></output></form>",

-					/* HtmlR */		"<form action='testform' oninput='x.value=parseInt(a.value)+parseInt(b.value)'>0<input type='range' id='a' value='50'/>+<input type='number' id='b' value='50'/>=<output name='x' for='a b'></output></form>\n",

+					/* Xml */		"<form action='testform' oninput='x.value=parseInt(a.value)+parseInt(b.value)'>0<input type='range' id='a' value='50'/>+<input type='number' id='b' value='50'/>=<output name='x' for='a b' nil='true'></output></form>",

+					/* XmlT */		"<form action='testform' oninput='x.value=parseInt(a.value)+parseInt(b.value)'>0<input type='range' id='a' value='50'/>+<input type='number' id='b' value='50'/>=<output name='x' for='a b' nil='true'></output></form>",

+					/* XmlR */		"<form action='testform' oninput='x.value=parseInt(a.value)+parseInt(b.value)'>0<input type='range' id='a' value='50'/>+<input type='number' id='b' value='50'/>=<output name='x' for='a b' nil='true'></output></form>\n",

+					/* XmlNs */		"<form action='testform' oninput='x.value=parseInt(a.value)+parseInt(b.value)'>0<input type='range' id='a' value='50'/>+<input type='number' id='b' value='50'/>=<output name='x' for='a b' nil='true'></output></form>",

+					/* Html */		"<form action='testform' oninput='x.value=parseInt(a.value)+parseInt(b.value)'>0<input type='range' id='a' value='50'/>+<input type='number' id='b' value='50'/>=<output name='x' for='a b' nil='true'></output></form>",

+					/* HtmlT */		"<form action='testform' oninput='x.value=parseInt(a.value)+parseInt(b.value)'>0<input type='range' id='a' value='50'/>+<input type='number' id='b' value='50'/>=<output name='x' for='a b' nil='true'></output></form>",

+					/* HtmlR */		"<form action='testform' oninput='x.value=parseInt(a.value)+parseInt(b.value)'>0<input type='range' id='a' value='50'/>+<input type='number' id='b' value='50'/>=<output name='x' for='a b' nil='true'></output></form>\n",

 					/* Uon */		"(_type=form,a=(action=testform,oninput='x.value=parseInt(a.value)+parseInt(b.value)'),c=@(0,(_type=input,a=(type=range,id=a,value=50)),+,(_type=input,a=(type=number,id=b,value=50)),'=',(_type=output,a=(name=x,for='a b'))))",

 					/* UonT */		"(t=form,a=(action=testform,oninput='x.value=parseInt(a.value)+parseInt(b.value)'),c=@(0,(t=input,a=(type=range,id=a,value=50)),+,(t=input,a=(type=number,id=b,value=50)),'=',(t=output,a=(name=x,for='a b'))))",

 					/* UonR */		"(\n\t_type=form,\n\ta=(\n\t\taction=testform,\n\t\toninput='x.value=parseInt(a.value)+parseInt(b.value)'\n\t),\n\tc=@(\n\t\t0,\n\t\t(\n\t\t\t_type=input,\n\t\t\ta=(\n\t\t\t\ttype=range,\n\t\t\t\tid=a,\n\t\t\t\tvalue=50\n\t\t\t)\n\t\t),\n\t\t+,\n\t\t(\n\t\t\t_type=input,\n\t\t\ta=(\n\t\t\t\ttype=number,\n\t\t\t\tid=b,\n\t\t\t\tvalue=50\n\t\t\t)\n\t\t),\n\t\t'=',\n\t\t(\n\t\t\t_type=output,\n\t\t\ta=(\n\t\t\t\tname=x,\n\t\t\t\tfor='a b'\n\t\t\t)\n\t\t)\n\t)\n)",

@@ -1762,13 +1762,13 @@
 					/* Json */		"{_type:'progress',a:{value:1}}",

 					/* JsonT */		"{t:'progress',a:{value:1}}",

 					/* JsonR */		"{\n\t_type: 'progress',\n\ta: {\n\t\tvalue: 1\n\t}\n}",

-					/* Xml */		"<progress value='1'/>",

-					/* XmlT */		"<progress value='1'/>",

-					/* XmlR */		"<progress value='1'/>\n",

-					/* XmlNs */		"<progress value='1'/>",

-					/* Html */		"<progress value='1'></progress>",

-					/* HtmlT */		"<progress value='1'></progress>",

-					/* HtmlR */		"<progress value='1'></progress>\n",

+					/* Xml */		"<progress value='1' nil='true'></progress>",

+					/* XmlT */		"<progress value='1' nil='true'></progress>",

+					/* XmlR */		"<progress value='1' nil='true'>\n</progress>\n",

+					/* XmlNs */		"<progress value='1' nil='true'></progress>",

+					/* Html */		"<progress value='1' nil='true'></progress>",

+					/* HtmlT */		"<progress value='1' nil='true'></progress>",

+					/* HtmlR */		"<progress value='1' nil='true'>\n</progress>\n",

 					/* Uon */		"(_type=progress,a=(value=1))",

 					/* UonT */		"(t=progress,a=(value=1))",

 					/* UonR */		"(\n\t_type=progress,\n\ta=(\n\t\tvalue=1\n\t)\n)",

diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/BasicHtmlTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/BasicHtmlTest.java
index d39c3b6..e1fbcdc 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/BasicHtmlTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/BasicHtmlTest.java
@@ -2174,9 +2174,9 @@
 					"BeanWithWhitespaceTextFields-1",
 					BeanWithWhitespaceTextFields.class,
 					new BeanWithWhitespaceTextFields().init(null),
-					"<object></object>",
-					"<object></object>\n",
-					"<object></object>"
+					"<object nil='true'></object>",
+					"<object nil='true'>\n</object>\n",
+					"<object nil='true'></object>"
 				)
 				{
 					@Override
@@ -2254,9 +2254,9 @@
 					"BeanWithWhitespaceTextPwsFields-1",
 					BeanWithWhitespaceTextPwsFields.class,
 					new BeanWithWhitespaceTextPwsFields().init(null),
-					"<object></object>",
-					"<object></object>\n",
-					"<object></object>"
+					"<object nil='true'></object>",
+					"<object nil='true'>\n</object>\n",
+					"<object nil='true'></object>"
 				)
 				{
 					@Override
@@ -2334,9 +2334,9 @@
 					"BeanWithWhitespaceMixedFields-1",
 					BeanWithWhitespaceMixedFields.class,
 					new BeanWithWhitespaceMixedFields().init(null),
-					"<object></object>",
-					"<object></object>\n",
-					"<object></object>"
+					"<object nil='true'></object>",
+					"<object nil='true'>\n</object>\n",
+					"<object nil='true'></object>"
 				)
 				{
 					@Override
@@ -2430,9 +2430,9 @@
 					"BeanWithWhitespaceMixedPwsFields-1",
 					BeanWithWhitespaceMixedPwsFields.class,
 					new BeanWithWhitespaceMixedPwsFields().init(null),
-					"<object></object>",
-					"<object></object>\n",
-					"<object></object>"
+					"<object nil='true'></object>",
+					"<object nil='true'>\n</object>\n",
+					"<object nil='true'></object>"
 				)
 				{
 					@Override
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/BasicXmlTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/BasicXmlTest.java
index e1911ad..a126acc 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/BasicXmlTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/BasicXmlTest.java
@@ -940,9 +940,9 @@
 			{	/* 42 */
 				"BeanWithWhitespaceTextFields-1",
 				new BeanWithWhitespaceTextFields().init(null),
-				"<object/>",
-				"<object/>\n",
-				"<object/>",
+				"<object nil='true'></object>",
+				"<object nil='true'>\n</object>\n",
+				"<object nil='true'></object>",
 			},
 			{	/* 43 */
 				"BeanWithWhitespaceTextFields-2",
@@ -975,9 +975,9 @@
 			{	/* 47 */
 				"BeanWithWhitespaceTextPwsFields-1",
 				new BeanWithWhitespaceTextPwsFields().init(null),
-				"<object/>",
-				"<object/>\n",
-				"<object/>",
+				"<object nil='true'></object>",
+				"<object nil='true'>\n</object>\n",
+				"<object nil='true'></object>",
 			},
 			{	/* 48 */
 				"BeanWithWhitespaceTextPwsFields-2",
@@ -1010,16 +1010,16 @@
 			{	/* 52 */
 				"BeanWithWhitespaceMixedFields-1",
 				new BeanWithWhitespaceMixedFields().init(null),
-				"<object/>",
-				"<object/>\n",
-				"<object/>",
+				"<object nil='true'></object>",
+				"<object nil='true'>\n</object>\n",
+				"<object nil='true'></object>",
 			},
 			{	/* 53 */
 				"BeanWithWhitespaceMixedFields-2",
 				new BeanWithWhitespaceMixedFields().init(new String[0]),
-				"<object/>",
-				"<object/>\n",
-				"<object/>",
+				"<object></object>",
+				"<object></object>\n",
+				"<object></object>",
 			},
 			{	/* 54 */
 				"BeanWithWhitespaceMixedFields-3",
@@ -1052,16 +1052,16 @@
 			{	/* 58 */
 				"BeanWithWhitespaceMixedPwsFields-1",
 				new BeanWithWhitespaceMixedPwsFields().init(null),
-				"<object/>",
-				"<object/>\n",
-				"<object/>",
+				"<object nil='true'></object>",
+				"<object nil='true'>\n</object>\n",
+				"<object nil='true'></object>",
 			},
 			{	/* 59 */
 				"BeanWithWhitespaceMixedPwsFields-2",
 				new BeanWithWhitespaceMixedPwsFields().init(new String[0]),
-				"<object/>",
-				"<object/>\n",
-				"<object/>",
+				"<object></object>",
+				"<object></object>\n",
+				"<object></object>",
 			},
 			{	/* 60 */
 				"BeanWithWhitespaceMixedPwsFields-3",
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/XmlContentTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/XmlContentTest.java
index d735513..9b5dcbf 100755
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/XmlContentTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/XmlContentTest.java
@@ -47,7 +47,7 @@
 		session = s1.createSession(SerializerSessionArgs.create());

 		session.serialize(t, sw);

 		r = sw.toString();

-		assertEquals("<A f1='f1'>_x0000_</A>", r);

+		assertEquals("<A f1='f1' nil='true'></A>", r);

 		t2 = p.parse(r, A.class);

 		assertEqualObjects(t, t2);

 

@@ -55,7 +55,7 @@
 		session = s2.createSession(SerializerSessionArgs.create());

 		session.serialize(t, sw);

 		r = sw.toString();

-		assertEquals("<A f1='f1'>_x0000_</A>\n", r);

+		assertEquals("<A f1='f1' nil='true'></A>\n", r);

 		t2 = p.parse(r, A.class);

 		assertEqualObjects(t, t2);

 

@@ -155,7 +155,7 @@
 		session = s1.createSession(SerializerSessionArgs.create());

 		session.serialize(t, sw);

 		r = sw.toString();

-		assertEquals("<A f1='f1'>_x0000_</A>", r);

+		assertEquals("<A f1='f1' nil='true'></A>", r);

 		t2 = p.parse(r, B.class);

 		assertEqualObjects(t, t2);

 

@@ -163,7 +163,7 @@
 		session = s2.createSession(SerializerSessionArgs.create());

 		session.serialize(t, sw);

 		r = sw.toString();

-		assertEquals("<A f1='f1'>_x0000_</A>\n", r);

+		assertEquals("<A f1='f1' nil='true'></A>\n", r);

 		t2 = p.parse(r, B.class);

 		assertEqualObjects(t, t2);

 

diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/XmlIgnoreCommentsTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/XmlIgnoreCommentsTest.java
index 37b87ae..e163eee 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/XmlIgnoreCommentsTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/XmlIgnoreCommentsTest.java
@@ -604,7 +604,7 @@
 				"BeanWithWhitespaceMixedFields-1",
 				BeanWithWhitespaceMixedFields.class,
 				new BeanWithWhitespaceMixedFields().init(null),
-				"|<object/>|",
+				"|<object nil='true'></object>|",
 				false
 			},
 			{ 	/* 53 */
@@ -639,7 +639,7 @@
 				"BeanWithWhitespaceMixedPwsFields-1",
 				BeanWithWhitespaceMixedPwsFields.class,
 				new BeanWithWhitespaceMixedPwsFields().init(null),
-				"|<object/>|",
+				"|<object nil='true'></object>|",
 				false
 			},
 			{ 	/* 58 */
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/html5/HtmlElement.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/html5/HtmlElement.java
index 912d939..54ab888 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/html5/HtmlElement.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/html5/HtmlElement.java
@@ -60,10 +60,12 @@
 	 */

 	@Beanp("a")

 	public HtmlElement setAttrs(LinkedHashMap<String,Object> attrs) {

-		for (Entry<String,Object> e : attrs.entrySet()) {

-			String key = e.getKey();

-			if ("url".equals(key) || "href".equals(key) || key.endsWith("action"))

-				e.setValue(StringUtils.toURI(e.getValue()));

+		if (attrs != null) {

+			for (Entry<String,Object> e : attrs.entrySet()) {

+				String key = e.getKey();

+				if ("url".equals(key) || "href".equals(key) || key.endsWith("action"))

+					e.setValue(StringUtils.toURI(e.getValue()));

+			}

 		}

 		this.attrs = attrs;

 		return this;

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 fd31ca6..bf4678b 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
@@ -581,7 +581,9 @@
 	 * Equivalent to calling {@link BeanMap#get(Object)}, but is faster since it avoids looking up the property meta.

 	 *

 	 * @param m The bean map to get the transformed value from.

-	 * @param pName The property name.

+	 * @param pName

+	 * 	The property name if this is a dyna property (i.e. <js>"*"</js>).

+	 * 	<br>Otherwise can be <jk>null</jk>.

 	 * @return

 	 * 	The property value.

 	 * 	<br>Returns <jk>null</jk> if this is a write-only property.

@@ -616,7 +618,9 @@
 	 * Equivalent to calling {@link BeanMap#getRaw(Object)}, but is faster since it avoids looking up the property meta.

 	 *

 	 * @param m The bean map to get the transformed value from.

-	 * @param pName The property name.

+	 * @param pName

+	 * 	The property name if this is a dyna property (i.e. <js>"*"</js>).

+	 * 	<br>Otherwise can be <jk>null</jk>.

 	 * @return The raw property value.

 	 */

 	public Object getRaw(BeanMap<?> m, String pName) {

@@ -680,7 +684,9 @@
 	 * This is a no-op on a read-only property.

 	 *

 	 * @param m The bean map to set the property value on.

-	 * @param pName The property name.

+	 * @param pName

+	 * 	The property name if this is a dyna property (i.e. <js>"*"</js>).

+	 * 	<br>Otherwise can be <jk>null</jk>.

 	 * @param value The value to set.

 	 * @return The previous property value.

 	 * @throws BeanRuntimeException If property could not be set.

@@ -966,7 +972,9 @@
 	 * larger array on each operation.

 	 *

 	 * @param m The bean of the field being set.

-	 * @param pName The property name.

+	 * @param pName

+	 * 	The property name if this is a dyna property (i.e. <js>"*"</js>).

+	 * 	<br>Otherwise can be <jk>null</jk>.

 	 * @param value The value to add to the field.

 	 * @throws BeanRuntimeException If field is not a collection or array.

 	 */

@@ -1042,7 +1050,9 @@
 	 * Adds a value to a {@link Map} or bean property.

 	 *

 	 * @param m The bean of the field being set.

-	 * @param pName The property name.

+	 * @param pName

+	 * 	The property name if this is a dyna property (i.e. <js>"*"</js>).

+	 * 	<br>Otherwise can be <jk>null</jk>.

 	 * @param key The key to add to the field.

 	 * @param value The value to add to the field.

 	 * @throws BeanRuntimeException If field is not a map or array.

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 151fbeb..8e66874 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
@@ -99,7 +99,7 @@
 		else

 			sType = eType;

 

-		if (sType.isOptional()) 

+		if (sType.isOptional())

 			return (T)Optional.ofNullable(parseAnything(eType.getElementType(), r, outer, isRoot, pMeta));

 

 		setCurrentClass(sType);

@@ -511,7 +511,9 @@
 					pMeta.set(m, key, value);

 				}

 			}

-			nextTag(r, xTR);

+			HtmlTag t = nextTag(r, xTD, xTR);

+			if (t == xTD)

+				nextTag(r, xTR);

 		}

 		return m;

 	}

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 e926d69..7213563 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
@@ -164,7 +164,7 @@
 	 * @throws IOException If a problem occurred trying to send output to the writer.

 	 */

 	private XmlWriter doSerialize(Object o, XmlWriter w) throws IOException, SerializeException {

-		serializeAnything(w, o, getExpectedRootType(o), null, null, getInitialDepth()-1, true);

+		serializeAnything(w, o, getExpectedRootType(o), null, null, getInitialDepth()-1, true, false);

 		return w;

 	}

 

@@ -201,7 +201,7 @@
 		HtmlClassMeta cHtml = cHtml(type);

 

 		if (type.isMapOrBean() && ! cHtml.isXml())

-			return serializeAnything(out, o, eType, elementName, pMeta, 0, false);

+			return serializeAnything(out, o, eType, elementName, pMeta, 0, false, false);

 

 		return super.serializeAnything(out, o, eType, elementName, elementNamespace, addNamespaceUris, format, isMixed, preserveWhitespace, pMeta);

 	}

@@ -217,13 +217,14 @@
 	 * @param pMeta The bean property being serialized, or <jk>null</jk> if we're not serializing a bean property.

 	 * @param xIndent The current indentation value.

 	 * @param isRoot <jk>true</jk> if this is the root element of the document.

+	 * @param nlIfElement <jk>true</jk> if we should add a newline to the output before serializing only if the object is an element and not text.

 	 * @return The type of content encountered.  Either simple (no whitespace) or normal (elements with whitespace).

 	 * @throws IOException Thrown by underlying stream.

 	 * @throws SerializeException Generic serialization error occurred.

 	 */

 	@SuppressWarnings({ "rawtypes", "unchecked" })

 	protected ContentResult serializeAnything(XmlWriter out, Object o,

-			ClassMeta<?> eType, String name, BeanPropertyMeta pMeta, int xIndent, boolean isRoot) throws IOException, SerializeException {

+			ClassMeta<?> eType, String name, BeanPropertyMeta pMeta, int xIndent, boolean isRoot, boolean nlIfElement) throws IOException, SerializeException {

 

 		ClassMeta<?> aType = null;       // The actual type

 		ClassMeta<?> wType = null;     // The wrapped type (delegate)

@@ -239,7 +240,7 @@
 			o = null;

 			aType = object();

 		}

-		

+

 		// Handle Optional<X>

 		if (isOptional(aType)) {

 			o = getOptionalValue(o);

@@ -300,13 +301,15 @@
 					indent -= xIndent;

 					pop();

 					out.nl(indent);

-					return serializeAnything(out, o2, null, typeName, null, xIndent, false);

+					return serializeAnything(out, o2, null, typeName, null, xIndent, false, false);

 				}

 			}

 

 			if (cHtml.isXml() || bpHtml.isXml()) {

 				pop();

 				indent++;

+				if (nlIfElement)

+					out.nl(0);

 				super.serializeAnything(out, o, null, null, null, false, XmlFormat.MIXED, false, false, null);

 				indent -= xIndent+1;

 				return cr;

@@ -424,14 +427,14 @@
 			out.cTag();

 			if (link != null)

 				out.oTag(i+3, "a").attrUri("href", link.replace("{#}", stringify(value))).cTag();

-			ContentResult cr = serializeAnything(out, key, keyType, null, null, 2, false);

+			ContentResult cr = serializeAnything(out, key, keyType, null, null, 2, false, false);

 			if (link != null)

 				out.eTag("a");

 			if (cr == CR_ELEMENTS)

 				out.i(i+2);

 			out.eTag("td").nl(i+2);

 			out.sTag(i+2, "td");

-			cr = serializeAnything(out, value, valueType, (key == null ? "_x0000_" : toString(key)), null, 2, false);

+			cr = serializeAnything(out, value, valueType, (key == null ? "_x0000_" : toString(key)), null, 2, false, false);

 			if (cr == CR_ELEMENTS)

 				out.ie(i+2);

 			out.eTag("td").nl(i+2);

@@ -494,7 +497,7 @@
 			try {

 				if (link != null)

 					out.oTag(i+3, "a").attrUri("href", link).cTag();

-				ContentResult cr = serializeAnything(out, value, cMeta, key, pMeta, 2, false);

+				ContentResult cr = serializeAnything(out, value, cMeta, key, pMeta, 2, false, true);

 				if (cr == CR_ELEMENTS)

 					out.i(i+2);

 				if (link != null)

@@ -577,7 +580,7 @@
 

 				if (cm == null) {

 					out.i(i+2);

-					serializeAnything(out, o, null, null, null, 1, false);

+					serializeAnything(out, o, null, null, null, 1, false, false);

 					out.nl(0);

 

 				} else if (cm.isMap() && ! (cm.isBeanMap())) {

@@ -585,7 +588,7 @@
 

 					for (Object k : th) {

 						out.sTag(i+2, "td");

-						ContentResult cr = serializeAnything(out, m2.get(k), eType.getElementType(), toString(k), null, 2, false);

+						ContentResult cr = serializeAnything(out, m2.get(k), eType.getElementType(), toString(k), null, 2, false, true);

 						if (cr == CR_ELEMENTS)

 							out.i(i+2);

 						out.eTag("td").nl(i+2);

@@ -619,7 +622,7 @@
 							out.cTag();

 							if (link != null)

 								out.oTag("a").attrUri("href", link).cTag();

-							ContentResult cr = serializeAnything(out, value, pMeta.getClassMeta(), p.getKey().toString(), pMeta, 2, false);

+							ContentResult cr = serializeAnything(out, value, pMeta.getClassMeta(), p.getKey().toString(), pMeta, 2, false, true);

 							if (cr == CR_ELEMENTS)

 								out.i(i+2);

 							if (link != null)

@@ -651,7 +654,7 @@
 					out.cTag();

 				if (link != null)

 					out.oTag(i+2, "a").attrUri("href", link.replace("{#}", stringify(o))).cTag();

-				ContentResult cr = serializeAnything(out, o, eType.getElementType(), name, null, 1, false);

+				ContentResult cr = serializeAnything(out, o, eType.getElementType(), name, null, 1, false, true);

 				if (link != null)

 					out.eTag("a");

 				if (cr == CR_ELEMENTS)

diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlWriter.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlWriter.java
index 29e6eff..21a29bd 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlWriter.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlWriter.java
@@ -67,7 +67,7 @@
 				append("&gt;");

 			else if (test == '\n')

 				append(preserveWhitespace ? "\n" : "<br/>");

-			else if (test == '\f')  // XML 1.0 doesn't support formfeeds or backslashes, so we have to invent something.

+			else if (test == '\f')  // XML 1.0 doesn't support form feeds or backslashes, so we have to invent something.

 				append(preserveWhitespace ? "\f" : "<ff/>");

 			else if (test == '\b')

 				append(preserveWhitespace ? "\b" : "<bs/>");

diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/NoCloseWriter.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/NoCloseWriter.java
index 0d96649..d1f462e 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/NoCloseWriter.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/NoCloseWriter.java
@@ -82,4 +82,9 @@
 	public void write(String str, int off, int len) throws IOException {
 		w.write(str, off, len);
 	}
+
+	@Override /* Object */
+	public String toString() {
+		return w.toString();
+	}
 }
\ No newline at end of file
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 2ef487d..872a1e8 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
@@ -288,13 +288,14 @@
 		else

 			sType = eType;

 

-		if (sType.isOptional()) 

+		if (sType.isOptional())

 			return (T)Optional.ofNullable(parseAnything(eType.getElementType(), currAttr, r, outer, isRoot, pMeta));

 

 		setCurrentClass(sType);

 

 		String wrapperAttr = (isRoot && isPreserveRootElement()) ? r.getName().getLocalPart() : null;

 		String typeAttr = r.getAttributeValue(null, getBeanTypePropertyName(eType));

+		boolean isNil = "true".equals(r.getAttributeValue(null, "nil"));

 		int jsonType = getJsonType(typeAttr);

 		String elementName = getElementName(r);

 		if (jsonType == 0) {

@@ -367,7 +368,7 @@
 				o = builder != null ? builder.build(this, m.getBean(), eType) : m.getBean();

 			} else {

 				BeanMap m = builder != null ? toBeanMap(builder.create(this, eType)) : newBeanMap(outer, sType.getInnerClass());

-				m = parseIntoBean(r, m);

+				m = parseIntoBean(r, m, isNil);

 				o = builder != null ? builder.build(this, m.getBean(), eType) : m.getBean();

 			}

 		} else if (sType.isArray() || sType.isArgs()) {

@@ -469,23 +470,25 @@
 		return UNKNOWN;

 	}

 

-	private <T> BeanMap<T> parseIntoBean(XmlReader r, BeanMap<T> m) throws IOException, ParseException, ExecutableException, XMLStreamException {

+	private <T> BeanMap<T> parseIntoBean(XmlReader r, BeanMap<T> m, boolean isNil) throws IOException, ParseException, ExecutableException, XMLStreamException {

 		BeanMeta<?> bMeta = m.getMeta();

 		XmlBeanMeta xmlMeta = bMeta.getExtendedMeta(XmlBeanMeta.class);

 

 		for (int i = 0; i < r.getAttributeCount(); i++) {

 			String key = getAttributeName(r, i);

-			String val = r.getAttributeValue(i);

-			String ns = r.getAttributeNamespace(i);

-			BeanPropertyMeta bpm = xmlMeta.getPropertyMeta(key);

-			if (bpm == null) {

-				if (xmlMeta.getAttrsProperty() != null) {

-					xmlMeta.getAttrsProperty().add(m, key, key, val);

-				} else if (ns == null) {

-					onUnknownProperty(key, m);

+			if (! "nil".equals(key)) {

+				String val = r.getAttributeValue(i);

+				String ns = r.getAttributeNamespace(i);

+				BeanPropertyMeta bpm = xmlMeta.getPropertyMeta(key);

+				if (bpm == null) {

+					if (xmlMeta.getAttrsProperty() != null) {

+						xmlMeta.getAttrsProperty().add(m, key, key, val);

+					} else if (ns == null) {

+						onUnknownProperty(key, m);

+					}

+				} else {

+					bpm.set(m, key, val);

 				}

-			} else {

-				bpm.set(m, key, val);

 			}

 		}

 

@@ -600,10 +603,17 @@
 			}

 		} while (depth >= 0);

 

-		if (sb != null && cp != null)

-			cp.set(m, null, sb.toString());

-		else if (l != null && cp != null)

-			cp.set(m, null, XmlUtils.collapseTextNodes(l));

+		if (cp != null && ! isNil) {

+			if (sb != null)

+				cp.set(m, null, sb.toString());

+			else if (l != null)

+				cp.set(m, null, XmlUtils.collapseTextNodes(l));

+			else if (cpcm.isCollectionOrArray()) {

+				Object o = cp.get(m, null);

+				if (o == null)

+					cp.set(m, cp.getName(), new ArrayList<>());

+			}

+		}

 

 		returnStringBuilder(sb);

 		return m;

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 775e188..fdec1e6 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
@@ -234,7 +234,7 @@
 	 * @param elementNamespace The namespace of the element.

 	 * @param addNamespaceUris Flag indicating that namespace URIs need to be added.

 	 * @param format The format to serialize the output to.

-	 * @param isMixed We're serializing mixed content, so don't use whitespace.

+	 * @param isMixedOrText We're serializing mixed content, so don't use whitespace.

 	 * @param preserveWhitespace

 	 * 	<jk>true</jk> if we're serializing {@link XmlFormat#MIXED_PWS} or {@link XmlFormat#TEXT_PWS}.

 	 * @param pMeta The bean property metadata if this is a bean property being serialized.

@@ -250,12 +250,12 @@
 			Namespace elementNamespace,

 			boolean addNamespaceUris,

 			XmlFormat format,

-			boolean isMixed,

+			boolean isMixedOrText,

 			boolean preserveWhitespace,

 			BeanPropertyMeta pMeta) throws IOException, SerializeException {

 

 		JsonType type = null;              // The type string (e.g. <type> or <x x='type'>

-		int i = isMixed ? 0 : indent;       // Current indentation

+		int i = isMixedOrText ? 0 : indent;       // Current indentation

 		ClassMeta<?> aType = null;     // The actual type

 		ClassMeta<?> wType = null;     // The wrapped type (delegate)

 		ClassMeta<?> sType = object(); // The serialized type

@@ -372,7 +372,7 @@
 		}

 

 		// Do we need a carriage return after the start tag?

-		boolean cr = o != null && (sType.isMapOrBean() || sType.isCollectionOrArray()) && ! isMixed;

+		boolean cr = o != null && (sType.isMapOrBean() || sType.isCollectionOrArray()) && ! isMixedOrText;

 

 		String en = elementName;

 		if (en == null && ! isRaw) {

@@ -435,21 +435,21 @@
 				out.append(o);

 			} else if (sType.isMap() || (wType != null && wType.isMap())) {

 				if (o instanceof BeanMap)

-					rc = serializeBeanMap(out, (BeanMap)o, elementNamespace, isCollapsed, isMixed);

+					rc = serializeBeanMap(out, (BeanMap)o, elementNamespace, isCollapsed, isMixedOrText);

 				else

-					rc = serializeMap(out, (Map)o, sType, eType.getKeyType(), eType.getValueType(), isMixed);

+					rc = serializeMap(out, (Map)o, sType, eType.getKeyType(), eType.getValueType(), isMixedOrText);

 			} else if (sType.isBean()) {

-				rc = serializeBeanMap(out, toBeanMap(o), elementNamespace, isCollapsed, isMixed);

+				rc = serializeBeanMap(out, toBeanMap(o), elementNamespace, isCollapsed, isMixedOrText);

 			} else if (sType.isCollection() || (wType != null && wType.isCollection())) {

 				if (isCollapsed)

 					this.indent--;

-				serializeCollection(out, o, sType, eType, pMeta, isMixed);

+				serializeCollection(out, o, sType, eType, pMeta, isMixedOrText);

 				if (isCollapsed)

 					this.indent++;

 			} else if (sType.isArray()) {

 				if (isCollapsed)

 					this.indent--;

-				serializeCollection(out, o, sType, eType, pMeta, isMixed);

+				serializeCollection(out, o, sType, eType, pMeta, isMixedOrText);

 				if (isCollapsed)

 					this.indent++;

 			} else if (sType.isReader() || sType.isInputStream()) {

@@ -478,7 +478,7 @@
 				else

 					out.ie(cr && rc != CR_MIXED ? i : 0).eTag(elementNs, en, encodeEn);

 			}

-			if (! isMixed)

+			if (! isMixedOrText)

 				out.nl(i);

 		}

 

@@ -527,7 +527,7 @@
 	}

 

 	private ContentResult serializeBeanMap(XmlWriter out, BeanMap<?> m,

-			Namespace elementNs, boolean isCollapsed, boolean isMixed) throws IOException, SerializeException {

+			Namespace elementNs, boolean isCollapsed, boolean isMixedOrText) throws IOException, SerializeException {

 		boolean hasChildren = false;

 		BeanMeta<?> bm = m.getMeta();

 

@@ -581,9 +581,9 @@
 							}

 						} else /* Map */ {

 							Map m2 = (Map)value;

-							for (Map.Entry e : (Set<Map.Entry>)(m2.entrySet())) {

-								out.attr(ns, toString(e.getKey()), e.getValue());

-							}

+							if (m2 != null)

+								for (Map.Entry e : (Set<Map.Entry>)(m2.entrySet()))

+									out.attr(ns, toString(e.getKey()), e.getValue());

 						}

 					} else {

 						out.attr(ns, key, value);

@@ -609,7 +609,7 @@
 					hasContent = true;

 					cf = xbm.getContentFormat();

 					if (cf.isOneOf(MIXED,MIXED_PWS,TEXT,TEXT_PWS,XMLTEXT))

-						isMixed = true;

+						isMixedOrText = true;

 					if (cf.isOneOf(MIXED_PWS, TEXT_PWS))

 						preserveWhitespace = true;

 					if (contentType.isCollection() && ((Collection)content).isEmpty())

@@ -628,46 +628,40 @@
 

 					if (! hasChildren) {

 						hasChildren = true;

-						out.appendIf(! isCollapsed, '>').nlIf(! isMixed, indent);

+						out.appendIf(! isCollapsed, '>').nlIf(! isMixedOrText, indent);

 					}

 

 					XmlBeanPropertyMeta bpXml = bpXml(pMeta);

-					serializeAnything(out, value, cMeta, key, bpXml.getNamespace(), false, bpXml.getXmlFormat(), isMixed, false, pMeta);

+					serializeAnything(out, value, cMeta, key, bpXml.getNamespace(), false, bpXml.getXmlFormat(), isMixedOrText, false, pMeta);

 				}

 			}

 		}

-		if (! hasContent)

+		if (contentProperty == null && ! hasContent)

 			return (hasChildren ? CR_ELEMENTS : isVoidElement ? CR_VOID : CR_EMPTY);

-		out.append('>').nlIf(! isMixed, indent);

 

 		// Serialize XML content.

 		if (content != null) {

+			out.append('>').nlIf(! isMixedOrText, indent);

 			if (contentType == null) {

 			} else if (contentType.isCollection()) {

 				Collection c = (Collection)content;

 				for (Iterator i = c.iterator(); i.hasNext();) {

 					Object value = i.next();

-					serializeAnything(out, value, contentType.getElementType(), null, null, false, cf, isMixed, preserveWhitespace, null);

+					serializeAnything(out, value, contentType.getElementType(), null, null, false, cf, isMixedOrText, preserveWhitespace, null);

 				}

 			} else if (contentType.isArray()) {

 				Collection c = toList(Object[].class, content);

 				for (Iterator i = c.iterator(); i.hasNext();) {

 					Object value = i.next();

-					serializeAnything(out, value, contentType.getElementType(), null, null, false, cf, isMixed, preserveWhitespace, null);

+					serializeAnything(out, value, contentType.getElementType(), null, null, false, cf, isMixedOrText, preserveWhitespace, null);

 				}

 			} else {

-				serializeAnything(out, content, contentType, null, null, false, cf, isMixed, preserveWhitespace, null);

+				serializeAnything(out, content, contentType, null, null, false, cf, isMixedOrText, preserveWhitespace, null);

 			}

 		} else {

-			if (! isTrimNullProperties()) {

-				if (! isMixed)

-					out.i(indent);

-				out.text(content);

-				if (! isMixed)

-					out.nl(indent);

-			}

+			out.attr("nil", "true").append('>').nlIf(! isMixedOrText, indent);

 		}

-		return isMixed ? CR_MIXED : CR_ELEMENTS;

+		return isMixedOrText ? CR_MIXED : CR_ELEMENTS;

 	}

 

 	private XmlWriter serializeCollection(XmlWriter out, Object in, ClassMeta<?> sType,

diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xmlschema/XmlSchemaSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xmlschema/XmlSchemaSerializerSession.java
index d81666a..4ac369a 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xmlschema/XmlSchemaSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xmlschema/XmlSchemaSerializerSession.java
@@ -329,6 +329,8 @@
 
 			w.cTag().nl(i);
 
+			boolean hasAnyAttrs = false;
+
 			if (! (cm.isMapOrBean() || cm.isCollectionOrArray() || (cm.isAbstract() && ! cm.isNumber()) || cm.isObject())) {
 				w.oTag(i+1, "attribute").attr("name", getBeanTypePropertyName(cm)).attr("type", "string").ceTag().nl(i+1);
 
@@ -343,7 +345,9 @@
 					for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) {
 						if (pMeta.canRead()) {
 							XmlFormat bpXml = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat();
-							if (bpXml != XmlFormat.ATTR)
+							if (bpXml == ATTRS)
+								hasAnyAttrs = true;
+							else if (bpXml != XmlFormat.ATTR)
 								hasChildElements = true;
 						}
 					}
@@ -376,11 +380,12 @@
 									if (xmlMeta.getXmlFormat() == COLLAPSED)
 										hasCollapsed = true;
 								}
-
 							}
 						}
 
-						if (hasOtherNsElement || hasCollapsed) {
+						if (hasAnyAttrs) {
+							w.oTag(i+1, "anyAttribute").attr("processContents", "skip").ceTag().nl(i+1);
+						} else if (hasOtherNsElement || hasCollapsed) {
 							// If this bean has any child elements in another namespace,
 							// we need to add an <any> element.
 							w.oTag(i+1, "choice").attr("maxOccurs", "unbounded").cTag().nl(i+1);
@@ -443,10 +448,12 @@
 
 							// Otherwise, it's just a plain attribute of this bean.
 							else {
-								w.oTag(i+1, "attribute")
+								if (! hasAnyAttrs) {
+									w.oTag(i+1, "attribute")
 									.attr("name", pMeta.getName(), true)
 									.attr("type", getXmlAttrType(pMeta.getClassMeta()))
 									.ceTag().nl(i+1);
+								}
 							}
 						}
 					}
@@ -485,10 +492,12 @@
 					w.eTag(i+1, "sequence").nl(i+1);
 				}
 
-				w.oTag(i+1, "attribute")
+				if (! hasAnyAttrs) {
+					w.oTag(i+1, "attribute")
 					.attr("name", getBeanTypePropertyName(null))
 					.attr("type", "string")
 					.ceTag().nl(i+1);
+				}
 			}
 
 			w.eTag(i, "complexType").nl(i);
@@ -578,6 +587,6 @@
 	public ObjectMap toMap() {
 		return super.toMap()
 			.append("XmlSchemaSerializerSession", new DefaultFilteringObjectMap()
-			);
+		);
 	}
 }
diff --git a/juneau-doc/docs/ReleaseNotes/8.1.3.html b/juneau-doc/docs/ReleaseNotes/8.1.3.html
new file mode 100644
index 0000000..6336107
--- /dev/null
+++ b/juneau-doc/docs/ReleaseNotes/8.1.3.html
@@ -0,0 +1,36 @@
+<!--
+/***************************************************************************************************************************
+ * 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.
+ ***************************************************************************************************************************/
+ -->
+
+8.1.3 (TBD)
+
+<p>
+	Juneau 8.1.3 is a minor release.
+</p>
+
+<h5 class='topic w800'>juneau-marshall</h5>
+<ul class='spaced-list'>
+	<li>
+		Better representation of nulls for XML and HTML content properties.
+		<br>Old:  <js>"&lt;myBean&gt;&lt;null&gt;&lt;/myBean&gt;"</js>
+		<br>New:  <js>"&lt;myBean nil='true'&gt;&lt;/myBean&gt;"</js>
+</ul>
+
+<h5 class='topic w800'>juneau-rest-server</h5>
+<ul class='spaced-list'>
+</ul>
+
+<h5 class='topic w800'>juneau-rest-client</h5>
+<ul class='spaced-list'>
+</ul>