Page 1 of 1

How to bind custom xml to docx with nested repeats

PostPosted: Tue Mar 15, 2011 5:51 am
by TopaZ
Hi,
I need to create a docx document from my object model programmatically. Document should contain a lot of similar sections, subsections and items.
To do this I can marshall all object model to xml and than as I understand I can populate docx from my custom xml using some rules of repeats and conditions.
What main steps should I do to achieve that? I've tries some examples from samples package, but most connected to custom xml binding do not work as I expect (for example generated invoice.docx doesn't show all invoice items, just first one).
It would be great if it is possible to define some docx template with styles, fonts I want and populate data basing on this template. Is it possible from docx4j?
Thanks in advance
Anton

Re: How to bind custom xml to docx with nested repeats

PostPosted: Tue Mar 15, 2011 9:54 am
by TopaZ
I have xml structure like this:
Code: Select all
<?xml version="1.0" encoding="UTF-8"?>
<sections>
    <section name="Name of the first section" id="1" ordinal="1" description="Description of the first section.">
        <subsection name="Name of first subsection" id="2" ordinal="1" description="Description of the first subsection.">
            <feature name="First feature name" id="3" ordinal="1" description="First feature description.">
                <parameter name="parameterName1" value="parameterValue1" type="type1"/>
                <parameter name="parameterName2" value="parameterValue2" type="type2"/>
            </feature>

            <feature name="Second feature name" id="4" ordinal="2" description="Second feature description.">
                <parameter name="parameterName1" value="parameterValue1" type="type1"/>
                <parameter name="parameterName2" value="parameterValue2" type="type2"/>
            </feature>

        </subsection>

        <subsection name="Name of second subsection" id="5" ordinal="2" description="Description of the second subsection.">
            <feature ...>
                ...
            </feature>

            <feature ...>
                ...
            </feature>

        </subsection>

    </section>
    <section>
        ...
    </section>
</sections>

The number of sections, subsections, features and parameters is defined in runtime and can be changed dynamically while application is running.
I have to transform this xml (or object structure) to word and optionaly to pdf.
The docx result should look like in docx attached. Additional requirement is to have an ability to change styles, fonts, colors, headings and footers for resulting docx without changes to java code.
How to achieve this task with docx4j?

Re: How to bind custom xml to docx with nested repeats

PostPosted: Tue Mar 15, 2011 9:58 pm
by jason
Custom XML data binding should work nicely for your purposes.

Another alternative may be to use XSLT to transform from your XML structure, to "Flat OPC XML" which Word can read.

TopaZ wrote:change styles, fonts, colors, headings and footers for resulting docx without changes to java code


One way to do this would be to represent what you want in your XML (eg a style or a color), and have docx4j interpret this and give effect to it.

You'll need to provide more details on exactly what you are trying to achieve formatting wise if you want more considered advice :-)

Re: How to bind custom xml to docx with nested repeats

PostPosted: Wed Mar 16, 2011 3:48 am
by TopaZ
The best way I see to make styled customizations is to create template (for example dotx or docx) with all styles, colors, headers and footers.
Then generate resulting docx document from this template using data from custom xml (I mean, that styles and other is static and could be changed for different instances of application by replace of template dotx). I think it would be the best way to customize look and feel of the resulting document without changes to java code.
I've tried some examples connected to custom xml binding from samples package, but unfortunately they don't work as I expect. So I think I do something wrong. I'm using version 2.6.0 from maven repo.

Re: How to bind custom xml to docx with nested repeats

PostPosted: Mon Mar 21, 2011 7:50 am
by TopaZ
Playing with examples, I've increased my understanding of the custom xml binding and repeats.
Now I'm able to get a list of items in invoice.docx - just need to runOpenDoPEHandler.preprocess() on opened document.

So, back to my task. I've created .dotx template with nested repeats and now I'm trying to add custom xml to the document and preprocess it.
But I have NPE when I call preprocess():
Code: Select all
java.lang.NullPointerException
   at org.docx4j.model.datastorage.OpenDoPEHandler.processDescendantBindings(OpenDoPEHandler.java:811)

Re: How to bind custom xml to docx with nested repeats

PostPosted: Mon Mar 21, 2011 8:21 am
by jason
TopaZ wrote:I've created .dotx template with nested repeats and now I'm trying to add custom xml to the document and preprocess it.
But I have NPE when I call preprocess():


If you could post your code and sample dotx, that'll make it easier to help.

Re: How to bind custom xml to docx with nested repeats

PostPosted: Mon Mar 21, 2011 8:56 am
by TopaZ
Here is my code:
Code: Select all
public class WordSpecificationExporter {

    private SpecificationExporter specificationExporter;
    private File wordTemplate;

