首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >MOXy:高效的JAXBElement深度拷贝

MOXy:高效的JAXBElement深度拷贝
EN

Stack Overflow用户
提问于 2016-08-22 08:14:35
回答 2查看 601关注 0票数 0

JAXBV3.3.0使用以下代码克隆docx4j对象:

代码语言:javascript
复制
    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,例如:

代码语言:javascript
复制
public void testIssue212() {

    CTBookmark bookmark = Context.getWmlObjectFactory().createCTBookmark();
    JAXBElement<CTBookmark> el =Context.getWmlObjectFactory().createBodyBookmarkStart(bookmark);

    Object o = XmlUtils.deepCopy(el);
}

结果如下:

代码语言:javascript
复制
    [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

我们可以使用以下内容来解决此问题:

代码语言:javascript
复制
        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);

        }

但是有没有更好的方法呢?

EN

回答 2

Stack Overflow用户

发布于 2016-08-22 14:01:29

免责声明:我是包含Copyable PluginJAXB2-Basics的作者,我认为它非常适合这项任务。

你可能会对Copyable Plugin感兴趣,它生成了无反射的策略复制方法。

在Maven中激活(另请参阅Using JAXB2 Basics Plugins):

代码语言:javascript
复制
        <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>

然后,该插件生成深度的、无反射的、基于策略的clonecopyTo方法(见下文)。这为您提供了非常高效的复制。您还可以“复制”到现有实例中,或者通过指定自己的策略来自定义复制的内容和方式。例如,您可能希望避免复制id字段或类似的内容。生成的代码也知道如何处理JAXBElement

这是一种生成的代码:

代码语言:javascript
复制
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特性。

票数 1
EN

Stack Overflow用户

发布于 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解决了这个问题

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39069943

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档