0

Here is my input XML:

<AllTitleByOrder>
  <CustProp name="Title of x CP" CPID='x'/>
  <CustProp name="Title of y CP" CPID='y'/>
  <CustProp name="Title of z CP" CPID='z'/>
</AllTitleByOrder>
<ResultSet>
  <ResultItem …>
    <Document ContentType="document" … >
      <CustomProperties>
         <CustPropItem CPID='x' value="value of x for doc"/>
      </CustomProperties>
    </Document>
    <Document ContentType="document" … >
      <CustomProperties>
         <CustPropItem CPID='x' value="value of x for doc"/>
         <CustPropItem CPID='y' value="value of y for doc"/>
         <CustPropItem CPID='z' value="value of z for doc"/>
      </CustomProperties>
    </Document>
  </ResultItem>
  <ResultItem …>
    <Document ContentType="research" … >
      <CustomProperties>
        <CustPropItem CPID='y' value="value of y for rsr"/>
      </CustomProperties>
    </Document>
  </ResultItem>
  <ResultItem>
    <Document ContentType="pleading" … >
      <CustomProperties>
        <CustPropItem CPID='z' value="value of z for pldg"/>
      </CustomProperties>
    </Document>
  </ResultItem>
</ResultSet>

I need the output to look like this:

Title of x CP ---------- Title of y CP ---------- Title of z CP
value of x for doc
value of x for doc       value of y for rsr       value of z for pldg
                         value of y for rsr
                                                  value of z for pldg

My trouble is filtering (output value of x for title X ONLY) since all content is under the same XML tag (namely Document).

I'm looping through //AllTitleByOrder/CustProp/@CPID but I'm not sure how to select the right value (corresponding to the right title) or just add white space.

7
  • You want text output? or html output? What version of XSLT? 1.0? 2.0? or 3.0? Commented Dec 1, 2015 at 6:03
  • In your sample, you have 3 output columns. Are the number of columns fixed at 3? or variable? Commented Dec 1, 2015 at 6:04
  • I need CSV output but I was looking for the concept of grabbing a node list and go loop over another list. Michael's solution is great and beyond my XSLT's abilities, I'm studying it. Can't just plug it in because of the existing code I have; I need to loop thru <CustomProperties> while processing <Document> for over values. Commented Dec 1, 2015 at 17:40
  • @Sqandr "I need CSV output" That should be a trivial adjustment. --- "I need to loop thru <CustomProperties> while processing <Document> for over values." I don't understand what you're saying. Commented Dec 1, 2015 at 18:32
  • the old code I'm dealing with, look like this: <xsl:if test="./Document"> <!-- deal with most Document's children, add values to csv's current row --> <!-- new code goes here --> </xsl:if> Commented Dec 1, 2015 at 19:13

2 Answers 2

3

Assuming that (a) you want an HTML table as the result, and (b) you have a well-formed XML document as the input, you could try something like:

XSLT 1.0

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:key name="item-by-rowXcol" match="CustPropItem" use="concat(@CPID, generate-id(../..))" />

<xsl:template match="/root">
    <xsl:variable name="columns" select="AllTitleByOrder/CustProp"/>
    <table border="1">
        <thead>
            <tr>
                <xsl:for-each select="$columns">
                    <th>
                        <xsl:value-of select="@name"/>
                    </th>
                </xsl:for-each>
            </tr>
        </thead>
        <tbody>
            <xsl:for-each select="ResultSet/ResultItem/Document">
                <xsl:variable name="row-id" select="generate-id()" />
                <tr>
                    <xsl:for-each select="$columns">
                        <td>
                            <xsl:value-of select="key('item-by-rowXcol', concat(@CPID, $row-id))/@value" />
                        </td>
                    </xsl:for-each>
                </tr>
            </xsl:for-each>
        </tbody>
    </table>
</xsl:template>

</xsl:stylesheet>

Applied to the following test input:

XML

