PeakGen
PeakGen

Reputation: 23035

Docx4J is not replacing placeholders

I am trying to replace a simple 'Place Holderin MS Word 2007 usingDocx4J. I created the placeholder byReference > Insert Citation > Add New Placeholder`

Below is my Docx4J code

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBElement;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.Text;

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Yohan
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        new Main();
    }
    public Main()
    {
        try
        {
            WordprocessingMLPackage template = getTemplate("C:/Users/Yohan/Desktop/Yohan.docx");
            replacePlaceholder(template,"Plane","Placeholder1");
            writeDocxToStream(template,"C:/Users/Yohan/Desktop/Yohan2.docx");

            System.out.println("Operation Completed");
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }



    private WordprocessingMLPackage getTemplate(String name) throws Docx4JException, FileNotFoundException {
        WordprocessingMLPackage template = WordprocessingMLPackage.load(new FileInputStream(new File(name)));
        return template;
    }

        private static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
        List<Object> result = new ArrayList<Object>();
        if (obj instanceof JAXBElement) obj = ((JAXBElement<?>) obj).getValue();

        if (obj.getClass().equals(toSearch))
            result.add(obj);
        else if (obj instanceof ContentAccessor) {
            List<?> children = ((ContentAccessor) obj).getContent();
            for (Object child : children) {
                result.addAll(getAllElementFromObject(child, toSearch));
            }

        }
        return result;
    }

        private void replacePlaceholder(WordprocessingMLPackage template, String name, String placeholder ) {
        List<Object> texts = getAllElementFromObject(template.getMainDocumentPart(), Text.class);

        for (Object text : texts) {
            Text textElement = (Text) text;
            if (textElement.getValue().equals(placeholder)) {
                textElement.setValue(name);
            }
        }
    }



    private void writeDocxToStream(WordprocessingMLPackage template, String target) throws IOException, Docx4JException {
        File f = new File(target);
        template.save(f);
    }

}

This generated the below in Netbeans console.

[main] INFO org.docx4j.jaxb.Context - java.vendor=Oracle Corporation
[main] INFO org.docx4j.jaxb.Context - java.version=1.8.0_05
[main] INFO org.docx4j.jaxb.Context - No MOXy JAXB config found; assume not intended..
[main] INFO org.docx4j.jaxb.NamespacePrefixMapperUtils - Using NamespacePrefixMapperSunInternal, which is suitable for Java 6
[main] INFO org.docx4j.jaxb.Context - Using Java 6/7 JAXB implementation
[main] INFO org.docx4j.jaxb.Context - Not using MOXy; using com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl
[main] WARN org.docx4j.utils.ResourceUtils - Couldn't get resource: docx4j.properties
[main] WARN org.docx4j.Docx4jProperties - Couldn't find/read docx4j.properties; docx4j.properties not found via classloader.
[main] INFO org.docx4j.XmlUtils - Using com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
[main] INFO org.docx4j.XmlUtils - Using com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
[main] INFO org.docx4j.openpackaging.contenttype.ContentTypeManager - Detected WordProcessingML package 
[main] INFO org.docx4j.openpackaging.io3.Load3 - Instantiated package of type org.docx4j.openpackaging.packages.WordprocessingMLPackage
[main] INFO org.docx4j.utils.XPathFactoryUtil - xpath implementation: org.apache.xpath.jaxp.XPathFactoryImpl
[main] INFO org.docx4j.openpackaging.io.Load - Found a CustomXmlPart, named /customXml/item1.xml
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - Lazily unmarshalling /customXml/itemProps1.xml
[main] INFO org.docx4j.openpackaging.io.Load - Identified/registered ds:itemId {2da45ade-e2fe-4251-9c73-a623aa57ebb0}
[main] INFO org.docx4j.openpackaging.io3.Load3 - package read;  elapsed time: 10932 ms
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - Lazily unmarshalling /word/document.xml
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPartXPathAware - For org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart, unmarshall via binder
[main] INFO org.docx4j.openpackaging.contenttype.ContentTypeManager - marshalling org.docx4j.openpackaging.contenttype.ContentTypeManager ...
[main] INFO org.docx4j.jaxb.NamespacePrefixMapperUtils - Using NamespacePrefixMapperSunInternal, which is suitable for Java 6
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - marshalling org.docx4j.openpackaging.parts.relationships.RelationshipsPart
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - marshalling org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - marshalling org.docx4j.openpackaging.parts.relationships.RelationshipsPart
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - marshalling org.docx4j.openpackaging.parts.WordprocessingML.BibliographyPart
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - marshalling org.docx4j.openpackaging.parts.relationships.RelationshipsPart
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - marshalling org.docx4j.openpackaging.parts.CustomXmlDataStoragePropertiesPart
[main] INFO org.docx4j.openpackaging.io3.Save - ...Done!
Operation Completed

However in Yohan2.docx, nothing is changed, it is a pure copy of Yohan.docx. What have I done wrong here?

UPDATE

I tested the MERGEFIELD example from the GITHUB but it also didn't work. Below is the output printed in console

