2016-08-22 3 views
0

docx4j v3.3.0 verwendet den folgenden Code ein JAXB Objekt zu klonen:moxy: Effiziente tiefe Kopie von 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); 
    } 
} 

Mit moxy v2.5.2 (die wir verwenden, da es Java unterstützt 6) und die neuesten 2.6.3, dem Versuch, einen JAXBElement zu klonen, zum Beispiel:

public void testIssue212() { 

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

    Object o = XmlUtils.deepCopy(el); 
} 

Ergebnisse in:

[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 

Wir können diesen Witz umgehen h etwas wie:

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

     } 

aber gibt es einen besseren Weg?

Antwort

0

Haftungsausschluss: Ich bin der Autor von JAXB2-Basics, der die Copyable Plugin 'enthält, die ich denke, die Aufgabe ziemlich gut passt.

Sie könnten interessiert sein Copyable Plugin, es generiert Reflektion-freie strategische Kopiermethoden.

Aktivierung in Maven (siehe auch 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> 

Das Plugin erzeugt dann tief, reflexionsfrei und strategiebasierten clone und copyTo Methoden (siehe unten). Dies ermöglicht Ihnen ein sehr effizientes Kopieren. Sie können auch auf eine vorhandene Instanz "kopieren" oder anpassen, was und wie kopiert werden soll, indem Sie Ihre eigene Strategie angeben. Zum Beispiel können Sie vermeiden, id Felder oder ähnliches zu kopieren. Generierter Code kann auch mit JAXBElement umgehen.

Dies ist eine Art von Code generiert:

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

Könnte ein bisschen komisch/umständlich aussehen, aber es dauert eine ganze Reihe von JAXB Besonderheiten Rechnung trägt.

+0

danke dafür. Iirc, ich hatte in der Vergangenheit Schwierigkeiten, mehrere Erweiterungen gleichzeitig zu verwenden. docx4j verwendet parent-pointer-plugin – JasonPlutext

Verwandte Themen