我一直面临这个问题,序列化的休眠对象会产生意外的 xml,其中包含来自 Hibernate 的所有检测代码。
在序列化对象之前,我们对对象进行了一些清理。
但是,是否有可用于直接序列化对象的标准选项?
我以前没有使用过 XStream,但是我已经序列化了 Hibernate 管理的实体。这不好玩。
有两个大问题:
前者很明显 - 您需要实际数据进行序列化。后者不那么重要 - 您针对集合接口(例如:)声明的任何一对多关系都Set<T>
将被 Hibernate 自己的(不可序列化!)集合实现插入。这很可能是 Hibernate 的类渗入您的对象的地方。
我最终编写了执行此操作的反射代码(实际上是内省的):
请注意,第 2 步很重要 - 如果您在关闭会话之前替换集合,Hibernate 只会在关闭时将其自己的集合放回...
编辑: @cliff.meyers 发现了一个我忘记提及的实现细节:如果你这样做,你需要将对象图限制在你自己的实体上,并注意循环引用路径(例如:通过缓存对你的对象的引用'已经走了)。
我想出了一些足够的解决方案。在我的应用程序中,只有 PersistentSets 会弄乱 XStream 生成的 XML。所以我向 XStream 添加了另一个转换器(它与打开的 Hibernate Session 和活动对象一起运行):
XStream xs = new XStream();
xs.registerConverter(new CollectionConverter(xs.getMapper()) {
@Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
org.hibernate.collection.PersistentSet ps = (PersistentSet) source;
super.marshal(new HashSet(ps), writer, context);
}
@Override
public boolean canConvert(Class type) {
return type.isAssignableFrom(org.hibernate.collection.PersistentSet.class);
}
}, XStream.PRIORITY_VERY_HIGH);
String s = xs.toXML(processInstance);
序列化的 XML 如下所示:
<processLogs class="org.hibernate.collection.PersistentSet">
<pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog>
<id>813017</id>
<entryDate>
<time>1310832421216</time>
<timezone>GMT</timezone>
</entryDate>
<eventI18NKey>process.log.action-performed</eventI18NKey>
<additionalInfo>Wydrukuj wniosek</additionalInfo>
<logValue>GENERATE_APPLICATION</logValue>
<logType>PERFORM_ACTION</logType>
<state reference="../../../definition/states/pl.net.bluesoft.rnd.processtool.model.config.ProcessStateConfiguration[8]"/>
<processInstance reference="../../.."/>
<user reference="../../../creator"/>
</pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog>
<pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog>
<id>808211</id>
<entryDate>
<time>1310828206169</time>
<timezone>GMT</timezone>
</entryDate>
<eventI18NKey>process.log.action-performed</eventI18NKey>
<additionalInfo>Zaakceptuj</additionalInfo>
<logValue>ACCEPT</logValue>
<logType>PERFORM_ACTION</logType>
<state reference="../../../definition/states/pl.net.bluesoft.rnd.processtool.model.config.ProcessStateConfiguration[4]"/>
<processInstance reference="../../.."/>
<user reference="../../../creator"/>
</pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog>
在我的例子中,class 属性并不重要,所以我忽略了它的值。你当然可以修补它。
XStream 现在提供了一个单独的模块/jar 来处理 Hibernate 集合。有关示例,请参阅XStream 的常见问题解答。
尽管我没有使用休眠,但我遇到了类似的问题。我查看了实体修剪器,但它并不是我想要的,因为我正在寻找更简单的解决方案。
我想出了一个非常简单的解决方案,它使用反射来代理 CGLIB 增强对象,就像一个魅力。
请访问http://www.anzaan.com/2010/06/serializing-cglib-enhanced-proxy-into-json-using-xstream/示例和代码。
Codehaus JIRA 上有一些关于此(和示例代码)的信息:
http://jira.codehaus.org/browse/XSTR-226
我们为许多其他远程实现(Axis 1、Blaze DS 等)编写了一些工具来解决此类问题。我们所做的与 Dan 的解决方案非常相似,尽管我们添加了声明哪些对象路径要走以及哪些要“剪断”的功能,因为在许多情况下我们对所有数据都不感兴趣;它还会导致“n+1 选择”问题发生数千次的严重问题!:) 我认为实现 XStream 转换器将是最佳方法,因为您只需遍历对象图一次。如果您在 Session 上设置 FlushMode.MANUAL ,您还应该能够随时修改对象图,而 Hibernate 不会做任何讨厌的事情。谨慎使用它,因为它是一种先进的技术。
没有使用它,但xstream-for-beans似乎适合(引用):
该项目提供了在以下方面增强 XStream 的映射器和转换器的实现:
- 序列化对象,因为它们被 getter 和 setter 公开。可用于字段的 XStream 功能应适用于为 getter/setter 属性定义的属性。
- 清理托管对象的序列化:自动省略不相关的字段和类信息。
- 处理“离线”字段和代理对象。
Converter
不幸的是,作为闭源项目的一部分,我编写了一次自定义 XStream来处理这个问题。xstream-for-beans 处理相同的问题,非常值得一试。
我过去成功地使用了 Terracotta 的Pojoizer实用程序,但我认为它不再被维护了。