x-stream / xstream

Serialize Java objects to XML and back again.
http://x-stream.github.io
Other
749 stars 227 forks source link

Problem of TreeSet/TreeMap #353

Closed urn1ce closed 1 year ago

urn1ce commented 1 year ago

if you deserialize of TreeSet/TreeMap at java17 like this

package ...;

import com.thoughtworks.xstream.XStream;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.TreeSet;

public class XStreamTest {
    public static class MComparator implements Comparator<Object>{
        @Override
        public int compare(Object o1, Object o2) {
            return o1.toString().compareTo(o2.toString());
        }
    }
    public static void main(String[] args) {
        var ts = new TreeSet<Object>(new MComparator());
        var al = new ArrayList<Object>();
        al.add("ABC");
        ts.add(al);
        var xs = new XStream();
        xs.allowTypesByWildcard(new String[]{"**"});
        var str = xs.toXML(ts);
        System.out.println(str);
        var obj = xs.fromXML(str);//TreeMap has the same problem
        System.out.println(obj);
    }
}

you will see error

<sorted-set>
  <comparator class="xxxxx.XStreamTest$MComparator"/>
  <list>
    <string>ABC</string>
  </list>
</sorted-set>
Exception in thread "main" java.lang.ExceptionInInitializerError
    at com.thoughtworks.xstream.converters.collections.TreeSetConverter.unmarshal(TreeSetConverter.java:62)
    at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:74)
    at com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:72)
    at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:68)
    at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:52)
    at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:136)
    at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:32)
    at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1464)
    at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1441)
    at com.thoughtworks.xstream.XStream.fromXML(XStream.java:1321)
    at com.thoughtworks.xstream.XStream.fromXML(XStream.java:1312)
    at com.example.webenv174jar.XStreamTest.main(XStreamTest.java:27)
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private transient java.util.NavigableMap java.util.TreeSet.m accessible: module java.base does not "opens java.util" to unnamed module @3c9d0b9d
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
    at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178)
    at java.base/java.lang.reflect.Field.setAccessible(Field.java:172)
    at com.thoughtworks.xstream.core.util.Fields.locate(Fields.java:40)
    at com.thoughtworks.xstream.converters.collections.TreeSetConverter$Reflections.<clinit>(TreeSetConverter.java:135)

I think the problem is at TreeSetConverter -> TreeSetConverter.Reflections.sortedMapField TreeMapConverter -> TreeMapConverter.Reflections.comparatorField Fields.locate -> field.setAccessible(true); related exception also need fix

maybe influence after java9 i guess

joehni commented 1 year ago

This is no bug, this is by design. A TreeSet/TreeMap may have an own Comparator and the Java API has no official way, to get them, therefore reflection is the only way. So, do as the exception says, run your application with run time option "--add-opens java.base/java.util=ALL-UNNAMED"

urn1ce commented 1 year ago

This is no bug, this is by design. A TreeSet/TreeMap may have an own Comparator and the Java API has no official way, to get them, therefore reflection is the only way. So, do as the exception says, run your application with run time option "--add-opens java.base/java.util=ALL-UNNAMED"

I known your meaning, but if i change like this (still not use "--add-opens java.base/java.util=ALL-UNNAMED") public static void main(String[] args) { var ts = new TreeSet(); //var al = new ArrayList(); //al.add("ABC"); //ts.add(al); ts.add("");//String is Comparable var xs = new XStream(); xs.allowTypesByWildcard(new String[]{"**"}); var str = xs.toXML(ts); System.out.println(str); var obj = xs.fromXML(str);//TreeMap has the same problem System.out.println(obj); } The problem like this: Exception in thread "main" java.lang.ExceptionInInitializerError at com.thoughtworks.xstream.converters.collections.TreeSetConverter.unmarshal(TreeSetConverter.java:62) at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:74) at com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:72) at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:68) at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:52) at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:136) at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:32) at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1464) at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1441) at com.thoughtworks.xstream.XStream.fromXML(XStream.java:1321) at com.thoughtworks.xstream.XStream.fromXML(XStream.java:1312) at com.example.webenv174jar.XStreamTest.main(XStreamTest.java:26) Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private transient java.util.NavigableMap java.util.TreeSet.m accessible: module java.base does not "opens java.util" to unnamed module @11a9e7c8 at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178) at java.base/java.lang.reflect.Field.setAccessible(Field.java:172) at com.thoughtworks.xstream.core.util.Fields.locate(Fields.java:40) at com.thoughtworks.xstream.converters.collections.TreeSetConverter$Reflections.(TreeSetConverter.java:135) This problem is also by design?

joehni commented 1 year ago

Yes, because how should XStream know, if a comparator has been set? There's just one way: Look into the private field.

urn1ce commented 1 year ago

Yes, because how should XStream know, if a comparator has been set? There's just one way: Look into the private field.

So even if not use comparator, also need "--add-opens java.base/java.util=ALL-UNNAMED", it maybe a bit redundant, if catch and handle exception may have a better experience., but i understand, thank you