Skip to content

Commit

Permalink
Add converter for Optional. Closes x-stream#293 and x-stream#294.
Browse files Browse the repository at this point in the history
  • Loading branch information
joehni committed Dec 29, 2022
1 parent 901e1e2 commit 373016c
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 2 deletions.
4 changes: 4 additions & 0 deletions xstream-distribution/src/content/changes.html
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,16 @@ <h2>Major changes</h2>
<ul>
<li>GHI:#308: Add converter for AtomicBoolean, AtomicInteger, AtomicLong, and AtomicReference of
package java.util.concurrent.atomic.</li>
<li>GHI:#293: Add converter for Optional of package java.util.</li>
</ul>

<h2>Stream compatibility</h2>

<ul>
<li>The atomic types with new converters of package java.util.concurrent.atomic, that have been written with
previous versions of XStream, can still be deserialized.</li>
<li>The Optional types with new converters of package java.util, that have been written with previous versions
of XStream, can still be deserialized.</li>
</ul>

<h2>API changes</h2>
Expand All @@ -127,6 +130,7 @@ <h2>API changes</h2>
<li>Added c.t.x.converters.extended.AtomicIntegerConverter.</li>
<li>Added c.t.x.converters.extended.AtomicLongConverter.</li>
<li>Added c.t.x.converters.extended.AtomicReferenceConverter.</li>
<li>Added c.t.x.converters.extended.OptionalConverter.</li>
</ul>

<h1 id="1.4.19">1.4.19</h1>
Expand Down
11 changes: 11 additions & 0 deletions xstream-distribution/src/content/converters.html
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,17 @@
<td>Available with Java 1.5 or greater.</td>
<td>normal</td>
</tr>
<tr>
<td><a href="javadoc/com/thoughtworks/xstream/converters/extended/OptionalConverter.html">OptionalConverter</a></td>
<td>java.util.Optional</td>
<td class="example">
&lt;optional&gt;<br/>
&nbsp;&nbsp;&lt;value&nbsp;&nbsp;class=&quot;string&quot;&gt;test&lt;/value&gt;<br/>
&lt;/optional&gt;
</td>
<td>Available with Java 1.8 or greater.</td>
<td>normal</td>
</tr>
<tr>
<td><a href="javadoc/com/thoughtworks/xstream/converters/enums/EnumMapConverter.html">EnumMapConverter</a></td>
<td>java.util.EnumMap</td>
Expand Down
3 changes: 2 additions & 1 deletion xstream-distribution/src/content/team.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<html>
<!--
Copyright (C) 2005, 2006 Joe Walnes.
Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016, 2017, 2019, 2020, 2021 XStream committers.
Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016, 2017, 2019, 2020, 2021, 2022 XStream committers.
All rights reserved.
The software in this package is published under the terms of the BSD
Expand Down Expand Up @@ -130,6 +130,7 @@ <h2 id="contributors">Contributors</h2>
<li>Falko Modler</li>
<li>Julia Boes of Oracle</li>
<li>Chris Hegarty of Oracle</li>
<li>Emanuel Alves</li>
<li>Basil Crow</li>
</ul>

