diff --git a/xstream-distribution/src/content/changes.html b/xstream-distribution/src/content/changes.html
index 65e08e4a3..1e0a3fe4d 100644
--- a/xstream-distribution/src/content/changes.html
+++ b/xstream-distribution/src/content/changes.html
@@ -108,6 +108,13 @@
Upcoming 1.4.x maintenance release
Not yet released.
+ Major changes
+
+
+ - Add a converter for the WeakHashMap which does not write any elements of the map. Avoids also access to the
+ ReentrantLock contained in the WeakHashMap since Java 19.
+
+
Minor changes
@@ -125,6 +132,13 @@ API changes
- Added constant c.t.x.io.xml.PrettyPrintWriter.XML_1_0_REPLACEMENT.
- Added constant c.t.x.io.xml.PrettyPrintWriter.XML_1_1_REPLACEMENT.
+ - Added c.t.x.converters.collections.WeakHashMapConverter.
+
+
+ Stream compatibility
+
+
+ - The WeakHashMaps, that have been written with previous versions of XStream, can still be deserialized.
1.4.20
diff --git a/xstream-distribution/src/content/converters.html b/xstream-distribution/src/content/converters.html
index 3c2b21161..ece101a5f 100644
--- a/xstream-distribution/src/content/converters.html
+++ b/xstream-distribution/src/content/converters.html
@@ -301,6 +301,17 @@
If the Properties instance includes a set of default Properties, these are serialized in a nested <defaults>
element.
normal |
+
+ WeakHashMapConverter |
+ java.util.WeakHashMap |
+
+ <weak-hash-map/>
+ |
+ A WeakHashMap is supposed to release its elements when they are no longer referenced. Therefore is at unmarshalling time no guarantee that
+ an entry is still available when it is referenced later in the stream. As consequence the converter will marshal no elements at all, it
+ will create an empty WeakHashMap at unmarshalling time. |
+ normal |
+
SingletonCollectionConverter |
java.util.Collections.singletonList().getClass() java.util.Collections.singleton().getClass() |
diff --git a/xstream/src/java/com/thoughtworks/xstream/XStream.java b/xstream/src/java/com/thoughtworks/xstream/XStream.java
index 76956a192..123e6d23f 100644
--- a/xstream/src/java/com/thoughtworks/xstream/XStream.java
+++ b/xstream/src/java/com/thoughtworks/xstream/XStream.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2003, 2004, 2005, 2006 Joe Walnes.
- * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 XStream Committers.
+ * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2024 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
@@ -104,6 +104,7 @@
import java.util.TreeSet;
import java.util.UUID;
import java.util.Vector;
+import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -146,6 +147,7 @@
import com.thoughtworks.xstream.converters.collections.SingletonMapConverter;
import com.thoughtworks.xstream.converters.collections.TreeMapConverter;
import com.thoughtworks.xstream.converters.collections.TreeSetConverter;
+import com.thoughtworks.xstream.converters.collections.WeakHashMapConverter;
import com.thoughtworks.xstream.converters.enums.EnumConverter;
import com.thoughtworks.xstream.converters.enums.EnumMapConverter;
import com.thoughtworks.xstream.converters.enums.EnumSetConverter;
@@ -843,6 +845,7 @@ protected void setupAliases() {
alias("hashtable", Hashtable.class);
alias("linked-hash-map", LinkedHashMap.class);
alias("linked-hash-set", LinkedHashSet.class);
+ alias("weak-hash-map", WeakHashMap.class);
alias("concurrent-hash-map", ConcurrentHashMap.class);
alias("atomic-boolean", AtomicBoolean.class);
alias("atomic-int", AtomicInteger.class);
@@ -1001,6 +1004,7 @@ protected void setupConverters() {
registerConverter(new EnumConverter(), PRIORITY_NORMAL);
registerConverter(new EnumSetConverter(mapper), PRIORITY_NORMAL);
registerConverter(new EnumMapConverter(mapper), PRIORITY_NORMAL);
+ registerConverter(new WeakHashMapConverter(), PRIORITY_NORMAL);
registerConverter(new FileConverter(), PRIORITY_NORMAL);
if (JVM.isSQLAvailable()) {
diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/collections/WeakHashMapConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/collections/WeakHashMapConverter.java
new file mode 100644
index 000000000..fcb374605
--- /dev/null
+++ b/xstream/src/java/com/thoughtworks/xstream/converters/collections/WeakHashMapConverter.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 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 19. October 2024 by Joerg Schaible
+ */
+package com.thoughtworks.xstream.converters.collections;
+
+import java.util.WeakHashMap;
+
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+
+
+/**
+ * Converts a WeakHashMap. A WeakHashMap is supposed to release its elements when they are no longer referenced.
+ * Therefore is at unmarshalling time no guarantee that an entry is still available when it is referenced later in the
+ * stream. As consequence the converter will marshal no elements at all, it will create an empty WeakHashMap at
+ * unmarshalling time.
+ *
+ * @author Joerg Schaible
+ * @since upcoming
+ */
+public class WeakHashMapConverter implements Converter {
+
+ @Override
+ public boolean canConvert(final Class> type) {
+ return WeakHashMap.class == type;
+ }
+
+ @Override
+ public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) {
+ // do nothing
+ }
+
+ @Override
+ public WeakHashMap, ?> unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) {
+ @SuppressWarnings("rawtypes")
+ final WeakHashMap, ?> weakHashMap = new WeakHashMap();
+ return weakHashMap;
+ }
+}
diff --git a/xstream/src/test/com/thoughtworks/acceptance/MapTest.java b/xstream/src/test/com/thoughtworks/acceptance/MapTest.java
index 3823b2c44..bf9414812 100644
--- a/xstream/src/test/com/thoughtworks/acceptance/MapTest.java
+++ b/xstream/src/test/com/thoughtworks/acceptance/MapTest.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2003, 2004, 2005 Joe Walnes.
- * Copyright (C) 2006, 2007, 2011, 2017, 2018, 2021 XStream Committers.
+ * Copyright (C) 2006, 2007, 2011, 2017, 2018, 2021, 2024 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
@@ -18,6 +18,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.WeakHashMap;
import com.thoughtworks.acceptance.objects.Hardware;
import com.thoughtworks.acceptance.objects.Software;
@@ -109,6 +110,15 @@ public void testSupportsOldHashtables() {
assertBothWays(hashtable, expected);
}
+ public void testSupportsWeakHashMap() {
+ final WeakHashMap hashtable = new WeakHashMap<>();
+ hashtable.put("hello", "world");
+
+ final String expected = "";
+
+ assertBothWays(hashtable, expected);
+ }
+
static class ThingWithDifferentTypesOfMaps extends StandardObject {
private static final long serialVersionUID = 200403L;
final Map, ?> m1 = new HashMap<>();