You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2020/03/14 15:54:43 UTC

[juneau] branch master updated: JUNEAU-187

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 9249b14  JUNEAU-187
9249b14 is described below

commit 9249b147a9d3f189035bccb6edc7f68a6c1c84a4
Author: JamesBognar <ja...@apache.org>
AuthorDate: Sat Mar 14 11:54:26 2020 -0400

    JUNEAU-187
    
    HtmlSerializer does not honor @Bean(bpi) on collections of beans with
    null values in first row.
---
 .../java/org/apache/juneau/html/CommonTest.java    |  20 ++--
 .../test/java/org/apache/juneau/html/HtmlTest.java |  54 +++++++--
 .../juneau/transforms/CalendarSwapComboTest.java   |   6 +-
 .../juneau/transforms/DateSwapComboTest.java       |   6 +-
 .../juneau/transforms/ReaderObjectSwapTest.java    |   6 +-
 .../main/java/org/apache/juneau/BeanSession.java   |   5 +
 .../org/apache/juneau/html/HtmlParserSession.java  |   5 +-
 .../apache/juneau/html/HtmlSerializerSession.java  | 122 ++++++---------------
 .../juneau/serializer/SerializerSession.java       |   6 +-
 juneau-doc/docs/ReleaseNotes/8.1.4.html            |   6 +
 10 files changed, 116 insertions(+), 120 deletions(-)

diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/CommonTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/CommonTest.java
index 5832564..60d30e3 100755
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/CommonTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/CommonTest.java
@@ -112,19 +112,19 @@ public class CommonTest {
 
 		s.trimEmptyCollections(false);
 		r = s.build().serialize(t1);
-		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f1</td><td><ul></ul></td></tr><tr><td>f2</td><td><table _type='array'><tr><th>s2</th></tr><tr><null/></tr><tr><td>s2</td></tr></table></td></tr></table>", r);
+		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f1</td><td><ul></ul></td></tr><tr><td>f2</td><td><table _type='array'><tr><th>s1</th><th>s2</th></tr><tr><null/></tr><tr><td><null/></td><td>s2</td></tr></table></td></tr></table>", r);
 		t2 = p.parse(r, C.class);
 		assertEqualObjects(t1, t2);
 
 		s.trimEmptyCollections(true);
 		r = s.build().serialize(t1);
-		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f2</td><td><table _type='array'><tr><th>s2</th></tr><tr><null/></tr><tr><td>s2</td></tr></table></td></tr></table>", r);
+		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f2</td><td><table _type='array'><tr><th>s1</th><th>s2</th></tr><tr><null/></tr><tr><td><null/></td><td>s2</td></tr></table></td></tr></table>", r);
 		t2 = p.parse(r, C.class);
 		assertNull(t2.f1);
 
 		s.trimEmptyCollections();
 		r = s.build().serialize(t1);
-		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f2</td><td><table _type='array'><tr><th>s2</th></tr><tr><null/></tr><tr><td>s2</td></tr></table></td></tr></table>", r);
+		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f2</td><td><table _type='array'><tr><th>s1</th><th>s2</th></tr><tr><null/></tr><tr><td><null/></td><td>s2</td></tr></table></td></tr></table>", r);
 		t2 = p.parse(r, C.class);
 		assertNull(t2.f1);
 	}
@@ -163,14 +163,15 @@ public class CommonTest {
 					+"<td>f2</td>"
 					+"<td>"
 						+"<table _type='array'>"
-							+"<tr><th>s2</th></tr>"
+							+"<tr><th>s1</th><th>s2</th></tr>"
 							+"<tr><null/></tr>"
-							+"<tr><td>s2</td></tr>"
+							+"<tr><td><null/></td><td>s2</td></tr>"
 						+"</table>"
 					+"</td>"
 				+"</tr>"
 			+"</table>",
 			r);
+
 		t2 = p.parse(r, D.class);
 		assertEqualObjects(t1, t2);
 