Expand Down
5 changes: 5 additions & 0 deletions xstream/src/java/com/thoughtworks/xstream/XStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
Expand Down Expand Up @@ -162,6 +163,7 @@
import com.thoughtworks.xstream.converters.extended.JavaMethodConverter;
import com.thoughtworks.xstream.converters.extended.LocaleConverter;
import com.thoughtworks.xstream.converters.extended.LookAndFeelConverter;
import com.thoughtworks.xstream.converters.extended.OptionalConverter;
import com.thoughtworks.xstream.converters.extended.PathConverter;
import com.thoughtworks.xstream.converters.extended.RegexPatternConverter;
import com.thoughtworks.xstream.converters.extended.SqlDateConverter;
Expand Down Expand Up @@ -758,6 +760,7 @@ protected void setupSecurity() {
allowTypeHierarchy(Chronology.class);
types.add(ValueRange.class);
types.add(WeekFields.class);
types.add(Optional.class);

types.remove(null);
allowTypes(types.toArray(new Class[types.size()]));
Expand Down Expand Up @@ -901,6 +904,7 @@ protected void setupAliases() {
alias("julian-field", JVM.loadClassForName("java.time.temporal.JulianFields$Field"));
alias("temporal-value-range", ValueRange.class);
alias("week-fields", WeekFields.class);
alias("optional", Optional.class);
alias("serialized-lambda", SerializedLambda.class);

aliasType("charset", Charset.class);
Expand Down Expand Up @@ -1014,6 +1018,7 @@ protected void setupConverters() {
registerConverter(new YearMonthConverter(), PRIORITY_NORMAL);
registerConverter(new ZonedDateTimeConverter(), PRIORITY_NORMAL);
registerConverter(new ZoneIdConverter(), PRIORITY_NORMAL);
registerConverter(new OptionalConverter(mapper), PRIORITY_NORMAL);
registerConverter(new DynamicProxyConverter(mapper, classLoaderReference), PRIORITY_NORMAL);
registerConverter(new JavaClassConverter(classLoaderReference), PRIORITY_NORMAL);
registerConverter(new JavaMethodConverter(classLoaderReference), PRIORITY_NORMAL);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (C) 2022 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
* Created on 8. December 2022 by Joerg Schaible
*/
package com.thoughtworks.xstream.converters.extended;

import java.util.Optional;

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.core.util.HierarchicalStreams;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;


/**
* Converts an Optional type.
*
* @since upcoming
* @author Emanuel Alves
* @author J&ouml;rg Schaible
*/
public class OptionalConverter implements Converter {

private final Mapper mapper;

public OptionalConverter(final Mapper mapper) {
this.mapper = mapper;
}

@Override
public boolean canConvert(final Class<?> type) {
return type != null && type == Optional.class;
}

@Override
public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) {
final Optional<?> optional = (Optional<?>)source;
if (optional.isPresent()) {
writer.startNode(mapper.serializedMember(Optional.class, "value"));

final Object object = optional.get();
final String name = mapper.serializedClass(object != null ? object.getClass() : null);
writer.addAttribute(mapper.aliasForSystemAttribute("class"), name);
context.convertAnother(optional.get());
writer.endNode();
}
}

@Override
public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) {
final Object value;
if (reader.hasMoreChildren()) {
reader.moveDown();

final Class<?> type = HierarchicalStreams.readClassType(reader, mapper);
value = context.convertAnother(context, type);
reader.moveUp();
return Optional.of(value);
} else {
return Optional.empty();
}
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2003, 2004, 2005 Joe Walnes.
* Copyright (C) 2006, 2007, 2008, 2012, 2014, 2016, 2017, 2018, 2020 XStream Committers.
* Copyright (C) 2006, 2007, 2008, 2012, 2014, 2016, 2017, 2018, 2020, 2022 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
Expand Down Expand Up @@ -29,6 +29,7 @@
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.TimeZone;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -288,4 +289,49 @@ public void testPathIsImmutable() {
+ " <path>same</path>\n" //
+ "</path-array>");
}

public void testEmptyOptional() {
final Optional<Object> optional = Optional.empty();
assertBothWays(optional, "<optional/>");
}

public void testOptional() {
final Optional<String> optional = Optional.of("test");
assertBothWays(optional, ("" //
+ "<optional>\n" //
+ " <value class='string'>test</value>\n" //
+ "</optional>").replace('\'', '"'));
}

public void testOptionalWithAlias() {
final Optional<String> optional = Optional.of("test");
xstream.aliasField("junit", Optional.class, "value");
assertBothWays(optional, ("" //
+ "<optional>\n" //
+ " <junit class='string'>test</junit>\n" //
+ "</optional>").replace('\'', '"'));
}

public void testOptionalIsRerenceable() {
@SuppressWarnings("unchecked")
final Optional<Object>[] array = new Optional[3];
array[0] = array[2] = Optional.of("test");
array[1] = Optional.empty();
assertBothWays(array, ("" //
+ "<optional-array>\n" //
+ " <optional>\n" //
+ " <value class='string'>test</value>\n" //
+ " </optional>\n" //
+ " <optional/>\n" //
+ " <optional reference='../optional'/>\n" //
+ "</optional-array>").replace('\'', '"'));
}

public void testOptionalWithOldFormat() {
assertEquals(Optional.of("test"), xstream.fromXML("" //
+ "<java.util.Optional>\n" //
+ " <value class='string'>test</value>\n" //
+ "</java.util.Optional>"));
}

}

0 comments on commit 373016c

Please sign in to comment.