<root>
  <AllTitleByOrder>
    <CustProp name="Title of x CP" CPID="x"/>
    <CustProp name="Title of y CP" CPID="y"/>
    <CustProp name="Title of z CP" CPID="z"/>
  </AllTitleByOrder>
  <ResultSet>
    <ResultItem>
      <Document ContentType="document">
        <CustomProperties>
          <CustPropItem CPID="x" value="value of x for doc"/>
        </CustomProperties>
      </Document>
      <Document ContentType="document">
        <CustomProperties>
          <CustPropItem CPID="x" value="value of x for doc"/>
          <CustPropItem CPID="y" value="value of y for doc"/>
          <CustPropItem CPID="z" value="value of z for doc"/>
        </CustomProperties>
      </Document>
    </ResultItem>
    <ResultItem>
      <Document ContentType="research">
        <CustomProperties>
          <CustPropItem CPID="y" value="value of y for rsr"/>
        </CustomProperties>
      </Document>
    </ResultItem>
    <ResultItem>
      <Document ContentType="pleading">
        <CustomProperties>
          <CustPropItem CPID="z" value="value of z for pldg"/>
        </CustomProperties>
      </Document>
    </ResultItem>
  </ResultSet>
</root>

the result (rendered) will be:

enter image description here

Sign up to request clarification or add additional context in comments.

Comments

0

This XSLT 2.0 stylesheet ...

<xsl:transform
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

<xsl:output method="text" encoding="UTF-8" />
<xsl:strip-space elements="*" />
<xsl:variable name="new-line" select="'&#x0A;&#x0D;'
   (: Adjust as required for your file-system. :)" />

<xsl:template match="/*">
  <!-- Do the header line. -->
  <xsl:value-of select="AllTitleByOrder/CustProp/@name" separator="," />
  <xsl:value-of select="$new-line" />

  <!-- Now the data rows. -->
  <xsl:apply-templates select="ResultSet/ResultItem/Document" />
</xsl:template>

<xsl:template match="Document">
  <xsl:value-of select="
    for $ID in ../../../AllTitleByOrder/CustProp/@CPID
      return concat( CustomProperties/CustPropItem[@CPID eq $ID]/@value, '')"
    separator="," />
  <xsl:value-of select="$new-line" />
</xsl:template>

</xsl:transform>

... when applied to this input document ...

<root>
<AllTitleByOrder>
  <CustProp name="Title of x CP" CPID='x'/>
  <CustProp name="Title of y CP" CPID='y'/>
  <CustProp name="Title of z CP" CPID='z'/>
</AllTitleByOrder>
<ResultSet>
  <ResultItem>
    <Document ContentType="document">
      <CustomProperties>
         <CustPropItem CPID='x' value="value of x for doc"/>
      </CustomProperties>
    </Document>
    <Document ContentType="document">
      <CustomProperties>
         <CustPropItem CPID='x' value="value of x for doc"/>
         <CustPropItem CPID='y' value="value of y for doc"/>
         <CustPropItem CPID='z' value="value of z for doc"/>
      </CustomProperties>
    </Document>
  </ResultItem>
  <ResultItem>
    <Document ContentType="research">
      <CustomProperties>
        <CustPropItem CPID='y' value="value of y for rsr"/>
      </CustomProperties>
    </Document>
  </ResultItem>
  <ResultItem>
    <Document ContentType="pleading">
      <CustomProperties>
        <CustPropItem CPID='z' value="value of z for pldg"/>
      </CustomProperties>
    </Document>
  </ResultItem>
</ResultSet>
</root>

... will yield this csv output document ...

Title of x CP,Title of y CP,Title of z CP
value of x for doc,,
value of x for doc,value of y for doc,value of z for doc
,value of y for rsr,
,,value of z for pldg

Fancy Pants One-Liner

If you want to get all fancy pants, you can solve the whole problem in one line of XPATH ...

<xsl:transform
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

<xsl:output method="text" encoding="UTF-8" />
<xsl:strip-space elements="*" />
<xsl:variable name="new-line" select="'&#x0A;&#x0D;'" />

<xsl:template match="/*">
  <xsl:value-of select="
    string-join( AllTitleByOrder/CustProp/@name, ','), (: Header :)
    ResultSet/ResultItem/Document/string-join(         (: Rows   :)
      for $ID in ../../../AllTitleByOrder/CustProp/@CPID
        return concat( CustomProperties/CustPropItem[@CPID eq $ID]/@value, ''),
      ',')
    " separator="{$new-line}"/>
</xsl:template>

</xsl:transform>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.