Search code examples
docx4j

How programmatically bind repeater in Table docx4j?


I know about OpenDOPE. When I create paragraph wrapped with repeater tag programmatically, it's ok. When I try to create repeater tr wrapped with table, it throws

org.docx4j.openpackaging.exceptions.Docx4JException: Problems applying bindings

at org.docx4j.model.datastorage.BindingTraverserXSLT.traverseToBind(BindingTraverserXSLT.java:237)
at org.docx4j.model.datastorage.BindingHandler.applyBindings(BindingHandler.java:292)
at org.docx4j.model.datastorage.BindingHandler.applyBindings(BindingHandler.java:216)
at org.docx4j.Docx4J.bind(Docx4J.java:554)

But when I save the document and then load with Docx4J.load() and then bind that works fine.

EDIT Problem occurs in XMLUtils.deepCopy sdtContent of tr becomes empty

/*
         * Losing content here?
         * 
         * First, make absolutely sure that what you have is valid.
         * 

Results of XmlUtils.marshaltoString(tbl);

<w:tbl xmlns:dsp="http://schemas.microsoft.com/office/drawing/2008/diagram" xmlns:cppr="http://schemas.microsoft.com/office/2006/coverPageProps" xmlns:odx="http://opendope.org/xpaths" xmlns:c14="http://schemas.microsoft.com/office/drawing/2007/8/2/chart" xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:odgm="http://opendope.org/SmartArt/DataHierarchy" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:dgm="http://schemas.openxmlformats.org/drawingml/2006/diagram" xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture" xmlns:we="http://schemas.microsoft.com/office/webextensions/webextension/2010/11" xmlns:pvml="urn:schemas-microsoft-com:office:powerpoint" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:sl="http://schemas.openxmlformats.org/schemaLibrary/2006/main" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:comp="http://schemas.openxmlformats.org/drawingml/2006/compatibility" xmlns:b="http://schemas.openxmlformats.org/officeDocument/2006/bibliography" xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:xvml="urn:schemas-microsoft-com:office:excel" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:oda="http://opendope.org/answers" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:odc="http://opendope.org/conditions" xmlns:cdr="http://schemas.openxmlformats.org/drawingml/2006/chartDrawing" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:odi="http://opendope.org/components" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:lc="http://schemas.openxmlformats.org/drawingml/2006/lockedCanvas" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:odq="http://opendope.org/questions" xmlns:wetp="http://schemas.microsoft.com/office/webextensions/taskpanes/2010/11" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid">
<w:tblPr>
    <w:tblStyle w:val="TableGrid"/>
    <w:tblW w:w="0" w:type="auto"/>
    <w:tblLook w:val="04A0"/>
</w:tblPr>
<w:tblGrid>
    <w:gridCol w:w="100"/>
</w:tblGrid>
<w:tr>
    <w:tc>
        <w:tcPr>
            <w:tcW w:w="100" w:type="dxa"/>
        </w:tcPr>
        <w:p/>
    </w:tc>
</w:tr>
<w:sdt>
    <w:sdtPr>
        <w:alias w:val="Repeat"/>
        <w:tag w:val="od:repeat=x1"/>
        <w:id w:val="2058423626"/>
    </w:sdtPr>
    <w:sdtContent>
        <w:tr>
            <w:sdt>
                <w:sdtPr>
                    <w:alias w:val=""/>
                    <w:tag w:val="od:xpath=x0"/>
                    <w:id w:val="947420646"/>
                    <w:dataBinding w:xpath="/invoice[1]/items/item[1]/name" w:storeItemID="{bb8f553d-dc92-4c50-b1cb-06e77bbe149a}"/>
                </w:sdtPr>
                <w:sdtContent>
                    <w:tc>
                        <w:p/>
                    </w:tc>
                </w:sdtContent>
            </w:sdt>
        </w:tr>
    </w:sdtContent>
</w:sdt>


Solution

  • In JAXB, there are different Java objects for the content controls, depending on whether they wrap block-level, row-level, cell-level, or run-level content. Those objects all implement interface SdtElement.

    Similarly, there are different Java objects for the SdtContent, depending on whether they wrap block-level, row-level, cell-level, or run-level content. Those objects all extend SdtContent.

    You have to use the correct objects, or as you have noticed, deepCopy won't work.

    I've modified your code to use SdtElement and SdtContent

    public class ContentControlFactory {
        ObjectFactory wmlObjectFactory = getWmlObjectFactory();
    
        public SdtPr createSdtPr(CTDataBinding dataBinding, String tag, String alias) {
            SdtPr sdtPr = createSdtPr(tag, alias);
            sdtPr.setDataBinding(dataBinding);
    
            return sdtPr;
        }
    
        public SdtPr createSdtPr(String tag, String alias) {
            Tag t = new Tag();
    
            SdtPr.Alias a = new SdtPr.Alias();
            a.setVal(alias);
            t.setVal(tag);
            SdtPr sdtPr = wmlObjectFactory.createSdtPr();
            sdtPr.getRPrOrAliasOrLock().add(a);
            sdtPr.setTag(t);
    
            sdtPr.setId();
            return sdtPr;
        }
    
        public SdtElement createRepeaterControl(Xpaths.Xpath repeaterXpath, Xpaths.Xpath xpath, Object template) {
    
            return createSdt(
                    createSdtPr("od:repeat=" + repeaterXpath.getId(), "Repeat"),
                    createSdtContent(createContentControl(xpath, template))
            );
        }
    
        public SdtElement createRepeaterControl(Xpaths.Xpath repeaterXpath, Object template) {
            return createSdt(
                    createSdtPr("od:repeat=" + repeaterXpath.getId(), "Repeat"),
                    createSdtContent(template)
            );
        }
    
        public SdtElement createContentControl(Xpaths.Xpath xpath, Object template) {
            return createSdt(
                    createSdtPr(
                            createDataBinding(xpath.getDataBinding().getXpath(), xpath.getDataBinding().getStoreItemID()),
                            "od:xpath=" + xpath.getId(), ""),
                    createSdtContent(template));
        }
    
        public SdtContent createSdtContent(Object template) {
    
            System.out.println(template.getClass().getName() );
            SdtContent sdtContent = null; 
            if (template instanceof Tr) {
                sdtContent = wmlObjectFactory.createCTSdtContentRow();
            } else {
                sdtContent = wmlObjectFactory.createSdtContentBlock();
    
            }
    
            sdtContent.getContent().add(template);
            return sdtContent;
        }
    
        public SdtElement createConditionControl(Condition condition, Object template) {
            return createSdt(
                    createSdtPr("od:condition=" + condition.getId(), ""),
                    createSdtContent(template)
            );
        }
    
        public SdtElement createSdt(SdtPr sdtPr, SdtContent sdtContent) {
            SdtElement sdtElement = null;
    
            if (sdtContent instanceof CTSdtContentRow) {
                sdtElement = wmlObjectFactory.createCTSdtRow();
            } else {
                sdtElement = wmlObjectFactory.createSdtBlock();
    
            }
    
            sdtElement.setSdtPr(sdtPr);
            sdtElement.setSdtContent(sdtContent);
    
            return sdtElement;
        }
    
        public CTDataBinding createDataBinding(String xPath, String storeItemID) {
            CTDataBinding dataBinding = wmlObjectFactory.createCTDataBinding();
            dataBinding.setXpath(xPath);//xPath - это строка с XPath до XML-элемента, связанного с этим Content Control
            dataBinding.setStoreItemID(storeItemID);//storeItemID - это ID корневого XML-элемента, из которого нужно брать данные
            return dataBinding;
        }
    
    }
    

    and fixed the tr case. You'll need to handle the other cases properly.