[main] INFO org.docx4j.jaxb.Context - java.vendor=Oracle Corporation
[main] INFO org.docx4j.jaxb.Context - java.version=1.8.0_05
[main] INFO org.docx4j.jaxb.Context - No MOXy JAXB config found; assume not intended..
[main] INFO org.docx4j.jaxb.NamespacePrefixMapperUtils - Using NamespacePrefixMapperSunInternal, which is suitable for Java 6
[main] INFO org.docx4j.jaxb.Context - Using Java 6/7 JAXB implementation
[main] INFO org.docx4j.jaxb.Context - Not using MOXy; using com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl
[main] WARN org.docx4j.utils.ResourceUtils - Couldn't get resource: docx4j.properties
[main] WARN org.docx4j.Docx4jProperties - Couldn't find/read docx4j.properties; docx4j.properties not found via classloader.
[main] INFO org.docx4j.XmlUtils - Using com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
[main] INFO org.docx4j.XmlUtils - Using com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
[main] INFO org.docx4j.openpackaging.contenttype.ContentTypeManager - Detected WordProcessingML package 
[main] INFO org.docx4j.openpackaging.io3.Load3 - Instantiated package of type org.docx4j.openpackaging.packages.WordprocessingMLPackage
[main] INFO org.docx4j.utils.XPathFactoryUtil - xpath implementation: org.apache.xpath.jaxp.XPathFactoryImpl
[main] WARN org.docx4j.openpackaging.contenttype.ContentTypeManager - DefaultPart used for part '/word/stylesWithEffects.xml' of content type 'application/vnd.ms-word.stylesWithEffects+xml'
[main] INFO org.docx4j.openpackaging.io3.Load3 - package read;  elapsed time: 14766 ms
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - Lazily unmarshalling /word/document.xml
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPartXPathAware - For org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart, unmarshall via binder
[main] INFO org.docx4j.model.fields.merge.MailMerger - Found 9 fields 
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'kundenname'
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'kundenname'
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'kundenname'
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'Kundenname'
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'KunDenName'
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'KUNDENNAME'
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'Kundenstrasse'
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'yourdate'
[main] INFO org.docx4j.model.fields.DateFormatInferencer - Infering dates based using International formats
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'yournumber'
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - Lazily unmarshalling /word/settings.xml
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPartXPathAware - For org.docx4j.openpackaging.parts.WordprocessingML.DocumentSettingsPart, unmarshall via binder
[main] INFO org.docx4j.openpackaging.contenttype.ContentTypeManager - marshalling org.docx4j.openpackaging.contenttype.ContentTypeManager ...
[main] INFO org.docx4j.jaxb.NamespacePrefixMapperUtils - Using NamespacePrefixMapperSunInternal, which is suitable for Java 6
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - marshalling org.docx4j.openpackaging.parts.relationships.RelationshipsPart
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - marshalling org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - marshalling org.docx4j.openpackaging.parts.relationships.RelationshipsPart
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - marshalling org.docx4j.openpackaging.parts.WordprocessingML.DocumentSettingsPart
[main] WARN org.docx4j.openpackaging.parts.WordprocessingML.DocumentSettingsPart - 
[main] INFO org.docx4j.openpackaging.io3.Save - ...Done!
[main] INFO org.docx4j.model.fields.merge.MailMerger - Found 9 fields 
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'kundenname'
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'kundenname'
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'kundenname'
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'Kundenname'
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'KunDenName'
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'KUNDENNAME'
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'Kundenstrasse'
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'yourdate'
[main] INFO org.docx4j.model.fields.merge.MailMerger - Key: 'yournumber'
[main] INFO org.docx4j.openpackaging.contenttype.ContentTypeManager - marshalling org.docx4j.openpackaging.contenttype.ContentTypeManager ...
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - marshalling org.docx4j.openpackaging.parts.relationships.RelationshipsPart
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - marshalling org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - marshalling org.docx4j.openpackaging.parts.relationships.RelationshipsPart
[main] INFO org.docx4j.openpackaging.parts.JaxbXmlPart - marshalling org.docx4j.openpackaging.parts.WordprocessingML.DocumentSettingsPart
[main] WARN org.docx4j.openpackaging.parts.WordprocessingML.DocumentSettingsPart - 
[main] INFO org.docx4j.openpackaging.io3.Save - ...Done!

Upvotes: 1

Views: 3359

Answers (2)

ozogla
ozogla

Reputation: 1

Place Holder in Insert > Quick Parts > Field

Select MergeField

Copy merge field name as appears in the document together with the arrow characters to your code. replacePlaceholder(template,"Plane","placeholdernamewitharrowcharacters");

Upvotes: 0

JasonPlutext
JasonPlutext

Reputation: 15878

A placeholder inserted via Reference > Insert Citation > Add New Placeholder results in something like:

  <w:sdt>
    <w:sdtPr>
      <w:id w:val="-963732549"/>
      <w:citation/>
    </w:sdtPr>
    <w:sdtContent>
      <w:r>
        <w:fldChar w:fldCharType="begin"/>
      </w:r>
      <w:r>
        <w:rPr>
          <w:lang w:val="en-AU"/>
        </w:rPr>
        <w:instrText xml:space="preserve"> CITATION PL1 \l 3081 </w:instrText>
      </w:r>
      <w:r>
        <w:fldChar w:fldCharType="separate"/>
      </w:r>
      <w:r>
        <w:rPr>
          <w:noProof/>
          <w:lang w:val="en-AU"/>
        </w:rPr>
        <w:t>(PL1)</w:t>
      </w:r>
      <w:r>
        <w:fldChar w:fldCharType="end"/>
      </w:r>
    </w:sdtContent>
  </w:sdt>

ie a CITATION field wrapped in a content control.

docx4j has an API for replacing MERGEFIELD and DOCPROPERTY fields, but not CITATION fields.

Is there a particular reason you are trying to use a CITATION field?

If you can swap to MERGEFIELD or DOCPROPERTY field, I'd do that. Otherwise, you could examine docx4j's code for handling those, and adapt it to handling CITATION.

Upvotes: 1

Related Questions