3

I'm trying to use XStream to (de)serialize a HashMap of some of my own classes in an Android app. For example, one of the classes is Word, which has the following variables:

private String word;
private boolean capitalizable;
private int useCount;
private HashMap<Character,Integer> endPunctuation;
private HashSet<String> nextWritables;

I already have the whole set up working in a standard java application, i'm just trying to wrap it all in an android UI (which works fine). Serializing works fine in Android. The problem I'm having is when I deserialize, i get the following error:

com.thoughtworks.xstream.converters.ConversionException: Cannot construct chatai.Word as it does not have a no-args constructor : Cannot construct chatai.Word as it does not have a no-args constructor 
---- Debugging information ---- 
message             : Cannot construct chatai.Word as it does not have a no-args constructor 
cause-exception     : com.thoughtworks.xstream.converters.reflection.ObjectAccessException 
cause-message       : Cannot construct chatai.Word as it does not have a no-args constructor 
class               : java.util.HashMap 
required-type       : chatai.Word 
path                : /map/entry/chatai.Word 
line number         : 1 
------------------------------- 

I get this error if I use any version of XStream other than 1.4.1 on my desktop application. I get the error always on my android application, no matter the verison of XStream. I'm sure that the desktop one has that issue because it's running on Java 7. I'm not sure about android. It has something to do with reflection, due to this warning when adding the xstream-1.4.1.jar:

[2011-09-07 21:06:52 - DroidBot] Dx warning: Ignoring InnerClasses attribute for an anonymous inner class 
(com.thoughtworks.xstream.XStream$2) that doesn't come with an 
associated EnclosingMethod attribute. This class was probably produced by a 
compiler that did not target the modern .class file format. The recommended 
solution is to recompile the class from source, using an up-to-date compiler 
and without specifying any "-target" type options. The consequence of ignoring 
this warning is that reflective operations on this class will incorrectly 
indicate that it is *not* an inner class. 

A quick test shows me that serializing and deserializing a String object in Android works fine. How can I get rid of that error?

OneWorld
  • 17,512
  • 21
  • 86
  • 136
Rahat Ahmed
  • 2,191
  • 2
  • 30
  • 40
  • >com.thoughtworks.xstream.converters.ConversionException: Cannot construct chatai.Word as it does not have a no-args constructor. Does the Word class have a no arg constructor? – sbridges Sep 08 '11 at 04:04
  • 1
    No, and it's not supposed to. It doesn't complain in the desktop application. And I can read the android serialized file on the desktop application as well. – Rahat Ahmed Sep 09 '11 at 00:04
  • the error message seems pretty clear, why is it asking for a default constructor? – sbridges Sep 09 '11 at 01:43
  • Well, someone on the mailing list told me that I'd have to recompile xstream to Java 5 or something. I've decided just to use the built in serialization for now. – Rahat Ahmed Sep 09 '11 at 21:16
  • Also see http://stackoverflow.com/questions/9621372/xstream-no-args-constructor-error?rq=1 for no-args constructor errors that were connected to different JDK versions which applies more to desktop machines – OneWorld Oct 24 '14 at 08:32

1 Answers1

2

Yes, this question is old, but for the curious internet researcher:

Deserialization requires xstream to construct an object and set all of its member fields to the values specified in the xml. If the object you are trying to deserialize does not have a no-arg constructor, then xstream needs help from the VM to build the object outside of the normal process of object instantiation and initialization. This help is available only in certain VMs; it is not available under the Dalvik VM when running on an Android device.

If you check the provider that is being used to deserialize your xml, you will find that on your desktop the provider is probably the Sun14ReflectionProvider, which uses special VM support to construct objects without calling their constructors. Under Android, the provider will be the PureJavaReflectionProvider, which cannot deserialize objects lacking a no-arg constructor.

    XStream xstream = new XStream();

    ReflectionProvider rp = xstream.getReflectionProvider();
    if(null != rp)
    {
        System.out.println("Provider class: " + rp.getClass().getName());
        if(rp instanceof Sun14ReflectionProvider)
            System.out.println("Using Sun14ReflectionProvider");
        else if(rp instanceof PureJavaReflectionProvider)
            System.out.println("Using PureJavaReflectionProvider");
    }

In summary: You cannot deserialize object lacking a no-arg constructor while running XStream in an Android environment. If you have control over the object, refactor it to have a no-arg constructor. If you do not have control over the object, you are out of luck as far as XStream is concerned.

MikeWeber
  • 36
  • 3
  • no-arg constructors seem to be supported in newer Android versions as I found out in my little test. Those devices worked: CM11 with 4.4.4, Samsung device with 4.3. Those devices raised the exception: Samsung devices with 4.2.2 and 4.1.2. So it really is safer to implement no-args constructors AND test your app on an older Android device! – OneWorld Oct 24 '14 at 08:13
  • Follow up to my tests. I determined the reflection providers with the code of MikeWeber. My Samsung device with Android 4.3 used `com.thoughtworks.xstream.converters.reflection.SunLimitedUnsafeReflectionProvider` which was detected as instance of `PureJavaReflectionProvider`. The older Samsung device with Android 4.2.2 used just the `PureJavaReflectionProvider`. – OneWorld Oct 24 '14 at 09:28
  • Docs to SunLimitedUnsafeReflectionProvid‌​er say: _Instantiates a new object bypassing the constructor using undocumented internal JDK features. [(..)](http://xstream.codehaus.org/javadoc/com/thoughtworks/xstream/converters/reflection/SunLimitedUnsafeReflectionProvider.html) is used as fallback on platforms that do not provide the complete implementation level for the internals (like Dalvik)_ Also see docs of [PureJavaReflectionProvider](http://xstream.codehaus.org/javadoc/com/thoughtworks/xstream/converters/reflection/PureJavaReflectionProvider.html) – OneWorld Oct 24 '14 at 09:36