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


Reply via email to