2016-08-22 31 views
0

docx4j 3.3.0高效深拷贝使用下面的代码来克隆一个JAXB对象:莫西:JAXBElement的

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); 
    } 
} 

随着莫西V2.5.2(我们使用,因为它支持Java 6)和最新的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); 

     } 

但有没有更好的办法?

回答

0

免责声明:我是JAXB2-Basics的作者,其中包括Copyable Plugin'我认为这适合任务很好。

你可能感兴趣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> 

插件然后产生深,反射和无基于策略的clonecopyTo的方法(见下文)。这给你非常有效的复制。您也可以“复制”到现有实例,或通过指定自己的策略来自定义应该复制的内容和方式。例如,你可能想避免复制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特点考虑在内。

+0

感谢您的支持。 iirc,我过去一直试图一次使用多个扩展。 docx4j使用父指针插件 – JasonPlutext