Pergunta

I spent the day searching for a solution to this error and none of the other topics really came close to addressing the same problem, let alone solving it. So, I apologize in advance if this is a duplicate question.

We have an application that allows users to save images to and retrieve images from our back-end data store. We also allow them to include metadata (called annotations) with the images. In the case of TIFF images, when the user retrieves an image, we also retrieve the annotations (in XML). We use Apache FOP to format the text into a table which is then rendered as a PNG file and displayed with the TIFF.

This was working until recently. I'm now seeing the following error when I make the call to transform the XML into the PNG file:

SXCH0003: org.apache.fop.fo.ValidationException: "fo:table-body" is missing child elements. Required content model: marker <XSL file:line#> "fo:table-body" is missing child elements. Required content model: marker* (table-row+|table-cell+) (See position <line #>)

At first we thought that this was a data issue, caused by empty elements/attributes within the source XML. I added code that would force placeholder values into the XML in any places where it was missing. When that did not solve the issue, it made be believe that the XSL (which I did not write) was likely the source of the problem.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" 
   xmlns:fo="http://www.w3.org/1999/XSL/Format" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:myns="http://xmlapp.fm.net/ns/app/service">

   <xsl:template match="/">
      <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
          <fo:layout-master-set>
            <fo:simple-page-master master-name="main">
               <fo:region-body margin-top="1cm" margin-bottom="1cm" />
               <fo:region-before extent="0.5cm" />
               <fo:region-after region-name="xsl-region-after" display-align="before" extent="2.0cm"/>
            </fo:simple-page-master>
         </fo:layout-master-set>

          <fo:page-sequence master-reference="main">
            <fo:static-content flow-name="xsl-region-before">
               <fo:block>Page title text</fo:block>
            </fo:static-content>
            <fo:static-content flow-name="xsl-region-after" >
               <fo:block-container left="500pt" top="28pt" width="220mm" position="absolute">
                  <fo:block font-size="16"><xsl:value-of select="//myns:cust" /> : <xsl:value-of select="//myns:docId" /></fo:block>
               </fo:block-container>
            </fo:static-content>
            <fo:flow flow-name="xsl-region-body" >
               <xsl:call-template name="annotationsTable" />
            </fo:flow>
         </fo:page-sequence>
       </fo:root>
   </xsl:template>

   <xsl:template name="annotationsTable">
      <fo:table table-layout="fixed">
         <fo:table-header>
            <fo:table-row>
               <fo:table-cell><fo:block> Annotation </fo:block></fo:table-cell>
               <fo:table-cell><fo:block> UserName </fo:block></fo:table-cell>
               <fo:table-cell><fo:block> UserID </fo:block></fo:table-cell>
               <fo:table-cell><fo:block> Date </fo:block></fo:table-cell>
            </fo:table-row>
         </fo:table-header>
         <fo:table-body>
            <xsl:apply-templates select="//myns:annotation" />
         </fo:table-body>
      </fo:table>
   </xsl:template>

    <xsl:template match="myns:annotation">
      <fo:table-row>
         <fo:table-cell>
            <fo:block>
               <xsl:value-of select="./myns:text" />
            </fo:block>
         </fo:table-cell>
         <fo:table-cell>
            <fo:block>
               <xsl:value-of select="./@createdByName"/>
            </fo:block>
         </fo:table-cell>
         <fo:table-cell>
            <fo:block>
               <xsl:value-of select="./@createdByUserID" />
            </fo:block>
         </fo:table-cell>
         <fo:table-cell>
            <fo:block>
               <xsl:value-of select="./@systemDate" />
            </fo:block>
         </fo:table-cell>
      </fo:table-row>
   </xsl:template>

</xsl:stylesheet>

The format of the XML that we're trying to transform is:

<annotation createdByName="Frank" createdByUserID="X1234" systemDate="2014-04-14">
   <text>this is the text of an annotation</text>
</annotation>

I know that I'm doing something wrong. The problem is that I don't know enough about XSL to know what. Can someone advise?

Thanks.

Foi útil?

Solução

Your code wasn't working because your source in no-namespace isn't being selected by XPath expressions trying to match elements the http://xmlapp.fm.net/ns/app/service namespace. The solution is either declare that namespace as default in your source (adding a xmlns attribute) or fix the expressions in your XSLT so they match no-namespace.

1) To fix it changing the XSLT remove the myns: prefixes from your XPath elements.

From the myns:annotation in the annotationsTable template:

<xsl:template name="annotationsTable">
    ...
        <fo:table-body>
            <xsl:apply-templates select="annotation" />
        </fo:table-body>
    </fo:table>
</xsl:template>

and from myns:annotation and myns:text elements in the myns:annotation template:

<xsl:template match="annotation">
    <fo:table-row>
        <fo:table-cell>
            <fo:block>
                <xsl:value-of select="text" />
            </fo:block>
        </fo:table-cell>
        <fo:table-cell>
            <fo:block>
                <xsl:value-of select="@createdByName"/>
            </fo:block>
        </fo:table-cell>
        <fo:table-cell>
            <fo:block>
                <xsl:value-of select="@createdByUserID" />
            </fo:block>
        </fo:table-cell>
        <fo:table-cell>
            <fo:block>
                <xsl:value-of select="@systemDate" />
            </fo:block>
        </fo:table-cell>
    </fo:table-row>
</xsl:template>

Now it should generate the table body contents.

2) If the namespace prefix was there in the first place, it might be that your source originally had a namespace. In this case you leave your XSLT as is, and edit your source, declaring a default namespace:

<annotation xmlns="http://xmlapp.fm.net/ns/app/service" 
            createdByName="Frank" createdByUserID="X1234" systemDate="2014-04-14">
    <text>this is the text of an annotation</text>
</annotation>

Now all elements belong to the "http://xmlapp.fm.net/ns/app/service" namespace, and your XSLT maps that namespace to a prefix (myns) so you can select the nodes of the source tree in XPath.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top