Page 1 of 1

Replace of xml-data in docx-Document

PostPosted: Thu Mar 27, 2014 2:47 am
by nicky
Hi,

first of all: docx4j is great, works really fine for me :)

But i have a problem:

I have an docx-file and set xml-data into it to bind it(= xpath) by using the opendope Plugin. After some time the data-xml changes (for example new data-fields have been added) and i don't want to create a new document and bind all fields again. I'd like to use the document, where i already have bind my fields to the xml-data and just want to exchange the xml-data. After exchanging the datafile i just want tobind the added field and the new field is supported by the document. But this doesn't work at the moment, here's my source:

Code: Select all
public byte[] enrichDocxWithXML(byte[] docx, DocumentType documentType) throws DocumentTemplateException {
   try {
      ByteArrayInputStream documentTemplateInputStream = new ByteArrayInputStream(docx);
      WordprocessingMLPackage wordMLPackage = Docx4J.load(documentTemplateInputStream);

      CustomXmlDataStoragePart customXmlDataStoragePart = __injectCustomXmlDataStoragePart(wordMLPackage.getMainDocumentPart(),
      this.documentDataXMLConverterService.getXML(documentType));

      __addProperties(customXmlDataStoragePart);

      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

      Docx4J.save(wordMLPackage, outputStream, Docx4J.FLAG_NONE);

      return outputStream.toByteArray();
      } catch (Docx4JException ex) {
         logger.error("Error at enriching document with xml file.", ex);
      }
   }

private void __addProperties(CustomXmlDataStoragePart customXmlDataStoragePart) throws InvalidFormatException {
   CustomXmlDataStoragePropertiesPart part = new CustomXmlDataStoragePropertiesPart();
   ObjectFactory of = new ObjectFactory();
   DatastoreItem dsi = of.createDatastoreItem();
   String newItemId = "{MyData}";
   dsi.setItemID(newItemId);
   part.setJaxbElement(dsi);
   customXmlDataStoragePart.addTargetPart(part);
}

private CustomXmlDataStoragePart __injectCustomXmlDataStoragePart(MainDocumentPart parent, byte[] xmlData) throws Docx4JException {
   CustomXmlDataStoragePart customXmlDataStoragePart = new CustomXmlDataStoragePart();
   CustomXmlDataStorage data = new CustomXmlDataStorageImpl();
   data.setDocument(new ByteArrayInputStream(xmlData));
   customXmlDataStoragePart.setData(data);
   parent.addTargetPart(customXmlDataStoragePart, AddPartBehaviour.RENAME_IF_NAME_EXISTS);
   return customXmlDataStoragePart;
}



Sometimes it works to replace it but most of the time not. I tried to use the different AddPartBehaviour but none of them worked like i want. And if i try to enrich and document, which has already been enriched by this method, i can be written but cannot be opened by word without error.

Any idea what's wrong?

Thanks in advance.

BR
Nicky

Re: Replace of xml-data in docx-Document

PostPosted: Thu Mar 27, 2014 4:01 am
by nicky
Something like this seems to work, but MS Word still says that the document is corrupt (i have to save it again, then it seems to work => further tests needed):

Code: Select all
private void __stripCustomXmlDataStorageParts(WordprocessingMLPackage wordMLPackage,
         RelationshipsPart rp){
      List<Relationship> deletions = new ArrayList<Relationship>();
      for ( Relationship r : rp.getRelationships().getRelationship() ) {
         
         Part part = rp.getPart(r);
         if (part instanceof CustomXmlDataStoragePart){
            deletions.add(r );
         }
         if (part.getRelationshipsPart()!=null) {
            __stripCustomXmlDataStorageParts(wordMLPackage, part.getRelationshipsPart());                  
         }
      }
      for ( Relationship r : deletions) {
         rp.removeRelationship(r);
      }
   }

Re: Replace of xml-data in docx-Document

PostPosted: Thu Mar 27, 2014 3:17 pm
by jason
You ought to be able to use the docx4j facade:

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
                // FLAG_BIND_INSERT_XML: inject the passed XML into the document
                // FLAG_BIND_BIND_XML: bind the document and the xml (including any OpenDope handling)
                // FLAG_BIND_REMOVE_SDT: remove the content controls from the document (only the content remains)
                // FLAG_BIND_REMOVE_XML: remove the custom xml parts from the document                 
                Docx4J.bind(wordMLPackage, xmlStream, Docx4J.FLAG_BIND_INSERT_XML & Docx4J.FLAG_BIND_BIND_XML);
 
Parsed in 0.014 seconds, using GeSHi 1.0.8.4


Alternatively, see line 203 of https://github.com/plutext/OpenDoPE-WAR ... tBoth.java

Re: Replace of xml-data in docx-Document

PostPosted: Tue Apr 01, 2014 11:15 pm
by nicky
Your example didn't work but gave me the right idea... this is now part of my source and works fine. Thank you very much.

Code: Select all
CustomXmlDataStoragePart customXmlDataStoragePart = null;
         
for (String key : wordMLPackage.getCustomXmlDataStorageParts().keySet()){
   CustomXmlPart xmlPart = wordMLPackage.getCustomXmlDataStorageParts().get(key);
   if (xmlPart instanceof CustomXmlDataStoragePart){
      customXmlDataStoragePart = (CustomXmlDataStoragePart)xmlPart;
      break;
   }
}
byte[] xmlBytes = ...

customXmlDataStoragePart.getData().setDocument(new ByteArrayInputStream(xmlBytes));

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Docx4J.bind(wordMLPackage, xmlStream, Docx4J.FLAG_BIND_INSERT_XML & Docx4J.FLAG_BIND_BIND_XML);