    //@Override
    public void export(SpecificationBean specificationBean, OutputStream outputStream) {
        try {
            process(specificationBean, outputStream);
        } catch (Docx4JException e) {
            throw new RuntimeException(e);
        }
    }

    public void process(SpecificationBean specificationBean, OutputStream outputStream) throws Docx4JException {

        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(wordTemplate);

        injectCustomXmlDataStoragePart(wordMLPackage.getMainDocumentPart(), wordMLPackage.getParts(), specificationBean);

        OpenDoPEHandler.preprocess(wordMLPackage);

        // Now save it
        SaveToZipFile zipper = new SaveToZipFile(wordMLPackage);
        zipper.save(outputStream);

    }



    public void injectCustomXmlDataStoragePart(DocumentPart base, Parts parts, SpecificationBean specificationBean) {
        try {
            CustomXmlDataStoragePart customXmlDataStoragePart = new CustomXmlDataStoragePart(parts);

            CustomXmlDataStorage data = new CustomXmlDataStorageImpl();
            data.setDocument(createCustomXmlDocument(specificationBean));

            customXmlDataStoragePart.setData(data);

            //BindingHandler.applyBindings(base);

            base.addTargetPart(customXmlDataStoragePart);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }


    }

    public org.w3c.dom.Document createCustomXmlDocument(SpecificationBean specificationBean) {
        return specificationExporter.export(specificationBean);
    }

    @Required
    public void setSpecificationExporter(SpecificationExporter specificationExporter) {
        this.specificationExporter = specificationExporter;
    }

    @Required
    public void setWordTemplate(File wordTemplate) {
        this.wordTemplate = wordTemplate;
    }

}


dotx template is attached to this post.

Here is also stack trace of the exception:
Code: Select all
Caused by: java.lang.NullPointerException
   at org.docx4j.model.datastorage.OpenDoPEHandler.processDescendantBindings(OpenDoPEHandler.java:811)
   at org.docx4j.model.datastorage.OpenDoPEHandler.access$200(OpenDoPEHandler.java:54)
   at org.docx4j.model.datastorage.OpenDoPEHandler$DeepTraversor.apply(OpenDoPEHandler.java:744)
   at org.docx4j.model.datastorage.OpenDoPEHandler$DeepTraversor.walkJAXBElements(OpenDoPEHandler.java:764)
   at org.docx4j.model.datastorage.OpenDoPEHandler$DeepTraversor.walkJAXBElements(OpenDoPEHandler.java:767)
   at org.docx4j.model.datastorage.OpenDoPEHandler$DeepTraversor.walkJAXBElements(OpenDoPEHandler.java:767)
   at org.docx4j.model.datastorage.OpenDoPEHandler$DeepTraversor.walkJAXBElements(OpenDoPEHandler.java:767)
   at org.docx4j.TraversalUtil.<init>(TraversalUtil.java:118)
   at org.docx4j.model.datastorage.OpenDoPEHandler.processRepeat(OpenDoPEHandler.java:674)
   at org.docx4j.model.datastorage.OpenDoPEHandler.processBindingRoleIfAny(OpenDoPEHandler.java:596)
   at org.docx4j.model.datastorage.OpenDoPEHandler.access$100(OpenDoPEHandler.java:54)
   at org.docx4j.model.datastorage.OpenDoPEHandler$ShallowTraversor.apply(OpenDoPEHandler.java:432)
   at org.docx4j.model.datastorage.OpenDoPEHandler$ShallowTraversor.walkJAXBElements(OpenDoPEHandler.java:473)
   at org.docx4j.TraversalUtil.<init>(TraversalUtil.java:118)
   at org.docx4j.model.datastorage.OpenDoPEHandler.preprocessRun(OpenDoPEHandler.java:399)
   at org.docx4j.model.datastorage.OpenDoPEHandler.preprocess(OpenDoPEHandler.java:119)
   at com.topaz.rfs.services.spec.export.WordSpecificationExporter.process(WordSpecificationExporter.java:44)
   at com.topaz.rfs.services.spec.export.WordSpecificationExporter.export(WordSpecificationExporter.java:32)
   at com.topaz.rfs.pages.Specification.createDocx(Specification.java:68)
   at com.topaz.rfs.pages.Specification.access$000(Specification.java:23)
   at com.topaz.rfs.pages.Specification$1.getStream(Specification.java:54)

Re: How to bind custom xml to docx with nested repeats

PostPosted: Tue Mar 22, 2011 12:41 am
by jason
If you insert

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
if (tag==null) return;
 
Parsed in 0.014 seconds, using GeSHi 1.0.8.4


in the source code where the error is thrown, the problem will go away. Alternatively for now, you could add a w:tag (with arbitrary content) to the one sdt in your docx which is missing it.

With this change, the repeats expand. You don't currently have any standard databindings within the repeats, so the specific content of your XML part is not reflected in the resulting docx.