@@ -183,9 +184,9 @@ public class CommonTest {
 					+"<td>f2</td>"
 					+"<td>"
 						+"<table _type='array'>"
-							+"<tr><th>s2</th></tr>"
+							+"<tr><th>s1</th><th>s2</th></tr>"
 							+"<tr><null/></tr>"
-							+"<tr><td>s2</td></tr>"
+							+"<tr><td><null/></td><td>s2</td></tr>"
 						+"</table>"
 					+"</td>"
 				+"</tr>"
@@ -203,9 +204,9 @@ public class CommonTest {
 					+"<td>f2</td>"
 					+"<td>"
 						+"<table _type='array'>"
-							+"<tr><th>s2</th></tr>"
+							+"<tr><th>s1</th><th>s2</th></tr>"
 							+"<tr><null/></tr>"
-							+"<tr><td>s2</td></tr>"
+							+"<tr><td><null/></td><td>s2</td></tr>"
 						+"</table>"
 					+"</td>"
 				+"</tr>"
@@ -340,6 +341,7 @@ public class CommonTest {
 					+"<td>2</td>"
 				+"</tr>"
 			+"</table>", html);
+
 	}
 
 	public static class F {
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlTest.java
index c549564..0332904 100755
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlTest.java
@@ -18,6 +18,7 @@ import static org.junit.Assert.*;
 import java.util.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
 import org.apache.juneau.html.annotation.*;
 import org.apache.juneau.testutils.pojos.*;
 import org.junit.*;
@@ -29,7 +30,7 @@ public class HtmlTest {
 	// Verifies that lists of maps/beans are converted to tables correctly.
 	//-----------------------------------------------------------------------------------------------------------------
 	@Test
-	public void testTables1() throws Exception {
+	public void a01_testTables1() throws Exception {
 		HtmlSerializer s = HtmlSerializer.DEFAULT_SQ;
 		Object[] t;
 		String html;
@@ -48,7 +49,7 @@ public class HtmlTest {
 	// Test URI_ANCHOR_SET options
 	//-----------------------------------------------------------------------------------------------------------------
 	@Test
-	public void testAnchorTextOptions() throws Exception {
+	public void a02_testAnchorTextOptions() throws Exception {
 		HtmlSerializerBuilder s = HtmlSerializer.create().sq().addKeyValueTableHeaders().uriResolution(UriResolution.NONE);
 		TestURI t = new TestURI();
 		String r;
@@ -209,7 +210,7 @@ public class HtmlTest {
 	// Test @Html.asPlainText annotation on classes and fields
 	//-----------------------------------------------------------------------------------------------------------------
 	@Test
-	public void testHtmlAnnotationAsPlainText() throws Exception {
+	public void b01_testHtmlAnnotationAsPlainText() throws Exception {
 		HtmlSerializer s = HtmlSerializer.create().sq().addKeyValueTableHeaders().build();
 		Object o = null;
 		String r;
@@ -238,7 +239,7 @@ public class HtmlTest {
 	}
 
 	@Test
-	public void testHtmlAnnotationAsPlainText_usingConfig() throws Exception {
+	public void b02_testHtmlAnnotationAsPlainText_usingConfig() throws Exception {
 		HtmlSerializer s = HtmlSerializer.create().sq().addKeyValueTableHeaders().applyAnnotations(B3.class).applyAnnotations(B4.class).build();
 
 		Object o = null;
@@ -271,7 +272,7 @@ public class HtmlTest {
 	// Test @Html.asXml annotation on classes and fields
 	//-----------------------------------------------------------------------------------------------------------------
 	@Test
-	public void testHtmlAnnotationAsXml() throws Exception {
+	public void c01_testHtmlAnnotationAsXml() throws Exception {
 		HtmlSerializer s = HtmlSerializer.create().sq().addKeyValueTableHeaders().build();
 		Object o = null;
 		String r;
@@ -296,7 +297,7 @@ public class HtmlTest {
 	}
 
 	@Test
-	public void testHtmlAnnotationAsXml_usingConfig() throws Exception {
+	public void c02_testHtmlAnnotationAsXml_usingConfig() throws Exception {
 		HtmlSerializer s = HtmlSerializer.create().sq().addKeyValueTableHeaders().applyAnnotations(C3.class).build();
 		Object o = null;
 		String r;
@@ -323,7 +324,7 @@ public class HtmlTest {
 	// Test @Html.noTableHeaders
 	//-----------------------------------------------------------------------------------------------------------------
 	@Test
-	public void testNoTableHeaders() throws Exception {
+	public void d01_testNoTableHeaders() throws Exception {
 		HtmlSerializer s = HtmlSerializer.DEFAULT_SQ;
 		Object o = null;
 		String r;
@@ -339,7 +340,7 @@ public class HtmlTest {
 	public static class MyMap extends LinkedHashMap<String,String> {}
 
 	@Test
-	public void testNoTableHeaders_usingConfig() throws Exception {
+	public void d02_testNoTableHeaders_usingConfig() throws Exception {
 		HtmlSerializer s = HtmlSerializer.DEFAULT_SQ.builder().applyAnnotations(MyMap2.class).build();
 		Object o = null;
 		String r;
@@ -358,7 +359,7 @@ public class HtmlTest {
 	// Test @Html.noTableHeaders on beans
 	//-----------------------------------------------------------------------------------------------------------------
 	@Test
-	public void testNoTableHeadersOnBeans() throws Exception {
+	public void d03_testNoTableHeadersOnBeans() throws Exception {
 		HtmlSerializer s = HtmlSerializer.DEFAULT_SQ;
 		Object o = null;
 		String r;
@@ -375,7 +376,7 @@ public class HtmlTest {
 	}
 
 	@Test
-	public void testNoTableHeadersOnBeans_usingConfig() throws Exception {
+	public void d04_testNoTableHeadersOnBeans_usingConfig() throws Exception {
 		HtmlSerializer s = HtmlSerializer.DEFAULT_SQ.builder().applyAnnotations(MyBean2.class).build();
 		Object o = null;
 		String r;
@@ -392,7 +393,7 @@ public class HtmlTest {
 	}
 
 	@Test
-	public void testNoTableHeadersOnBeans_usingConcreteAnnotation() throws Exception {
+	public void d05_testNoTableHeadersOnBeans_usingConcreteAnnotation() throws Exception {
 		HtmlSerializer s = HtmlSerializer.DEFAULT_SQ.builder().annotations(new HtmlAnnotation("MyBean2").noTables(true)).build();
 		Object o = null;
 		String r;
@@ -402,4 +403,35 @@ public class HtmlTest {
 		r = s.serialize(o);
 		assertEquals("<table _type='array'><tr><td>1</td><td>2</td><td>3</td></tr><tr><td>1</td><td>2</td><td>3</td></tr></table>", r);
 	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// @Bean(bpi) on collections of beans
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Bean(bpi="f3,f2,f1")
+	public static class E {
+		public Integer f1, f2, f3;
+
+		public E(Integer f1, Integer f2, Integer f3) {
+			this.f1 = f1;
+			this.f2 = f2;
+			this.f3 = f3;
+		}
+	}
+
+	@Test
+	public void e01_collectionOfBeansWithBpi() throws Exception {
+		E[] ee = new E[]{
+			new E(null, 2, 3),
+			new E(4, 5, 6)
+		};
+		assertEquals("<table _type='array'><tr><th>f3</th><th>f2</th><th>f1</th></tr><tr><td>3</td><td>2</td><td><null/></td></tr><tr><td>6</td><td>5</td><td>4</td></tr></table>", HtmlSerializer.DEFAULT_SQ.toString(ee));
+		assertEquals("<table _type='array'><tr><th>f3</th><th>f2</th><th>f1</th></tr><tr><td>3</td><td>2</td><td><null/></td></tr><tr><td>6</td><td>5</td><td>4</td></tr></table>", HtmlSerializer.DEFAULT_SQ.toString(Arrays.asList(ee)));
+
+		ee = new E[] {
+			new E(null, null, null),
+			new E(null, null, null)
+		};
+		assertEquals("<table _type='array'><tr><th>f3</th><th>f2</th><th>f1</th></tr><tr><td><null/></td><td><null/></td><td><null/></td></tr><tr><td><null/></td><td><null/></td><td><null/></td></tr></table>", HtmlSerializer.DEFAULT_SQ.toString(ee));
+	}
 }
\ No newline at end of file
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/CalendarSwapComboTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/CalendarSwapComboTest.java
index f8f64e5..d99f6c4 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/CalendarSwapComboTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/CalendarSwapComboTest.java
@@ -514,9 +514,9 @@ public class CalendarSwapComboTest extends ComboRoundTripTest {
 					/* XmlT */		"<array><object><time t='number'>-2172116928000</time><timeZone>PST</timeZone></object></array>",
 					/* XmlR */		"<array>\n\t<object>\n\t\t<time _type='number'>-2172116928000</time>\n\t\t<timeZone>PST</timeZone>\n\t</object>\n</array>\n",
 					/* XmlNs */		"<array><object><time _type='number'>-2172116928000</time><timeZone>PST</timeZone></object></array>",
-					/* Html */		"<ul><li><table><tr><td>time</td><td><number>-2172116928000</number></td></tr><tr><td>timeZone</td><td>PST</td></tr></table></li></ul>",
-					/* HtmlT */		"<ul><li><table><tr><td>time</td><td><number>-2172116928000</number></td></tr><tr><td>timeZone</td><td>PST</td></tr></table></li></ul>",
-					/* HtmlR */		"<ul>\n\t<li>\n\t\t<table>\n\t\t\t<tr>\n\t\t\t\t<td>time</td>\n\t\t\t\t<td><number>-2172116928000</number></td>\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td>timeZone</td>\n\t\t\t\t<td>PST</td>\n\t\t\t</tr>\n\t\t</table>\n\t</li>\n</ul>\n",
+					/* Html */		"<table _type='array'><tr><th>time</th><th>timeZone</th></tr><tr><td><number>-2172116928000</number></td><td>PST</td></tr></table>",
+					/* HtmlT */		"<table t='array'><tr><th>time</th><th>timeZone</th></tr><tr><td><number>-2172116928000</number></td><td>PST</td></tr></table>",
+					/* HtmlR */		"<table _type='array'>\n\t<tr>\n\t\t<th>time</th>\n\t\t<th>timeZone</th>\n\t</tr>\n\t<tr>\n\t\t<td><number>-2172116928000</number></td>\n\t\t<td>PST</td>\n\t</tr>\n</table>\n",
 					/* Uon */		"@((time=-2172116928000,timeZone=PST))",
 					/* UonT */		"@((time=-2172116928000,timeZone=PST))",
 					/* UonR */		"@(\n\t(\n\t\ttime=-2172116928000,\n\t\ttimeZone=PST\n\t)\n)",
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/DateSwapComboTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/DateSwapComboTest.java
index 1536bd4..389a890 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/DateSwapComboTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/DateSwapComboTest.java
@@ -510,9 +510,9 @@ public class DateSwapComboTest extends ComboRoundTripTest {
 					/* XmlT */		"<array><object><time t='number'>-2172116928000</time></object></array>",
 					/* XmlR */		"<array>\n\t<object>\n\t\t<time _type='number'>-2172116928000</time>\n\t</object>\n</array>\n",
 					/* XmlNs */		"<array><object><time _type='number'>-2172116928000</time></object></array>",
-					/* Html */		"<ul><li><table><tr><td>time</td><td><number>-2172116928000</number></td></tr></table></li></ul>",
-					/* HtmlT */		"<ul><li><table><tr><td>time</td><td><number>-2172116928000</number></td></tr></table></li></ul>",
-					/* HtmlR */		"<ul>\n\t<li>\n\t\t<table>\n\t\t\t<tr>\n\t\t\t\t<td>time</td>\n\t\t\t\t<td><number>-2172116928000</number></td>\n\t\t\t</tr>\n\t\t</table>\n\t</li>\n</ul>\n",
+					/* Html */		"<table _type='array'><tr><th>time</th></tr><tr><td><number>-2172116928000</number></td></tr></table>",
+					/* HtmlT */		"<table t='array'><tr><th>time</th></tr><tr><td><number>-2172116928000</number></td></tr></table>",
+					/* HtmlR */		"<table _type='array'>\n\t<tr>\n\t\t<th>time</th>\n\t</tr>\n\t<tr>\n\t\t<td><number>-2172116928000</number></td>\n\t</tr>\n</table>\n",
 					/* Uon */		"@((time=-2172116928000))",
 					/* UonT */		"@((time=-2172116928000))",
 					/* UonR */		"@(\n\t(\n\t\ttime=-2172116928000\n\t)\n)",
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/ReaderObjectSwapTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/ReaderObjectSwapTest.java
index 4208b6e..48272b4 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/ReaderObjectSwapTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/ReaderObjectSwapTest.java
@@ -299,9 +299,9 @@ public class ReaderObjectSwapTest extends ComboSerializeTest {
 					/* XmlT */		"<object><f><object><f1>x1a-xml</f1><f2>x2a-xmlx2b-xml<null/></f2><f4>x4a-xmlx4b-xml<null/></f4><f5><foo>x5a-xml</foo><bar t='null'/><_x0000_>x5c-xml</_x0000_></f5></object><null/></f></object>",
 					/* XmlR */		"<object>\n\t<f>\n\t\t<object>\n\t\t\t<f1>x1a-xml</f1>\n\t\t\t<f2>\n\t\t\t\tx2a-xml\n\t\t\t\tx2b-xml\n\t\t\t\t<null/>\n\t\t\t</f2>\n\t\t\t<f4>\n\t\t\t\tx4a-xml\n\t\t\t\tx4b-xml\n\t\t\t\t<null/>\n\t\t\t</f4>\n\t\t\t<f5>\n\t\t\t\t<foo>x5a-xml</foo>\n\t\t\t\t<bar _type='null'/>\n\t\t\t\t<_x0000_>x5c-xml</_x0000_>\n\t\t\t</f5>\n\t\t</object>\n\t\t<null/>\n\t</f>\n</object>\n",
 					/* XmlNs */		"<object><f><object><f1>x1a-xml</f1><f2>x2a-xmlx2b-xml<null/></f2><f4>x4a-xmlx4b-xml<null/></f4><f5><foo>x5a-xml</foo><bar _type='null'/><_x0000_>x5c-xml</_x0000_></f5></object><null/></f></object>",
-					/* Html */		"<table><tr><td>f</td><td><table _type='array'><tr><th>f1</th><th>f2</th><th>f4</th><th>f5</th></tr><tr><td>x1a-html</td><td><ul><li>x2a-html</li><li>x2b-html</li><li><null/></li></ul></td><td><ul><li>x4a-html</li><li>x4b-html</li><li><null/></li></ul></td><td><table><tr><td>foo</td><td>x5a-html</td></tr><tr><td>bar</td><td><null/></td></tr><tr><td><null/></td><td>x5c-html</td></tr></table></td></tr><tr><null/></tr></table></td></tr></table>",
-					/* HtmlT */		"<table><tr><td>f</td><td><table t='array'><tr><th>f1</th><th>f2</th><th>f4</th><th>f5</th></tr><tr><td>x1a-html</td><td><ul><li>x2a-html</li><li>x2b-html</li><li><null/></li></ul></td><td><ul><li>x4a-html</li><li>x4b-html</li><li><null/></li></ul></td><td><table><tr><td>foo</td><td>x5a-html</td></tr><tr><td>bar</td><td><null/></td></tr><tr><td><null/></td><td>x5c-html</td></tr></table></td></tr><tr><null/></tr></table></td></tr></table>",
-					/* HtmlR */		"<table>\n\t<tr>\n\t\t<td>f</td>\n\t\t<td>\n\t\t\t<table _type='array'>\n\t\t\t\t<tr>\n\t\t\t\t\t<th>f1</th>\n\t\t\t\t\t<th>f2</th>\n\t\t\t\t\t<th>f4</th>\n\t\t\t\t\t<th>f5</th>\n\t\t\t\t</tr>\n\t\t\t\t<tr>\n\t\t\t\t\t<td>x1a-html</td>\n\t\t\t\t\t<td>\n\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t<li>x2a-html</li>\n\t\t\t\t\t\t\t<li>x2b-html</li>\n\t\t\t\t\t\t\t<li><null/></li>\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</td>\n\t\t\t\t\t<td>\n\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t<li>x4a-html</li>\n\t [...]
+					/* Html */		"<table><tr><td>f</td><td><table _type='array'><tr><th>f1</th><th>f2</th><th>f3</th><th>f4</th><th>f5</th></tr><tr><td>x1a-html</td><td><ul><li>x2a-html</li><li>x2b-html</li><li><null/></li></ul></td><td><null/></td><td><ul><li>x4a-html</li><li>x4b-html</li><li><null/></li></ul></td><td><table><tr><td>foo</td><td>x5a-html</td></tr><tr><td>bar</td><td><null/></td></tr><tr><td><null/></td><td>x5c-html</td></tr></table></td></tr><tr><null/></tr></table></td></tr></table>",
+					/* HtmlT */		"<table><tr><td>f</td><td><table t='array'><tr><th>f1</th><th>f2</th><th>f3</th><th>f4</th><th>f5</th></tr><tr><td>x1a-html</td><td><ul><li>x2a-html</li><li>x2b-html</li><li><null/></li></ul></td><td><null/></td><td><ul><li>x4a-html</li><li>x4b-html</li><li><null/></li></ul></td><td><table><tr><td>foo</td><td>x5a-html</td></tr><tr><td>bar</td><td><null/></td></tr><tr><td><null/></td><td>x5c-html</td></tr></table></td></tr><tr><null/></tr></table></td></tr></table>",
+					/* HtmlR */		"<table>\n\t<tr>\n\t\t<td>f</td>\n\t\t<td>\n\t\t\t<table _type='array'>\n\t\t\t\t<tr>\n\t\t\t\t\t<th>f1</th>\n\t\t\t\t\t<th>f2</th>\n\t\t\t\t\t<th>f3</th>\n\t\t\t\t\t<th>f4</th>\n\t\t\t\t\t<th>f5</th>\n\t\t\t\t</tr>\n\t\t\t\t<tr>\n\t\t\t\t\t<td>x1a-html</td>\n\t\t\t\t\t<td>\n\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t<li>x2a-html</li>\n\t\t\t\t\t\t\t<li>x2b-html</li>\n\t\t\t\t\t\t\t<li><null/></li>\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</td>\n\t\t\t\t\t<td><null/></td>\n\t\t\t\t\t<td>\n\t [...]
 					/* Uon */		"(f=@((f1=x1a-uon,f2=@(x2a-uon,x2b-uon,null),f4=@(x4a-uon,x4b-uon,null),f5=(foo=x5a-uon,bar=null,null=x5c-uon)),null))",
 					/* UonT */		"(f=@((f1=x1a-uon,f2=@(x2a-uon,x2b-uon,null),f4=@(x4a-uon,x4b-uon,null),f5=(foo=x5a-uon,bar=null,null=x5c-uon)),null))",
 					/* UonR */		"(\n\tf=@(\n\t\t(\n\t\t\tf1=x1a-uon,\n\t\t\tf2=@(\n\t\t\t\tx2a-uon,\n\t\t\t\tx2b-uon,\n\t\t\t\tnull\n\t\t\t),\n\t\t\tf4=@(\n\t\t\t\tx4a-uon,\n\t\t\t\tx4b-uon,\n\t\t\t\tnull\n\t\t\t),\n\t\t\tf5=(\n\t\t\t\tfoo=x5a-uon,\n\t\t\t\tbar=null,\n\t\t\t\tnull=x5c-uon\n\t\t\t)\n\t\t),\n\t\tnull\n\t)\n)",
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
index 88985a0..0d58457 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
@@ -756,6 +756,9 @@ public class BeanSession extends Session {
 	 * If object is not a true bean, then throws a {@link BeanRuntimeException} with an explanation of why it's not a
 	 * bean.
 	 *
+	 * <p>
+	 * If object is already a {@link BeanMap}, simply returns the same object.
+	 *
 	 * <h5 class='section'>Example:</h5>
 	 * <p class='bcode w800'>
 	 * 	<jc>// Construct a bean map around a bean instance</jc>
@@ -767,6 +770,8 @@ public class BeanSession extends Session {
 	 * @return The wrapped object.
 	 */
 	public final <T> BeanMap<T> toBeanMap(T o) {
+		if (o instanceof BeanMap)
+			return (BeanMap<T>)o;
 		return this.toBeanMap(o, (Class<T>)o.getClass());
 	}
 
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 dc00be3..85266d4 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
@@ -486,7 +486,10 @@ public final class HtmlParserSession extends XmlParserSession {
 					m2.put(getBeanTypePropertyName(type.getElementType()), c);
 					l.add((E)cast(m2, pMeta, elementType));
 				} else {
-					l.add((E)m);
+					if (m instanceof ObjectMap)
+						l.add((E)convertToType(m, elementType));
+					else
+						l.add((E)m);
 				}
 			}
 			nextTag(r, xTR);
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 72dac32..e5c5f10 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
@@ -608,11 +608,7 @@ public class HtmlSerializerSession extends XmlSerializerSession {
 						out.eTag("td").nl(i+2);
 					}
 				} else {
-					BeanMap m2 = null;
-					if (o instanceof BeanMap)
-						m2 = (BeanMap)o;
-					else
-						m2 = toBeanMap(o);
+					BeanMap m2 = toBeanMap(o);
 
 					if (th == null)
 						th = m2.keySet().toArray(new Object[m2.size()]);
@@ -712,15 +708,17 @@ public class HtmlSerializerSession extends XmlSerializerSession {
 	/*
 	 * Returns the table column headers for the specified collection of objects.
 	 * Returns null if collection should not be serialized as a 2-dimensional table.
+	 * Returns an empty array if it should be treated as a table but without headers.
 	 * 2-dimensional tables are used for collections of objects that all have the same set of property names.
 	 */
 	@SuppressWarnings({ "rawtypes", "unchecked" })
 	private Object[] getTableHeaders(Collection c, HtmlBeanPropertyMeta bpHtml) throws SerializeException  {
+
 		if (c.size() == 0)
 			return null;
+
 		c = sort(c);
-		Object[] th;
-		Set<ClassMeta> prevC = new HashSet<>();
+
 		Object o1 = null;
 		for (Object o : c)
 			if (o != null) {
@@ -729,107 +727,55 @@ public class HtmlSerializerSession extends XmlSerializerSession {
 			}
 		if (o1 == null)
 			return null;
-		ClassMeta<?> cm = getClassMetaForObject(o1);
 
-		PojoSwap swap = cm.getPojoSwap(this);
-		if (swap != null) {
-			o1 = swap(swap, o1);
-			cm = swap.getSwapClassMeta(this);
-		}
+		ClassMeta<?> cm1 = getClassMetaForObject(o1);
 
-		if (cm == null || ! cm.isMapOrBean())
-			return null;
-		if (cm.hasAnnotation(HtmlLink.class))
+		PojoSwap swap = cm1.getPojoSwap(this);
+		o1 = swap(swap, o1);
+		if (swap != null)
+			cm1 = swap.getSwapClassMeta(this);
+
+		if (cm1 == null || ! cm1.isMapOrBean() || cm1.hasAnnotation(HtmlLink.class))
 			return null;
 
-		HtmlClassMeta cHtml = getHtmlClassMeta(cm);
+		HtmlClassMeta cHtml = getHtmlClassMeta(cm1);
 
-		if (cHtml.isNoTables() || bpHtml.isNoTables() || cHtml.isXml() || bpHtml.isXml())
+		if (cHtml.isNoTables() || bpHtml.isNoTables() || cHtml.isXml() || bpHtml.isXml() || canIgnoreValue(cm1, null, o1))
 			return null;
+
 		if (cHtml.isNoTableHeaders() || bpHtml.isNoTableHeaders())
 			return new Object[0];
-		if (canIgnoreValue(cm, null, o1))
-			return null;
-		if (cm.isMap() && ! cm.isBeanMap()) {
+
+		// If it's a non-bean map, only use table if all entries are also maps.
+		if (cm1.isMap() && ! cm1.isBeanMap()) {
+
 			Set<Object> set = new LinkedHashSet<>();
 			for (Object o : c) {
-				if (! canIgnoreValue(cm, null, o)) {
-					if (! cm.isInstance(o))
+				o = swap(swap, o);
+				if (! canIgnoreValue(cm1, null, o)) {
+					if (! cm1.isInstance(o))
 						return null;
 					Map m = sort((Map)o);
-					for (Map.Entry e : (Set<Map.Entry>)m.entrySet()) {
-						if (e.getValue() != null)
-							set.add(e.getKey() == null ? null : e.getKey());
-					}
-				}
-			}
-			th = set.toArray(new Object[set.size()]);
-		} else {
-			Map<String,Boolean> m = new LinkedHashMap<>();
-			for (Object o : c) {
-				if (! canIgnoreValue(cm, null, o)) {
-					if (! cm.isInstance(o))
-						return null;
-					BeanMap<?> bm = (o instanceof BeanMap ? (BeanMap)o : toBeanMap(o));
-					for (Map.Entry<String,Object> e : bm.entrySet()) {
-						String key = e.getKey();
-						if (e.getValue() != null)
-							m.put(key, true);
-						else if (! m.containsKey(key))
-							m.put(key, false);
-					}
+					for (Map.Entry e : (Set<Map.Entry>)m.entrySet())
+						if (! set.contains(e.getKey()))
+							set.add(e.getKey());
 				}
 			}
-			for (Iterator<Boolean> i = m.values().iterator(); i.hasNext();)
-				if (! i.next())
-					i.remove();
-			th = m.keySet().toArray(new Object[m.size()]);
+			return set.toArray(new Object[set.size()]);
+
 		}
-		prevC.add(cm);
-		boolean isSortable = true;
-		for (Object o : th)
-			isSortable &= (o instanceof Comparable);
-		Set<Object> s = (isSortable ? new TreeSet<>() : new LinkedHashSet<>());
-		s.addAll(Arrays.asList(th));
 
+		// Must be a bean or BeanMap.
 		for (Object o : c) {
-			if (o == null)
-				continue;
-			cm = getClassMetaForObject(o);
-
-			PojoSwap ps = cm == null ? null : cm.getPojoSwap(this);
-			if (ps != null) {
-				o = swap(ps, o);
-				cm = ps.getSwapClassMeta(this);
-			}
-			if (prevC.contains(cm))
-				continue;
-			if (cm == null || ! (cm.isMap() || cm.isBean()))
-				return null;
-			if (cm.hasAnnotation(HtmlLink.class))
-				return null;
-			if (canIgnoreValue(cm, null, o))
-				return null;
-			if (cm.isMap() && ! cm.isBeanMap()) {
-				Map m = (Map)o;
-				if (th.length != m.keySet().size())
-					return null;
-				for (Object k : m.keySet())
-					if (! s.contains(k.toString()))
-						return null;
-			} else {
-				BeanMap<?> bm = (o instanceof BeanMap ? (BeanMap)o : toBeanMap(o));
-				int l = 0;
-				for (String k : bm.keySet()) {
-					if (! s.contains(k))
-						return null;
-					l++;
-				}
-				if (s.size() != l)
+			o = swap(swap, o);
+			if (! canIgnoreValue(cm1, null, o)) {
+				if (! cm1.isInstance(o))
 					return null;
 			}
 		}
-		return th;
+		
+		BeanMap<?> bm = toBeanMap(o1);
+		return bm.keySet().toArray(new String[bm.size()]);
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java
index 2991a09..fa9a06e 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java
@@ -653,9 +653,9 @@ public abstract class SerializerSession extends BeanTraverseSession {
 	}
 
 	/**
-	 * Invokes the specified swap on the specified object.
+	 * Invokes the specified swap on the specified object if the swap is not null.
 	 *
-	 * @param swap The swap to invoke.
+	 * @param swap The swap to invoke.  Can be <jk>null</jk>.
 	 * @param o The input object.
 	 * @return The swapped object.
 	 * @throws SerializeException If swap method threw an exception.
@@ -663,6 +663,8 @@ public abstract class SerializerSession extends BeanTraverseSession {
 	@SuppressWarnings({ "rawtypes", "unchecked" })
 	protected Object swap(PojoSwap swap, Object o) throws SerializeException {
 		try {
+			if (swap == null)
+				return o;
 			return swap.swap(this, o);
 		} catch (Exception e) {
 			throw new SerializeException(e);
diff --git a/juneau-doc/docs/ReleaseNotes/8.1.4.html b/juneau-doc/docs/ReleaseNotes/8.1.4.html
index f8b10de..4fc0812 100644
--- a/juneau-doc/docs/ReleaseNotes/8.1.4.html
+++ b/juneau-doc/docs/ReleaseNotes/8.1.4.html
@@ -142,6 +142,12 @@
 	<li>
 		New {@link oaj.html.annotation.HtmlDoc#asideFloat} setting so that you can position the contents of the aside section on the page.
 	<li>
+		Various minor fixes surrounding HTML serialization.
+		<ul>
+			<li>Collections of beans that were supposed to be serialized as tables were being serialized as lists.
+			<li>Collections of beans with <c><ja>@Bean</ja>(bpi)</c> were not being serialized in the correct column order.			
+		</ul>
+	<li>
 		HTML-Schema support is being deprecated due to low-use and difficulty in maintaining.  It will be removed in 9.0.
 </ul>