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 674be40  JUNEAU-133 Serializers/parsers should support Optional 
objects and properties.
674be40 is described below

commit 674be40ad517418463fdeda91e553612de8f73b1
Author: JamesBognar <[email protected]>
AuthorDate: Wed Sep 4 17:38:20 2019 -0400

    JUNEAU-133 Serializers/parsers should support Optional objects and
    properties.
---
 .../a/rttests/RoundTripBeanPropertiesTest.java     |  94 +++++
 .../a/rttests/RoundTripOptionalObjectsTest.java    | 379 +++++++++++++++++++++
 .../a/rttests/RoundTripSimpleObjectsTest.java      |  23 ++
 .../org/apache/juneau/jena/RdfParserSession.java   |   7 +-
 .../apache/juneau/jena/RdfSerializerSession.java   |   7 +
 .../main/java/org/apache/juneau/BeanContext.java   |  20 +-
 .../src/main/java/org/apache/juneau/BeanMap.java   |   9 +-
 .../main/java/org/apache/juneau/BeanSession.java   |  24 +-
 .../org/apache/juneau/BeanTraverseSession.java     |  36 ++
 .../src/main/java/org/apache/juneau/ClassMeta.java |  40 ++-
 .../org/apache/juneau/html/HtmlParserSession.java  |   4 +
 .../apache/juneau/html/HtmlSerializerSession.java  |   7 +
 .../org/apache/juneau/json/JsonParserSession.java  |   4 +
 .../apache/juneau/json/JsonSerializerSession.java  |   7 +
 .../juneau/msgpack/MsgPackParserSession.java       |   4 +
 .../juneau/msgpack/MsgPackSerializerSession.java   |   7 +
 .../apache/juneau/oapi/OpenApiParserSession.java   |  10 +
 .../juneau/serializer/SerializerSession.java       |   7 +-
 .../org/apache/juneau/uon/UonParserSession.java    |   4 +
 .../apache/juneau/uon/UonSerializerSession.java    |   7 +
 .../urlencoding/UrlEncodingParserSession.java      |   3 +
 .../main/java/org/apache/juneau/utils/AList.java   |  14 +
 .../main/java/org/apache/juneau/utils/ASet.java    |  14 +
 .../org/apache/juneau/xml/XmlParserSession.java    |   4 +
 .../apache/juneau/xml/XmlSerializerSession.java    |   7 +
 .../xmlschema/XmlSchemaSerializerSession.java      |   5 +
 juneau-doc/docs/ReleaseNotes/8.1.0.html            |   2 +-
 juneau-doc/docs/ReleaseNotes/8.1.1.html            |  26 ++
 .../02.juneau-marshall/19.PojoCategories.html      |   6 +-
 29 files changed, 761 insertions(+), 20 deletions(-)

diff --git 
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanPropertiesTest.java
 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanPropertiesTest.java
new file mode 100644
index 0000000..3864a92
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanPropertiesTest.java
@@ -0,0 +1,94 @@
+// 
***************************************************************************************************************************
+// * 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.a.rttests;
+
+import static org.junit.Assert.*;
+
+import java.util.*;
+
+import org.apache.juneau.parser.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.utils.*;
+import org.junit.*;
+import org.junit.runners.*;
+
+/**
+ * Tests designed to serialize and parse objects to make sure we end up
+ * with the same objects for all serializers and parsers.
+ */
+@SuppressWarnings({"unchecked"})
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class RoundTripBeanPropertiesTest extends RoundTripTest {
+
+       public RoundTripBeanPropertiesTest(String label, SerializerBuilder s, 
ParserBuilder p, int flags) throws Exception {
+               super(label, s, p, flags);
+       }
+
+       
//------------------------------------------------------------------------------------------------------------------
+       // Combo arrays/lists
+       
//------------------------------------------------------------------------------------------------------------------
+
+       public static class A01 {
+               public List<Long>[] f1;
+       }
+
+       @Test
+       public void a01_arrayOfListOfLongs() throws Exception {
+               A01 o = new A01();
+               o.f1 = new List[1];
+               o.f1[0] = AList.create(123l);
+               o = roundTrip(o);
+               assertEquals(123, o.f1[0].get(0).intValue());
+               assertTrue(o.f1[0].get(0) instanceof Long);
+       }
+
+       public static class A02 {
+               public List<Long[]> f1;
+       }
+
+       @Test
+       public void a02_ListOfArrayOfLongs() throws Exception {
+               A02 o = new A02();
+               o.f1 = AList.<Long[]>create(new Long[]{123l});
+               o = roundTrip(o);
+               assertEquals(123, o.f1.get(0)[0].intValue());
+               assertTrue(o.f1.get(0)[0] instanceof Long);
+       }
+
+       public static class A03 {
+               public List<Long>[][] f1;
+       }
+
+       @Test
+       public void a03_2dArrayOfListOfLongs() throws Exception {
+               A03 o = new A03();
+               o.f1 = new List[1][1];
+               o.f1[0] = new List[]{AList.create(123l)};
+               o = roundTrip(o);
+               assertEquals(123, o.f1[0][0].get(0).intValue());
+               assertTrue(o.f1[0][0].get(0) instanceof Long);
+       }
+
+       public static class A04 {
+               public List<Long[][]> f1;
+       }
+
+       @Test
+       public void a04_ListOf2dArrayOfLongs() throws Exception {
+               A04 o = new A04();
+               o.f1 = AList.<Long[][]>create(new Long[][]{new Long[]{123l}});
+               o = roundTrip(o);
+               assertEquals(123, o.f1.get(0)[0][0].intValue());
+               assertTrue(o.f1.get(0)[0][0] instanceof Long);
+       }
+}
diff --git 
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripOptionalObjectsTest.java
 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripOptionalObjectsTest.java
