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