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> </td>
<td> </td>
<td> </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<Integer,Bean></c>
<li><c>List<<jk>int</jk>[][]></c>
<li><c>Bean[]</c>
+ <li><c>Optional<Bean></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<Bean,Integer></c>
<li><c>TreeMap<Integer,Reader></c>
+ <li><c>Optional<Reader></c>
</ul>
</td>
<td
style='background-color:lightgreen;text-align:center'><b>yes</b></td>