Hi, Jörg Schaible wrote:
> Hi Алексеев, > > Алексеев Сергей wrote: > >> Hello team. I got some question/problem with XStream. >> >> I have a class with custom realization of methods equals/hashCode. >> Such >> objects contains in HashSet collection. Default XStream behavior: create >> object instance, read attributes, read inner fields, return object and >> add it to collection if needed. >> The problem: due a specific object graph on some step of >> deserialization >> object was created, added to collection, and after all other fields was >> loaded. After fields was loaded, result of hashCode was changed, causing >> "damage" to collection (because HashSet uses HashMap inside). That >> happens because xstream got object with null fields from cache by >> 'refference' attribute. This does not looks like a bug, but working >> behavior is not ideal. > > Well, your object graph has circular references and at therefore it is > simply not possible. At some stage, one of the object cannot be > initialized fully. XStream behaves even here exactly like Java > serialization. Let ConnectionPin and Pin both implement Serializable and > make the serialization algorithm exchangeable: > > ===================== %< =================== > private static List<?> serializedWithXStream(List<ConnectionPin> > inList) > { > /* toXML ---------------------------------------- */ > XStream xstream = new XStream(); > xstream.setMode(XStream.ID_REFERENCES); > xstream.autodetectAnnotations(true); > > String serialization = xstream.toXML(inList); > System.out.println(serialization); > > /* fromXML ---------------------------------------- */ > > System.out.println("\nLoad from xml\n"); > > List<?> list = (List<?>)xstream.fromXML(serialization); > > System.out.println(); > return list; > } > > private static List<?> > serializedWithJavaSerialization(List<ConnectionPin> inList) { > try { > /* to bytes ---------------------------------------- */ > ByteArrayOutputStream outputStream = new > ByteArrayOutputStream(); > ObjectOutputStream oos = new ObjectOutputStream(outputStream); > oos.writeObject(inList); > oos.close(); > > /* fromXML ---------------------------------------- */ > > System.out.println("\nLoad from bytes\n"); > > ByteArrayInputStream inputStream = new > ByteArrayInputStream(outputStream.toByteArray()); > ObjectInputStream ois = new ObjectInputStream(inputStream); > > List<?> list = (List<?>)ois.readObject(); > > System.out.println(); > return list; > } catch (IOException e) { > throw new RuntimeException(e); > } catch (ClassNotFoundException e) { > throw new RuntimeException(e); > } > } > ===================== %< =================== > > If you run your application now calling one time the serialization with > XStream and one time with Java serialization, you'll see, that you have > exactly the same problems, because it is simply not possible to have the > referring and the referred element initialized completely at the same > time. > >> In my project I fixed it by restoring collection (just recreate it with >> same objects) in readResolve of other object (1 level upward in graph). >> There are other problem: calling readResolve does not guarantee that >> all >> fields of 'this' object (including sub objects in fields) was fully >> deserialized at moment of call. That why I cant fix collection in >> readResolve of object with HashSet or HashMap and should do it in other >> place. >> >> There are attach file that nicely describes a situation. > > You will have to break the circle for serialization and restore it at > deserialization time. You may consider readResolve and writeReplace > methods that actually write/read different replacement objects or play > with Externalizable or ... actually it is enough to prevent the usage of the bogus hash code. Simply use an appropriate Set implementation: ===================== %< =================== ... public static class ConnectionPin { public Set<Connection> inConnections = new ArraySet<Main.Connection>(); public Set<Connection> outConnections = new ArraySet<Main.Connection>(); ... } @XStreamAlias( "array-set" ) @XStreamConverter( CollectionConverter.class ) public static class ArraySet<T> extends AbstractSet<T> implements Set<T> { private List<T> list = new ArrayList<T>(); @Override public boolean add(T e) { return list.contains(e) ? false : list.add(e); } @Override public int size() { return list.size(); } @Override public Iterator<T> iterator() { return list.iterator(); } } ===================== %< =================== While in this use case a ConnectionPin instance cannot be complete in readResolve (which is no longer required anyway), the sets are setup correctly now. Cheers, Jörg --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email