- First of all, I use Hibernate 2. I know, it's old, but it does the work and I won't fix it unless it is broken. I expect most of the advice will also apply to Hibernate 3.
- I'm aliasing all classes using
XStream.alias()
. Reason being that on the server side, some data model classes are actually subclassed for additional (although non-Hibernate) related functionality. You might or might not need this. - This is however essential: We need to ensure that XStream will treat Hibernate lists, sets, and maps as Java lists, sets, and maps. I believe this is what helps avoid the infamous "com.thoughtworks.xstream.converters.ConversionException: Cannot handle CGLIB enhanced proxies with multiple callbacks..." problem. There are three things that need to be done:
- use
Xstream.addDefaultImplementation()
to tell XStream to treat all Hibernate-enhanced collection classes as plain Java collections:xstream.addDefaultImplementation(
net.sf.hibernate.collection.List.class, java.util.List.class);
xstream.addDefaultImplementation(
net.sf.hibernate.collection.Map.class, java.util.Map.class);
xstream.addDefaultImplementation(
net.sf.hibernate.collection.Set.class, java.util.Set.class); - Finally, in order for XStream to actually handle these collections I needed to define and register some custom converters that are able to handle Hibernate collections as Java collections:
Mapper mapper = xstream.getMapper();
These custom converter classes are rather trivial, here are their definitions. All they really do is extend the XStream built-in collection and map converters, and declare their ability to handle Hibernate lists, sets, and maps:
xstream.registerConverter(new HibernateCollectionConverter(mapper));
xstream.registerConverter(new HibernateMapConverter(mapper));import net.sf.hibernate.collection.List;
and
import net.sf.hibernate.collection.Set;
import com.thoughtworks.xstream.converters.collections.CollectionConverter;
import com.thoughtworks.xstream.mapper.Mapper;
class HibernateCollectionConverter extends CollectionConverter {
HibernateCollectionConverter(Mapper mapper) {
super(mapper);
}
public boolean canConvert(Class type) {
return super.canConvert(type) || type == List.class || type == Set.class;
}
}import net.sf.hibernate.collection.Map;
import com.thoughtworks.xstream.converters.collections.MapConverter;
import com.thoughtworks.xstream.mapper.Mapper;
class HibernateMapConverter extends MapConverter {
HibernateMapConverter(Mapper mapper) {
super(mapper);
}
public boolean canConvert(Class type) {
return super.canConvert(type) || type == Map.class;
}
}
- use
That's all I did and it eliminated all of my Hibernate+XStream problems - hope it will also help you.
So is this with Hibernate 2 or Hibernate 3? I am using Hibernate 3 and i tried using the above solution and it does not work.
ReplyDeleteThe other weird thing is the same stuff works (XML conversion) on the same Object (same hibernate mapping) on a different scenario.
Wondering whats wrong??? Any inputs on this would be greatly appreciated
Cheers & Thanks
Peacemaker
I was using Hibernate 2 -- it could well be that it doesn't work this way with Hibernate 3. If anyone has a H3 solution, you're welcome to post it here.
ReplyDeleteHibernate 3 is simply a matter of replacing net.sf.hibernate.collection with org.hibernate.mapping in each of the files.
ReplyDeleteAnother issue related to hibernate3 and xstream is that the CGLIB proxy generated by hibernate actually contains a null second value. I have created a fix converter class based on the CGLIBEnhancedConverter class.
ReplyDeleteThis can be downloaded from http://bushlife.com.au/downloads/xstream/CGLIBEnhancedConverterHibernateFix.java
This is relevant for at least XStream 1.2.2 and XStream 1.3.
This needs to be registered via xstream.registerConverter(new CGLIBEnhancedConverterHibernateFix(mapper,xstream.getReflectionProvider()));
For more information see http://jira.codehaus.org/browse/XSTR-423
Greate Sweetfa!!
ReplyDeleteYour solution is work.
However, the addDefaultImplement with org.hibernate.mapping.List, Map and Set are not work.
xstream.addDefaultImplementation(org.hibernate.mapping.List.class, java.util.List.class);
xstream.addDefaultImplementation(org.hibernate.mapping.Map.class, java.util.Map.class);
xstream.addDefaultImplementation(org.hibernate.mapping.Set.class, java.util.Set.class);
Mapper mapper = xstream.getMapper();
xstream.registerConverter(new HibernateCollectionConverter(mapper));
xstream.registerConverter(new HibernateMapConverter(mapper));
I got this error during runtime.
com.thoughtworks.xstream.converters.ConversionException: Cannot instantiate org.hibernate.mapping.Set
Anyway, only these line is enough for me.
Mapper mapper = xstream.getMapper();
xstream.registerConverter(new CGLIBEnhancedConverterHibernateFix(mapper,xstream.getReflectionProvider()));
THANK YOU ALL for this!
ReplyDeleteHibernate 3 support that worked in my situation:
ReplyDeleteXStream xstream = new XStream();
xstream.addDefaultImplementation(java.util.ArrayList.class, org.hibernate.collection.PersistentList.class); xstream.addDefaultImplementation(java.util.HashMap.class, org.hibernate.collection.PersistentMap.class); xstream.addDefaultImplementation(java.util.HashSet.class, org.hibernate.collection.PersistentSet.class);
Mapper mapper = xstream.getMapper();
xstream.registerConverter(new HibernateCollectionConverter(mapper));
xstream.registerConverter(new HibernateMapConverter(mapper));
class HibernateCollectionConverter extends
CollectionConverter {
HibernateCollectionConverter(Mapper mapper) {
super(mapper);
}
public boolean canConvert(Class type) {
return super.canConvert(type)
|| org.hibernate.collection.PersistentList.class
.equals(type)
|| org.hibernate.collection.PersistentSet.class
.equals(type);
}
}
class HibernateMapConverter extends MapConverter {
HibernateMapConverter(Mapper mapper) {
super(mapper);
}
public boolean canConvert(Class type) {
return super.canConvert(type)
|| org.hibernate.collection.PersistentMap.class
.equals(type);
}
}
This was a life saving tip. Thanks so much!
ReplyDeleteTims solution worked for me. However, I also had to add a check for org.hibernate.collection.PersistentBag on HibernateCollectionConverter.canConvert(Class type).
ReplyDeleteBest regards,
Daniel Felix Ferber
Very thank you Tim.
ReplyDeleteNice tips!
xStream.addDefaultImplementation(org.hibernate.mapping.Set.class, java.util.List.class);
ReplyDeletexStream.addDefaultImplementation(org.hibernate.mapping.List.class, java.util.Set.class);
xStream.addDefaultImplementation(org.hibernate.mapping.Map.class, java.util.Map.class);
xStream.addDefaultImplementation(org.hibernate.collection.PersistentSet.class, java.util.Set.class);
Thanks a lot :-)
ReplyDelete