Link to home
Start Free TrialLog in
Avatar of lm1189
lm1189

asked on

XSL Style Sheet Questions

Experts,

I have a stylesheet that has me a bit stumped, I'm new at XQuery and XSL so I apologize in advanced.  I have a style sheet, and I'm trying to capture the data in the node /DataArea/Image/Header, and create a record in the preset tags, the record should look like:

<Field Name="NAMEOFTAG" value="VALUEINTAG" pass="True"/>

So for example, if I'm looking at data that looks like:

	<DataArea>
		<Process confirm="Always" acknowledge="Never"/>
		<Image>
			<Header>
				<AppNumber>165934</AppNumber>
				<OrderNumber>613242</OrderNumber>
				<StockNumber>2724607</StockNumber>
				<Count>9</Count>
			</Header>

Open in new window


It should look similar to below in my output:

<Field Name="AppNumber" value="165934" pass="True"/>
<Field Name="OrderNumber" value="613242" pass="True"/>
<Field Name="StockNumber" value="2724607" pass="True"/>


<?xml version="1.0"?>

<!-- Begin XST Style Sheet -->


<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  
	<xsl:template match="/">
    <Import>
      <Archive ConnectionID="1" Name="1">
        <Document pass="True">
          <xsl:for-each select="Pages/Page">
          <DocFile FileLoc="{@FileName}" />
          </xsl:for-each>
          <Fields>
            <xsl:for-each select="DataArea/Image/Header">
              <Field Name="{name(.)}" value="{@Value}" pass="True"/>
            </xsl:for-each>
          </Fields>
        </Document>
      </Archive>
    </Import>
	</xsl:template>
</xsl:stylesheet>

Open in new window


Any direction that could be given could be much appreciated.
SOLUTION
Avatar of Robert Schutt
Robert Schutt
Flag of Netherlands image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi, in the for-each you are visiting the Header element, which is only one...
you should visit the child elements on line 16
and you don't have to access the @value but the content

            <xsl:for-each select="DataArea/Image/Header/*">
              <Field Name="{name()}" value="{.}" pass="True"/>
Ah, you got there already

Note that you should NOT use ./text() because there is no garantuee that you will get all the content then

value="{./text()}" will put the first child text node in the value attribute. Processors or parsers can decibe to break a text content up in multiple nodes at will, so that could cause your value attribute to only be partially there

value="{.}" will take all the text nodes (including deeper nested text nodes) and serializes them as one string

It is a hot item on the list of best practices in XSLT development to avoid text() as much as you can, because it is unpredictable.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Nice solution, Gertone.

I wonder about ./text(), it may depend on what's more likely to happen, you may need a scheme check anyway. For example if the data sent would suddenly contain
<OrderNumber>613242<BAD>True</BAD></OrderNumber>

Open in new window

then your solution concatenates the text nodes into the output which doesn't seem right either?
interesting for lm1189 to know indeed

using {./text()} your example would render
<Field Name="OrderNumber" value="613242" pass="True"/>
but you are never 100% sure, it could render
<Field Name="OrderNumber" value="613" pass="True"/>
depending what the XML object is and where it comes from

using {.} you are right, it would render
<Field Name="OrderNumber" value="613242True" pass="True"/>

But given this counter example
<OrderNumber><BAD>True</BAD>613242</OrderNumber>

using {./text()} this example would render
<Field Name="OrderNumber" value="" pass="True"/>
since it catches the empty whitespace text node in front of the BAD element

using {.} render
<Field Name="OrderNumber" value="True613242" pass="True"/>

Point I mainly wanted to make is that at least using '.' it is predictable

Since the XSLT is labelled 2.0
And you want to avoid child elements (I considered child elements less likely from the source than broken text nodes would be)
This one is 100% safe
       <Field Name="{name()}" value="{normalize-space(string-join(text(), ''))}" pass="True"/>
I had not considered the order but I tested and don't get the empty value, but point taken. This is great stuff, hopefully not just us looking at it ;-)
well, I just tested with Saxon, and indeed, it seems not all processors take the empty text node as a text() node
so
<OrderNumber><BAD>True</BAD>613242</OrderNumber>
not necessarily has a text node before the BAD, it would if there was a space or a comment or a ...
Msxml has the tendency to drop it anyhow

Interesting discussion indeed... maybe we are making lm1189 nervous now :-)
Avatar of lm1189
lm1189

ASKER

I'm not nervous.. :)

I tried the different solutions, and neither seem to work.  I've attached the sample XML file I am using as well as the XSL.  Any idea why?  Logically it makes sense, it looks like it should grab the next child node and report it back.
ND7.xsl
sample.xml
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of lm1189

ASKER

Everyone, thanks for your help.  It's doing exactly as I need it, I have some other questions, I'll follow up in another post.