JAXBV3.3.0使用以下代码克隆docx4j对象:
public static <T> T deepCopy(T value, JAXBContext jc) {
if (value==null) {
throw new IllegalArgumentException("Can't clone a null argument");
}
try {
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) value.getClass();
JAXBElement<T> contentObject = new JAXBElement<T>(new QName(clazz.getSimpleName()), clazz, value);
JAXBSource source = new JAXBSource(jc, contentObject);
JAXBElement<T> elem = jc.createUnmarshaller().unmarshal(source, clazz);
T res;
if (value instanceof JAXBElement<?>) {
@SuppressWarnings("unchecked")
T resT = (T) elem;
res = resT;
} else {
@SuppressWarnings("unchecked")
T resT = (T) elem.getValue();
res = resT;
}
return res;
} catch (JAXBException ex) {
throw new IllegalArgumentException(ex);
}
}使用MOXy v2.5.2 (我们使用它,因为它支持Java6)和最新的2.6.3版本,尝试克隆JAXBElement,例如:
public void testIssue212() {
CTBookmark bookmark = Context.getWmlObjectFactory().createCTBookmark();
JAXBElement<CTBookmark> el =Context.getWmlObjectFactory().createBodyBookmarkStart(bookmark);
Object o = XmlUtils.deepCopy(el);
}结果如下:
[Exception [EclipseLink-25007] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: A descriptor for class javax.xml.bind.JAXBElement was not found in the project. For JAXB, if the JAXBContext was bootstrapped using TypeMappingInfo[] you must call a marshal method that accepts TypeMappingInfo as an input parameter.]
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.handleXMLMarshalException(JAXBUnmarshaller.java:980)
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:303)
at org.docx4j.XmlUtils.deepCopy(XmlUtils.java:974)
... 25 more
Caused by: Exception [EclipseLink-25007] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: A descriptor for class javax.xml.bind.JAXBElement was not found in the project. For JAXB, if the JAXBContext was bootstrapped using TypeMappingInfo[] you must call a marshal method that accepts TypeMappingInfo as an input parameter.
at org.eclipse.persistence.exceptions.XMLMarshalException.descriptorNotFoundInProject(XMLMarshalException.java:140)
at org.eclipse.persistence.internal.oxm.Context$ContextState.getSession(Context.java:145)
at org.eclipse.persistence.oxm.XMLContext$XMLContextState.getSession(XMLContext.java:795)
at org.eclipse.persistence.oxm.XMLContext$XMLContextState.getSession(XMLContext.java:1)
at org.eclipse.persistence.internal.oxm.Context.getSession(Context.java:466)
at org.eclipse.persistence.oxm.XMLContext.getSession(XMLContext.java:364)
at org.eclipse.persistence.oxm.XMLContext.getSession(XMLContext.java:1)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:466)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:695)
at org.eclipse.persistence.oxm.XMLUnmarshaller.unmarshal(XMLUnmarshaller.java:655)
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:301)
... 26 more我们可以使用以下内容来解决此问题:
JAXBElement<T> elem;
if (Context.getJaxbImplementation().equals(JAXBImplementation.ECLIPSELINK_MOXy)
&& value instanceof JAXBElement<?>) {
elem = (JAXBElement<T>) value;
Class<?> valueClass = elem.getDeclaredType();
Marshaller mar = jc.createMarshaller();
ByteArrayOutputStream bout = new ByteArrayOutputStream(256);
mar.marshal(elem, bout);
Unmarshaller unmar = jc.createUnmarshaller();
elem = (JAXBElement<T>)unmar.unmarshal(new StreamSource(new ByteArrayInputStream(
bout.toByteArray())), valueClass);
}但是有没有更好的方法呢?
发布于 2016-08-22 14:01:29
免责声明:我是包含Copyable Plugin的JAXB2-Basics的作者,我认为它非常适合这项任务。
你可能会对Copyable Plugin感兴趣,它生成了无反射的策略复制方法。
在Maven中激活(另请参阅Using JAXB2 Basics Plugins):
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<configuration>
<extension>true</extension>
<args>
<arg>-Xcopyable</arg>
</args>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics</artifactId>
</plugin>
</plugins>
</configuration>
</plugin>然后,该插件生成深度的、无反射的、基于策略的clone和copyTo方法(见下文)。这为您提供了非常高效的复制。您还可以“复制”到现有实例中,或者通过指定自己的策略来自定义复制的内容和方式。例如,您可能希望避免复制id字段或类似的内容。生成的代码也知道如何处理JAXBElement。
这是一种生成的代码:
public Object clone() {
return copyTo(createNewInstance());
}
public Object copyTo(Object target) {
final CopyStrategy2 strategy = JAXBCopyStrategy.INSTANCE;
return copyTo(null, target, strategy);
}
public Object copyTo(ObjectLocator locator, Object target, CopyStrategy2 strategy) {
final Object draftCopy = ((target == null)?createNewInstance():target);
if (draftCopy instanceof IssueJIIB35) {
final IssueJIIB35 copy = ((IssueJIIB35) draftCopy);
{
Boolean nameShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, this.isSetName());
if (nameShouldBeCopiedAndSet == Boolean.TRUE) {
String sourceName;
sourceName = this.getName();
String copyName = ((String) strategy.copy(LocatorUtils.property(locator, "name", sourceName), sourceName, this.isSetName()));
copy.setName(copyName);
} else {
if (nameShouldBeCopiedAndSet == Boolean.FALSE) {
copy.name = null;
}
}
}
// ...
}
return draftCopy;
}
public Object createNewInstance() {
return new IssueJIIB35();
}看起来可能有点奇怪/笨拙,但它考虑到了相当多的JAXB特性。
发布于 2016-08-23 08:51:30
事实证明,https://github.com/plutext/docx4j/pull/163中引入的docx4j代码在复制JAXBElement时遇到了问题,无论是使用MOXy还是Sun/Oracle实现。
https://github.com/plutext/docx4j/commit/b5d8b4722e814945e502da9f0516d59c498b64bb解决了这个问题
https://stackoverflow.com/questions/39069943
复制相似问题