new file mode 100644
index 0000000..47057b0
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripOptionalObjectsTest.java
@@ -0,0 +1,379 @@
+// 
***************************************************************************************************************************
+// * 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.a.rttests;
+
+import static org.junit.Assert.*;
+
+import java.util.*;
+
+import org.apache.juneau.parser.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.utils.*;
+import org.junit.*;
+import org.junit.runners.*;
+
+/**
+ * Tests designed to serialize and parse objects to make sure we end up
+ * with the same objects for all serializers and parsers.
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class RoundTripOptionalObjectsTest extends RoundTripTest {
+
+       public RoundTripOptionalObjectsTest(String label, SerializerBuilder s, 
ParserBuilder p, int flags) throws Exception {
+               super(label, s, p, flags);
+       }
+
+       
//------------------------------------------------------------------------------------------------------------------
+       // Standalone Optional objects
+       
//------------------------------------------------------------------------------------------------------------------
+
+       // Empty Optional
+       @Test
+       public void a01_emptyOptional() throws Exception {
+               Optional<String> o = Optional.empty();
+               o = roundTrip(o);
+               assertFalse(o.isPresent());
+       }
+
+       // Optional containing String.
+       @Test
+       public void a02_optionalContainingString() throws Exception {
+               Optional<String> o = Optional.of("foobar");
+               o = roundTrip(o);
+               assertEquals("foobar", o.get());
+               o = Optional.of("");
+               o = roundTrip(o);
+               assertEquals("", o.get());
+       }
+
+       
//------------------------------------------------------------------------------------------------------------------
+       // Bean properties of Optional types.
+       
//------------------------------------------------------------------------------------------------------------------
+
+       //-----------------------------------------------------
+       // Optional<String>
+       //-----------------------------------------------------
+
+       public static class B01 {
+               public Optional<String> f1;
+       }
+
+       @Test
+       public void b01a_stringField() throws Exception {
+               B01 x = new B01();
+               x.f1 = Optional.of("foo");
+               x = roundTrip(x);
+               assertEquals("foo", x.f1.get());
+       }
+
+       @Test
+       public void b01b_stringField_emptyValue() throws Exception {
+               B01 x = new B01();
+               x.f1 = Optional.empty();
+               x = roundTrip(x);
+               assertFalse(x.f1.isPresent());
+       }
+
+       @Test
+       public void b01c_stringField_nullField() throws Exception {
+               B01 x = new B01();
+               x.f1 = null;
+               x = roundTrip(x);
+               if (isValidationOnly())
+                       return;
+               assertFalse(x.f1.isPresent());
+       }
+
+       //-----------------------------------------------------
+       // Optional<Integer>
+       //-----------------------------------------------------
+
+       public static class B02 {
+               public Optional<Integer> f1;
+       }
+
+       @Test
+       public void b02a_integerField() throws Exception {
+               B02 x = new B02();
+               x.f1 = Optional.of(123);
+               x = roundTrip(x);
+               assertEquals(123, x.f1.get().intValue());
+       }
+
+       @Test
+       public void b02b_integerField_emptyValue() throws Exception {
+               B02 x = new B02();
+               x.f1 = Optional.empty();
+               x = roundTrip(x);
+               assertFalse(x.f1.isPresent());
+       }
+
+       @Test
+       public void b02c_integerField_nullField() throws Exception {
+               B02 x = new B02();
+               x.f1 = null;
+               x = roundTrip(x);
+               if (isValidationOnly())
+                       return;
+               assertFalse(x.f1.isPresent());
+       }
+
+       //-----------------------------------------------------
+       // Optional<List<Integer>>
+       //-----------------------------------------------------
+
+       public static class B03 {
+               public Optional<List<Integer>> f1;
+       }
+
+       @Test
+       public void b03a_integerListField() throws Exception {
+               B03 x = new B03();
+               x.f1 = Optional.of(AList.of(123));
+               x = roundTrip(x);
+               assertEquals(123, x.f1.get().get(0).intValue());
+       }
+
+       @Test
+       public void b03b_integerListField_listWithNull() throws Exception {
+               B03 x = new B03();
+               x.f1 = Optional.of(AList.of((Integer)null));
+               x = roundTrip(x);
+               assertTrue(x.f1.isPresent());
+               assertEquals(1, x.f1.get().size());
+               assertNull(x.f1.get().get(0));
+       }
+
+       @Test
+       public void b03c_integerListField_emptyList() throws Exception {
+               B03 x = new B03();
+               x.f1 = Optional.of(AList.of());
+               x = roundTrip(x);
+               assertTrue(x.f1.isPresent());
+               assertEquals(0, x.f1.get().size());
+       }
+
+       @Test
+       public void b03d_integerListField_emptyValue() throws Exception {
+               B03 x = new B03();
+               x.f1 = Optional.empty();
+               x = roundTrip(x);
+               assertFalse(x.f1.isPresent());
+       }
+
+       @Test
+       public void b03e_integerListField_nullField() throws Exception {
+               B03 x = new B03();
+               x.f1 = null;
+               x = roundTrip(x);
+               if (isValidationOnly())
+                       return;
+               assertFalse(x.f1.isPresent());
+       }
+
+       //-----------------------------------------------------
+       // Optional<Optional<Integer>>
+       //-----------------------------------------------------
+
+       public static class B04 {
+               public Optional<Optional<Integer>> f1;
+       }
+
+       @Test
+       public void b04a_optionalOptionalInteger() throws Exception {
+               B04 x = new B04();
+               x.f1 = Optional.of(Optional.of(123));
+               x = roundTrip(x);
+               assertEquals(123, x.f1.get().get().intValue());
+       }
+
+       @Test
+       public void b04b_optionalOptionalInteger_emptyInnerValue() throws 
Exception {
+               B04 x = new B04();
+               x.f1 = Optional.of(Optional.empty());
+               x = roundTrip(x);
+               assertTrue(x.f1.isPresent());
+               assertFalse(x.f1.get().isPresent());
+       }
+
+       @Test
+       public void b04c_optionalOptionalInteger_emptyOuterValue() throws 
Exception {
+               B04 x = new B04();
+               x.f1 = Optional.empty();
+               x = roundTrip(x);
+               if (isValidationOnly())
+                       return;
+               assertTrue(x.f1.isPresent());
+               assertFalse(x.f1.get().isPresent());
+       }
+
+       @Test
+       public void b04d_optionalOptionalInteger_nullField() throws Exception {
+               B04 x = new B04();
+               x.f1 = null;
+               x = roundTrip(x);
+               if (isValidationOnly())
+                       return;
+               assertTrue(x.f1.isPresent());
+               assertFalse(x.f1.get().isPresent());
+       }
+
+       //-----------------------------------------------------
+       // Optional<Optional<Bean>>
+       //-----------------------------------------------------
+
+       public static class B05 {
+               public Optional<Optional<B05B>> f1;
+       }
+
+       public static class B05B {
+               public int f2;
+               public static B05B create() {
+                       B05B b = new B05B();
+                       b.f2 = 123;
+                       return b;
+               }
+       }
+
+       @Test
+       public void b05a_optionalOptionalBean() throws Exception {
+               B05 x = new B05();
+               x.f1 = Optional.of(Optional.of(B05B.create()));
+               x = roundTrip(x);
+               assertEquals(123, x.f1.get().get().f2);
+       }
+
+       @Test
+       public void b05b_optionalOptionalBean_emptyInnerValue() throws 
Exception {
+               B05 x = new B05();
+               x.f1 = Optional.of(Optional.empty());
+               x = roundTrip(x);
+               assertTrue(x.f1.isPresent());
+               assertFalse(x.f1.get().isPresent());
+       }
+
+       @Test
+       public void b05c_optionalOptionalBean_emptyOuterValue() throws 
Exception {
+               B05 x = new B05();
+               x.f1 = Optional.empty();
+               x = roundTrip(x);
+               if (isValidationOnly())
+                       return;
+               assertTrue(x.f1.isPresent());
+               assertFalse(x.f1.get().isPresent());
+       }
+
+       @Test
+       public void b05d_optionalOptionalBean_nullField() throws Exception {
+               B05 x = new B05();
+               x.f1 = null;
+               x = roundTrip(x);
+               if (isValidationOnly())
+                       return;
+               assertTrue(x.f1.isPresent());
+               assertFalse(x.f1.get().isPresent());
+       }
+
+       //-----------------------------------------------------
+       // List<Optional<Integer>>
+       //-----------------------------------------------------
+
+       public static class B06 {
+               public List<Optional<Integer>> f1;
+       }
+
+       @Test
+       public void b06a_listOfOptionalIntegers() throws Exception {
+               B06 x = new B06();
+               x.f1 = AList.of(Optional.of(123));
+               x = roundTrip(x);
+               assertEquals(123, x.f1.get(0).get().intValue());
+       }
+
+       @Test
+       public void b06b_listOfOptionalIntegers_listWithEmpty() throws 
Exception {
+               B06 x = new B06();
+               x.f1 = AList.of(Optional.empty());
+               x = roundTrip(x);
+               assertEquals(1, x.f1.size());
+               assertFalse(x.f1.get(0).isPresent());
+       }
+
+       @Test
+       public void b06c_listOfOptionalIntegers_listWithNull() throws Exception 
{
+               B06 x = new B06();
+               x.f1 = AList.of((Optional<Integer>)null);
+               x = roundTrip(x);
+               if (isValidationOnly())
+                       return;
+               assertEquals(1, x.f1.size());
+               assertFalse(x.f1.get(0).isPresent());
+       }
+
+       @Test
+       public void b06d_listOfOptionalIntegers_nullField() throws Exception {
+               B06 x = new B06();
+               x.f1 = null;
+               x = roundTrip(x);
+               assertNull(x.f1);
+       }
+
+       //-----------------------------------------------------
+       // Optional<Integer>[]
+       //-----------------------------------------------------
+
+       public static class B07 {
+               public Optional<Integer>[] f1;
+               public List<Integer>[] f2;
+       }
+
+       @Test
+       @SuppressWarnings("unchecked")
+       public void b07a_arrayOfOptionalIntegers() throws Exception {
+               B07 x = new B07();
+               x.f1 = new Optional[]{Optional.of(123)};
+               x.f2 = new List[]{AList.create(123)};
+               x = roundTrip(x);
+               assertEquals(123, x.f1[0].get().intValue());
+       }
+
+       @Test
+       @SuppressWarnings("unchecked")
+       public void b07b_arrayOfOptionalIntegers_listWithEmpty() throws 
Exception {
+               B07 x = new B07();
+               x.f1 = new Optional[]{Optional.empty()};
+               x = roundTrip(x);
+               assertEquals(1, x.f1.length);
+               assertFalse(x.f1[0].isPresent());
+       }
+
+       @Test
+       @SuppressWarnings("unchecked")
+       public void b07c_arrayOfOptionalIntegers_listWithNull() throws 
Exception {
+               B07 x = new B07();
+               x.f1 = new Optional[]{null};
+               x = roundTrip(x);
+               if (isValidationOnly())
+                       return;
+               assertEquals(1, x.f1.length);
+               assertFalse(x.f1[0].isPresent());
+       }
+
+       @Test
+       public void b07d_arrayOfOptionalIntegers_nullField() throws Exception {
+               B07 x = new B07();
+               x.f1 = null;
+               x = roundTrip(x);
+               assertNull(x.f1);
+       }
+}
diff --git 
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripSimpleObjectsTest.java
 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripSimpleObjectsTest.java
index 7c472b2..0257626 100755
--- 
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripSimpleObjectsTest.java
+++ 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripSimpleObjectsTest.java
@@ -43,6 +43,16 @@ public class RoundTripSimpleObjectsTest extends 
RoundTripTest {
        }
 
        
//====================================================================================================
+       // testOptional
+       
//====================================================================================================
+       @Test
+       public void testOptional() throws Exception {
+               Optional<String> o = Optional.empty();
+               o = roundTrip(o);
+               assertFalse(o.isPresent());
+       }
+
+       
//====================================================================================================
        // testString
        
//====================================================================================================
        @Test
@@ -56,6 +66,19 @@ public class RoundTripSimpleObjectsTest extends 
RoundTripTest {
        }
 
        
//====================================================================================================
+       // testOptional
+       
//====================================================================================================
+       @Test
+       public void testOptionalContainingString() throws Exception {
+               Optional<String> o = Optional.of("foobar");
+               o = roundTrip(o);
+               assertEquals("foobar", o.get());
+               o = Optional.of("");
+               o = roundTrip(o);
+               assertEquals("", o.get());
+       }
+
+       
//====================================================================================================
        // testStringArray
        
//====================================================================================================
        @Test
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 ead6465..31b3eed 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
@@ -100,7 +100,8 @@ public class RdfParserSession extends ReaderParserSession {
                }
 
                if (roots.isEmpty())
-                       return null;
+                       return type.isOptional() ? (T)Optional.empty() : null;
+
                if (roots.size() > 1)
                        throw new ParseException(this, "Too many root nodes 
found in model:  {0}", roots.size());
                Resource resource = roots.get(0);
@@ -219,6 +220,10 @@ public class RdfParserSession extends ReaderParserSession {
                        sType = swap.getSwapClassMeta(this);
                else
                        sType = eType;
+
+               if (sType.isOptional())
+                       return 
(T)Optional.ofNullable(parseAnything(eType.getElementType(), n, outer, pMeta));
+
                setCurrentClass(sType);
 
                if (! sType.canCreateNewInstance(outer)) {
diff --git 
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
 
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
index c9fd337..2036305 100644
--- 
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
+++ 
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
@@ -158,6 +158,13 @@ public final class RdfSerializerSession extends 
WriterSerializerSession {
                        aType = object();
                }
 
+               // Handle Optional<X>
+               if (isOptional(aType)) {
+                       o = getOptionalValue(o);
+                       eType = getOptionalType(eType);
+                       aType = getClassMetaForObject(o, object());
+               }
+
                if (o != null) {
 
                        if (aType.isDelegate()) {
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
index 999f783..8899cad 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
@@ -2349,7 +2349,7 @@ public class BeanContext extends Context {
         */
        private ClassMeta<?> getTypedClassMeta(ClassMeta<?>[] c, int pos) {
                ClassMeta<?> cm = c[pos++];
-               if (cm.isCollection()) {
+               if (cm.isCollection() || cm.isOptional()) {
                        ClassMeta<?> ce = c.length == pos ? object() : 
getTypedClassMeta(c, pos);
                        return (ce.isObject() ? cm : new ClassMeta(cm, null, 
null, ce));
                } else if (cm.isMap()) {
@@ -2373,7 +2373,7 @@ public class BeanContext extends Context {
                                return cm;
                        if (cm.isMap())
                                return getClassMeta(cm.innerClass, 
cm.getKeyType(), cm.getValueType());
-                       if (cm.isCollection())
+                       if (cm.isCollection() || cm.isOptional())
                                return getClassMeta(cm.innerClass, 
cm.getElementType());
                        return getClassMeta(cm.innerClass);
                }
@@ -2390,7 +2390,7 @@ public class BeanContext extends Context {
                // If this is a Map or Collection, and the parameter types 
aren't part
                // of the class definition itself (e.g. class AddressBook 
extends List<Person>),
                // then we need to figure out the parameters.
-               if (rawType.isMap() || rawType.isCollection()) {
+               if (rawType.isMap() || rawType.isCollection() || 
rawType.isOptional()) {
                        ClassMeta[] params = findParameters(o, c);
                        if (params == null)
                                return rawType;
@@ -2401,7 +2401,7 @@ public class BeanContext extends Context {
                                        return rawType;
                                return new ClassMeta(rawType, params[0], 
params[1], null);
                        }
-                       if (rawType.isCollection()) {
+                       if (rawType.isCollection() || rawType.isOptional()) {
                                if (params.length != 1)
                                        return rawType;
                                if (params[0].isObject())
@@ -2410,6 +2410,14 @@ public class BeanContext extends Context {
                        }
                }
 
+               if (rawType.isArray()) {
+                       if (o instanceof GenericArrayType) {
+                               GenericArrayType gat = (GenericArrayType)o;
+                               ClassMeta elementType = 
resolveClassMeta(gat.getGenericComponentType(), typeVarImpls);
+                               return new ClassMeta(rawType, null, null, 
elementType);
+                       }
+               }
+
                return rawType;
        }
 
@@ -2551,10 +2559,10 @@ public class BeanContext extends Context {
                                return new ClassMeta<>(cm2, keyType, valueType, 
null);
                        }
 
-                       if (cm2.isCollection()) {
+                       if (cm2.isCollection() || cm2.isOptional()) {
                                Class<?>[] pParams = (p.params().length == 0 ? 
new Class[]{Object.class} : p.params());
                                if (pParams.length != 1)
-                                       throw new 
FormattedRuntimeException("Invalid number of parameters specified for 
Collection (must be 1): {0}", pParams.length);
+                                       throw new 
FormattedRuntimeException("Invalid number of parameters specified for 
"+(cm2.isCollection() ? "Collection" : cm2.isOptional() ? "Optional" : 
"Array")+" (must be 1): {0}", pParams.length);
                                ClassMeta<?> elementType = 
resolveType(pParams[0], cm2.getElementType(), cm.getElementType());
                                if (elementType.isObject())
                                        return cm2;
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 e6d273f..c975510 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
@@ -148,6 +148,14 @@ public class BeanMap<T> extends AbstractMap<String,Object> 
implements Delegate<T
                        }
                        arrayPropertyCache = null;
                }
+
+               // Initialize any null Optional<X> fields.
+               for (BeanPropertyMeta pMeta : this.meta.properties.values()) {
+                       ClassMeta<?> cm = pMeta.getClassMeta();
+                       if (cm.isOptional() && pMeta.get(this, pMeta.getName()) 
== null)
+                               pMeta.set(this, pMeta.getName(), 
cm.getOptionalDefault());
+               }
+
                return b;
        }
 
@@ -266,7 +274,6 @@ public class BeanMap<T> extends AbstractMap<String,Object> 
implements Delegate<T
                p.add(this, property, value);
        }
 
-
        /**
         * Gets a property on the bean.
         *
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 8ada5d3..b78fba9 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
@@ -283,15 +283,18 @@ public class BeanSession extends Session {
                                        return to.getPrimitiveDefault();
 
                                // Otherwise, just return null.
-                               return null;
+                               return to.isOptional() ? 
(T)to.getOptionalDefault() : null;
                        }
 
+                       if (to.isOptional() && (! (value instanceof Optional)))
+                               return (T) 
Optional.ofNullable(convertToMemberType(outer, value, to.getElementType()));
+
                        Class<T> tc = to.getInnerClass();
 
                        // If no conversion needed, then just return the value.
                        // Don't include maps or collections, because child 
elements may need conversion.
                        if (tc.isInstance(value))
-                               if (! ((to.isMap() && 
to.getValueType().isNotObject()) || (to.isCollection() && 
to.getElementType().isNotObject())))
+                               if (! ((to.isMap() && 
to.getValueType().isNotObject()) || ((to.isCollection() || to.isOptional()) && 
to.getElementType().isNotObject())))
                                        return (T)value;
 
                        PojoSwap swap = to.getPojoSwap(this);
@@ -573,7 +576,7 @@ public class BeanSession extends Session {
                        if (to.isString()) {
                                if (from.isByteArray()) {
                                        return (T) new String((byte[])value);
-                               } else if (from.isMapOrBean() || 
from.isCollectionOrArray()) {
+                               } else if (from.isMapOrBean() || 
from.isCollectionOrArrayOrOptional()) {
                                        if (SimpleJsonSerializer.DEFAULT != 
null)
                                                return 
(T)SimpleJsonSerializer.DEFAULT.serialize(value);
                                } else if (from.isClass()) {
@@ -1016,9 +1019,20 @@ public class BeanSession extends Session {
         * @return The ClassMeta object, or <jk>null</jk> if {@code o} is 
<jk>null</jk>.
         */
        public final <T> ClassMeta<T> getClassMetaForObject(T o) {
+               return (ClassMeta<T>)getClassMetaForObject(o, null);
+       }
+
+       /**
+        * Shortcut for calling {@code getClassMeta(o.getClass())} but returns 
a default value if object is <jk>null</jk>.
+        *
+        * @param o The class to find the class type for.
+        * @param def The default {@link ClassMeta} if the object is null.
+        * @return The ClassMeta object, or the default value if {@code o} is 
<jk>null</jk>.
+        */
+       protected final ClassMeta<?> getClassMetaForObject(Object o, 
ClassMeta<?> def) {
                if (o == null)
-                       return null;
-               return (ClassMeta<T>)getClassMeta(o.getClass());
+                       return def;
+               return getClassMeta(o.getClass());
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanTraverseSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanTraverseSession.java
index ab1c48d..8301fb4 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanTraverseSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanTraverseSession.java
@@ -165,6 +165,42 @@ public class BeanTraverseSession extends BeanSession {
        }
 
        /**
+        * Same as {@link ClassMeta#isOptional()} but gracefully handles a null 
{@link ClassMeta}.
+        *
+        * @param cm The meta to check.
+        * @return <jk>true</jk> if the specified meta is an {@link Optional}.
+        */
+       protected final boolean isOptional(ClassMeta<?> cm) {
+               return (cm != null && cm.isOptional());
+       }
+
+       /**
+        * Returns the inner type of an {@link Optional}.
+        *
+        * @param cm The meta to check.
+        * @return The inner type of an {@link Optional}.
+        */
+       protected final ClassMeta<?> getOptionalType(ClassMeta<?> cm) {
+               if (cm.isOptional())
+                       return getOptionalType(cm.getElementType());
+               return cm;
+       }
+
+       /**
+        * If the specified object is an {@link Optional}, returns the inner 
object.
+        *
+        * @param o The object to check.
+        * @return The inner object if it's an {@link Optional}, <jk>null</jk> 
if it's <jk>null</jk>, or else the same object.
+        */
+       protected final Object getOptionalValue(Object o) {
+               if (o == null)
+                       return null;
+               if (o instanceof Optional)
+                       return getOptionalValue(((Optional<?>)o).orElse(null));
+               return o;
+       }
+
+       /**
         * Logs a warning message.
         *
         * @param t The throwable that was thrown (if there was one).
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 2a0dc9e..f893ac8 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
@@ -60,7 +60,7 @@ public final class ClassMeta<T> implements Type {
 
        /** Class categories. */
        enum ClassCategory {
-               MAP, COLLECTION, CLASS, METHOD, NUMBER, DECIMAL, BOOLEAN, CHAR, 
DATE, ARRAY, ENUM, OTHER, CHARSEQ, STR, OBJ, URI, BEANMAP, READER, INPUTSTREAM, 
VOID, ARGS
+               MAP, COLLECTION, CLASS, METHOD, NUMBER, DECIMAL, BOOLEAN, CHAR, 
DATE, ARRAY, ENUM, OTHER, CHARSEQ, STR, OBJ, URI, BEANMAP, READER, INPUTSTREAM, 
VOID, ARGS, OPTIONAL
        }
 
        final Class<T> innerClass;                              // The class 
being wrapped.
@@ -416,6 +416,8 @@ public final class ClassMeta<T> implements Type {
                                        cc = READER;
                                else if (ci.isChildOf(InputStream.class))
                                        cc = INPUTSTREAM;
+                               else if (ci.is(Optional.class))
+                                       cc = OPTIONAL;
                        }
 
                        isMemberClass = ci.isMemberClass() && ci.isNotStatic();
@@ -552,7 +554,7 @@ public final class ClassMeta<T> implements Type {
                                }
 
                                // If this is a COLLECTION, see if it's 
parameterized (e.g. AddressBook extends LinkedList<Person>)
-                               else if (cc == COLLECTION) {
+                               else if (cc == COLLECTION || cc == OPTIONAL) {
                                        ClassMeta[] parameters = 
findParameters();
                                        if (parameters != null && 
parameters.length == 1) {
                                                elementType = parameters[0];
@@ -654,6 +656,7 @@ public final class ClassMeta<T> implements Type {
                                        case OBJ:
                                        case OTHER:
                                        case READER:
+                                       case OPTIONAL:
                                        case VOID:
                                                break;
                                }
@@ -1072,6 +1075,15 @@ public final class ClassMeta<T> implements Type {
        }
 
        /**
+        * Returns <jk>true</jk> if this class is a subclass of {@link 
Optional}.
+        *
+        * @return <jk>true</jk> if this class is a subclass of {@link 
Optional}.
+        */
+       public boolean isOptional() {
+               return cc == OPTIONAL;
+       }
+
+       /**
         * Returns <jk>true</jk> if this class is a subclass of {@link 
Collection} or is an array.
         *
         * @return <jk>true</jk> if this class is a subclass of {@link 
Collection} or is an array.
@@ -1080,6 +1092,14 @@ public final class ClassMeta<T> implements Type {
                return cc == COLLECTION || cc == ARRAY;
        }
 
+       /**
+        * Returns <jk>true</jk> if this class is a subclass of {@link 
Collection} or is an array or {@link Optional}.
+        *
+        * @return <jk>true</jk> if this class is a subclass of {@link 
Collection} or is an array or {@link Optional}.
+        */
+       public boolean isCollectionOrArrayOrOptional() {
+               return cc == COLLECTION || cc == ARRAY || cc == OPTIONAL;
+       }
 
        /**
         * Returns <jk>true</jk> if this class extends from {@link Set}.
@@ -1638,6 +1658,20 @@ public final class ClassMeta<T> implements Type {
        }
 
        /**
+        * If this is an {@link Optional}, returns an empty optional.
+        *
+        * <p>
+        * Note that if this is a nested optional, will recursively create 
empty optionals.
+        *
+        * @return An empty optional, or <jk>null</jk> if this isn't an 
optional.
+        */
+       public Optional<?> getOptionalDefault() {
+               if (isOptional())
+                       return 
Optional.ofNullable(getElementType().getOptionalDefault());
+               return null;
+       }
+
+       /**
         * Converts the specified object to a string.
         *
         * @param t The object to convert.
@@ -1788,7 +1822,7 @@ public final class ClassMeta<T> implements Type {
                        return sb.append(n).append(keyType.isObject() && 
valueType.isObject() ? "" : 
"<"+keyType.toString(simple)+","+valueType.toString(simple)+">");
                if (cc == BEANMAP)
                        return 
sb.append(BeanMap.class.getName()).append('<').append(n).append('>');
-               if (cc == COLLECTION)
+               if (cc == COLLECTION || cc == OPTIONAL)
                        return sb.append(n).append(elementType.isObject() ? "" 
: "<"+elementType.toString(simple)+">");
                return sb.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 b9d7b6a..151fbeb 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
@@ -98,6 +98,10 @@ public final class HtmlParserSession extends 
XmlParserSession {
                        sType = swap.getSwapClassMeta(this);
                else
                        sType = eType;
+
+               if (sType.isOptional()) 
+                       return 
(T)Optional.ofNullable(parseAnything(eType.getElementType(), r, outer, isRoot, 
pMeta));
+
                setCurrentClass(sType);
 
                int event = r.getEventType();
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 f17bf73..e926d69 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
@@ -239,6 +239,13 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                        o = null;
                        aType = object();
                }
+               
+               // Handle Optional<X>
+               if (isOptional(aType)) {
+                       o = getOptionalValue(o);
+                       eType = getOptionalType(eType);
+                       aType = getClassMetaForObject(o, object());
+               }
 
                indent += xIndent;
 
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 571baae..27faae0 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
@@ -124,6 +124,10 @@ public final class JsonParserSession extends 
ReaderParserSession {
                        sType = swap.getSwapClassMeta(this);
                else
                        sType = eType;
+
+               if (sType.isOptional()) 
+                       return 
(T)Optional.ofNullable(parseAnything(eType.getElementType(), r, outer, pMeta));
+
                setCurrentClass(sType);
                String wrapperAttr = 
sType.getExtendedMeta(JsonClassMeta.class).getWrapperAttr();
 
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 915fb84..3ed2bc2 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
@@ -105,6 +105,13 @@ public class JsonSerializerSession extends 
WriterSerializerSession {
                        aType = object();
                }
 
+               // Handle Optional<X>
+               if (isOptional(aType)) {
+                       o = getOptionalValue(o);
+                       eType = getOptionalType(eType);
+                       aType = getClassMetaForObject(o, object());
+               }
+
                sType = aType;
                String typeName = getBeanTypeName(eType, aType, pMeta);
 
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 a33d250..89088c3 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
@@ -67,6 +67,10 @@ public final class MsgPackParserSession extends 
InputStreamParserSession {
                        sType = swap.getSwapClassMeta(this);
                else
                        sType = eType;
+
+               if (sType.isOptional()) 
+                       return 
(T)Optional.ofNullable(parseAnything(eType.getElementType(), is, outer, pMeta));
+
                setCurrentClass(sType);
 
                Object o = null;
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 d8b3ddc..b3f7aec 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
@@ -89,6 +89,13 @@ public final class MsgPackSerializerSession extends 
OutputStreamSerializerSessio
                        o = null;
                        aType = object();
                }
+               
+               // Handle Optional<X>
+               if (isOptional(aType)) {
+                       o = getOptionalValue(o);
+                       eType = getOptionalType(eType);
+                       aType = getClassMetaForObject(o, object());
+               }
 
                sType = aType;
                String typeName = getBeanTypeName(eType, aType, pMeta);
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/oapi/OpenApiParserSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/oapi/OpenApiParserSession.java
index 035bcb7..1efadd5 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/oapi/OpenApiParserSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/oapi/OpenApiParserSession.java
@@ -67,13 +67,23 @@ public class OpenApiParserSession extends UonParserSession {
        }
 
 
+       @SuppressWarnings("unchecked")
        @Override /* HttpPartParser */
        public <T> T parse(HttpPartType partType, HttpPartSchema schema, String 
in, ClassMeta<T> type) throws ParseException, SchemaValidationException {
+               boolean isOptional = type.isOptional();
+               while (isOptional)
+                       type = (ClassMeta<T>)type.getElementType();
+
                schema = ObjectUtils.firstNonNull(schema, getSchema(), 
DEFAULT_SCHEMA);
+
                T t = parseInner(partType, schema, in, type);
                if (t == null && type.isPrimitive())
                        t = type.getPrimitiveDefault();
                schema.validateOutput(t, ctx);
+
+               if (isOptional)
+                       t = (T)Optional.ofNullable(t);
+
                return t;
        }
 
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 455a175..4377894 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
@@ -586,7 +586,12 @@ public abstract class SerializerSession extends 
BeanTraverseSession {
         * @return The expected type.
         */
        protected final ClassMeta<?> getExpectedRootType(Object o) {
-               return isAddRootType() ? object() : getClassMetaForObject(o);
+               if (isAddRootType())
+                       return object();
+               ClassMeta<?> cm = getClassMetaForObject(o);
+               if (cm != null && cm.isOptional())
+                       return cm.getElementType();
+               return cm;
        }
 
        /**
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 7cc5e81..4bfe8ec 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
@@ -185,6 +185,10 @@ public class UonParserSession extends ReaderParserSession 
implements HttpPartPar
                        sType = swap.getSwapClassMeta(this);
                else
                        sType = eType;
+               
+               if (sType.isOptional()) 
+                       return 
(T)Optional.ofNullable(parseAnything(eType.getElementType(), r, outer, 
isUrlParamValue, pMeta));
+               
                setCurrentClass(sType);
 
                Object o = null;
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 3cfbdd3..1cb2b76 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
@@ -115,6 +115,13 @@ public class UonSerializerSession extends 
WriterSerializerSession implements Htt
                        aType = object();
                }
 
+               // Handle Optional<X>
+               if (isOptional(aType)) {
+                       o = getOptionalValue(o);
+                       eType = getOptionalType(eType);
+                       aType = getClassMetaForObject(o, object());
+               }
+
                sType = aType;
                String typeName = getBeanTypeName(eType, aType, pMeta);
 
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 3fde0ca..5bbc18d 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
@@ -95,6 +95,9 @@ public class UrlEncodingParserSession extends 
UonParserSession {
                        sType = swap.getSwapClassMeta(this);
                else
                        sType = eType;
+               
+               if (sType.isOptional()) 
+                       return 
(T)Optional.ofNullable(parseAnything(eType.getElementType(), r, outer));
 
                int c = r.peekSkipWs();
                if (c == '?')
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/AList.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/AList.java
index 352a723..50195dc 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/AList.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/AList.java
@@ -43,6 +43,20 @@ public final class AList<T> extends LinkedList<T> {
        }
 
        /**
+        * Convenience method for creating a list of objects.
+        *
+        * <p>
+        * Identical to {@link #create(Object...)}.
+        *
+        * @param t The initial values.
+        * @return A new list.
+        */
+       @SafeVarargs
+       public static <T> AList<T> of(T...t) {
+               return new AList<T>().appendAll(t);
+       }
+
+       /**
         * Adds an entry to this list.
         *
         * @param t The entry to add to this list.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ASet.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ASet.java
index 5cd70aa..b42dc67 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ASet.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ASet.java
@@ -43,6 +43,20 @@ public final class ASet<T> extends LinkedHashSet<T> {
        }
 
        /**
+        * Convenience method for creating a list of objects.
+        *
+        * <p>
+        * Identical to {@link #create(Object...)}.
+        *
+        * @param t The initial values.
+        * @return A new list.
+        */
+       @SafeVarargs
+       public static <T> ASet<T> of(T...t) {
+               return new ASet<T>().appendAll(t);
+       }
+
+       /**
         * Adds an entry to this set.
         *
         * @param t The entry to add to this set.
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 7fca283..2ef487d 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
@@ -287,6 +287,10 @@ public class XmlParserSession extends ReaderParserSession {
                        sType = swap.getSwapClassMeta(this);
                else
                        sType = eType;
+
+               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;
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 f69c2bb..775e188 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
@@ -271,6 +271,13 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
                        aType = object();
                }
 
+               // Handle Optional<X>
+               if (isOptional(aType)) {
+                       o = getOptionalValue(o);
+                       eType = getOptionalType(eType);
+                       aType = getClassMetaForObject(o, object());
+               }
+
                if (o != null) {
 
                        if (aType.isDelegate()) {
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 e4be1cc..d81666a 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
@@ -169,6 +169,8 @@ public class XmlSchemaSerializerSession extends 
XmlSerializerSession {
 
                void process(Object o) throws IOException {
                        ClassMeta<?> cm = getClassMetaForObject(o);
+                       if (cm != null && cm.isOptional())
+                               cm = 
getClassMetaForObject(((Optional<?>)o).orElse(null));
                        Namespace ns = defaultNs;
                        if (cm == null)
                                queueElement(ns, "null", object());
@@ -311,6 +313,9 @@ public class XmlSchemaSerializerSession extends 
XmlSerializerSession {
                        int i = indent + 1;
 
                        cm = cm.getSerializedClassMeta(schemas.session);
+                       while (cm.isOptional())
+                               cm = cm.getElementType();
+
                        XmlBeanMeta xbm = cm.isBean() ? 
cm.getBeanMeta().getExtendedMeta(XmlBeanMeta.class) : null;
 
                        w.oTag(i, "complexType")
diff --git a/juneau-doc/docs/ReleaseNotes/8.1.0.html 
b/juneau-doc/docs/ReleaseNotes/8.1.0.html
index ef7599b..f6acfeb 100644
--- a/juneau-doc/docs/ReleaseNotes/8.1.0.html
+++ b/juneau-doc/docs/ReleaseNotes/8.1.0.html
@@ -13,7 +13,7 @@
  
***************************************************************************************************************************/
  -->
 
-8.1.0 (TBD)
+8.1.0 (Aug 21, 2019)
 
 <p>
        8.1.0 introduces some significant new features including:
diff --git a/juneau-doc/docs/ReleaseNotes/8.1.1.html 
b/juneau-doc/docs/ReleaseNotes/8.1.1.html
new file mode 100644
index 0000000..ca24122
--- /dev/null
+++ b/juneau-doc/docs/ReleaseNotes/8.1.1.html
@@ -0,0 +1,26 @@
+<!--
+/***************************************************************************************************************************
+ * 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.1 (TBD)
+
+<p>
+       TODO
+</p>
+
+<h5 class='topic w800'>juneau-marshall</h5>
+<ul class='spaced-list'>
+       <li>
+               Support for serializing/parsing {@link java.util.Optional} 
objects and bean properties.
+</ul>
diff --git a/juneau-doc/docs/Topics/02.juneau-marshall/19.PojoCategories.html 
b/juneau-doc/docs/Topics/02.juneau-marshall/19.PojoCategories.html
index 85bf42b..34ca92b 100644
--- a/juneau-doc/docs/Topics/02.juneau-marshall/19.PojoCategories.html
+++ b/juneau-doc/docs/Topics/02.juneau-marshall/19.PojoCategories.html
@@ -37,7 +37,7 @@ POJO Categories
        </tr>                   
        <tr class='dark bb' style='background-color:lightyellow'>
                <td style='text-align:center'>2</td>
-               <td><b>Java Collections Framework objects and Java 
arrays</b></td>
+               <td><b>Java Collections Framework objects, Java arrays, Java 
Optionals</b></td>
                <td>&nbsp;</td>
                <td>&nbsp;</td>
                <td>&nbsp;</td>
@@ -47,7 +47,7 @@ POJO Categories
                <td>
                        <b>With standard keys/values</b>
                        <br>Map keys are group [1, 4a, 6a] objects.
-                       <br>Map, Collection, and array values are group [1, 2, 
3ac, 4a, 6a] objects.    
+                       <br>Map, Collection, Optional, and array values are 
group [1, 2, 3ac, 4a, 6a] objects.  
                </td>
                <td>
                        <ul class='normal'>
@@ -55,6 +55,7 @@ POJO Categories
                                <li><c>TreeMap&lt;Integer,Bean&gt;</c>
                                <li><c>List&lt;<jk>int</jk>[][]&gt;</c>
                                <li><c>Bean[]</c>
+                               <li><c>Optional&lt;Bean&gt;</c>
                        </ul>
                </td>
                <td 
style='background-color:lightgreen;text-align:center'><b>yes</b></td>
@@ -71,6 +72,7 @@ POJO Categories
                        <ul class='normal'>
                                <li><c>HashSet&lt;Bean,Integer&gt;</c>
                                <li><c>TreeMap&lt;Integer,Reader&gt;</c>
+                               <li><c>Optional&lt;Reader&gt;</c>
                        </ul>
                </td>
                <td 
style='background-color:lightgreen;text-align:center'><b>yes</b></td>

Reply via email to