tags:

views:

563

answers:

3

Hi ....

The Title pretty much says it all. I have an XML document that I am processing with XSLT .... but I don't know how many columns (fields) that are in the XML document (therefore, obviously, I don't know their names). How can I determine the number of "unknown" fields there are? Also, how can I read the attributes, if any, for the unknown fields?

Example data ....

<Dataset>
   <Row>
       <UnknownCol1 Msg="HowDoIGetThisMsgAttribute?"/>
       <UnknownCol2 />
       <UnknownCol3 />
   </Row>
</Dataset>
+2  A: 

There's easy ways around this problem.

For the example you provided you could use the following XPath:

select='/Dataset/Row/*/@Msg'

If you wanted a more specific example of this, you may want to make your question clearer on exactly what you'd like to do with the data and the unknown columns. Are you copying it exactly? What specifically do you want to transform it into? Do you have any keys you'd like to match?

That kind of thing.

Jweede
Jweede, thanks for your response. I'll answer your questions to the best of my ability:I would like to take the XML and transform it via XSLT. I didn't copy the input data exactly, but the above is an accurate approximation of it. I want to transform it into HTML. No keys to match.
OneSource
+2  A: 

How can I determine the number of "unknown" fields there are?

This XPath expression:

count(/*/*/*)

evalutes to the count of the elements, that a re children of the elements that are children of the top node of the XML document -- exactly what is wanted in this case.

If the "Row" element can have children whose name does not start with "UnknownCol",

then this XPath expression provides the count of elements, whose name starts with "UnknownCol", and that are children of elements that are children of the top element:

count(/*/*/*[starts-with(name(), "UnknownCol")])

In case the top element may have other children than "Row", then an XPath expression giving the required count is:

count(/*/Row/*[starts-with(name(), "UnknownCol")])

Also, how can I read the attributes, if any, for the unknown fields?

By knowing XPath :)

/*/Row/*[starts-with(name(), "UnknownCol")]/@*

selects all the attributes of all "UnknownCol"{String} elements

This XPath expression gives us the number of these attributes:

  count(/*/Row/*[starts-with(name(), "UnknownCol")]/@*)

This Xpath expression gives us the name of the k-th such attribute ($ind must be set to the number k):

name( (/*/Row/*[starts-with(name(), "UnknownCol")]/@*)[$ind] )

And finally, this XPath expression produces the value of the k-th such attribute:

string( (/*/Row/*[starts-with(name(), "UnknownCol")]/@*)[$ind] )

Edit: The OP commented that he completely doesn't lnow the names of the children element.

The fix is easy: simply remove the predicate from all expressions:

count(/*/*/*)

count(/*/Row/*)


/*/Row/*/@*


count(/*/Row/*/@*)

name( (/*/Row/*/@*)[$ind] )

string( (/*/Row/*/@*)[$ind] )
Dimitre Novatchev
Dimitre, thanks. I don't know how much of the above will be useful because you are referencing "UnknownCol" in your examples. I will not know the column name at runtime.
OneSource
@OneSource This is how I understood your question... The edit is simple: simply remove the predicate everywhere.
Dimitre Novatchev
@Dimitre .... I looked into your responses in more detail and figured out the removing the predicate part. I should be all set (assuming I can use these functions within an XSLT document). Big thanks for your help!
OneSource
@OneSource These are called "XPath expressions" and can be used anywhere in XSLT where an XPath expression is allowed: mainly in the "select" attribute of <xsl:value-of>, <xsl:copy-of>, <xsl:variable>, <xsl:param>, <xsl:for-each>, <xsl:apply-templates>, ..., etc.
Dimitre Novatchev
+1  A: 

Here is a small sample of XSLT 1.0 code that transforms your input into a HTML table.

<xsl:template match="Dataset">
  <table>
    <thead>
      <tr>
        <xsl:apply-templates select="Row[1]/*" mode="th" />
      </tr>
    </thead>
    <tbody>
      <xsl:apply-templates select="Row" />
    </tbody>
  </table>
</xsl:template>

<xsl:template match="Row">
  <tr>
    <xsl:apply-templates select="*" mode="td" />
  </tr>
</xsl:template>

<xsl:template match="Row/*" mode="th">
  <th>
    <xsl:value-of select="local-name()" />
  </th>
</xsl:template>

<xsl:template match="Row/*" mode="td">
  <td>
    <xsl:value-of select="@Msg" />
  </td>
</xsl:template>

When applied to this sample input:

<Dataset>
  <Row>
    <UnknownCol1 Msg="Data_1_1" />
    <UnknownCol2 Msg="Data_1_2" />
    <UnknownCol3 Msg="Data_1_3" />
  </Row>
  <Row>
    <UnknownCol1 />
    <UnknownCol2 Msg="Data_2_2" />
    <UnknownCol3 Msg="" />
  </Row>
</Dataset>

this output is returned:

<table>
  <thead>
    <tr>
      <th>UnknownCol1</th>
      <th>UnknownCol2</th>
      <th>UnknownCol3</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Data_1_1</td>
      <td>Data_1_2</td>
      <td>Data_1_3</td>
    </tr>
    <tr>
      <td></td>
      <td>Data_2_2</td>
      <td></td>
    </tr>
  </tbody>
</table>
Tomalak