Solved

XSL Style Sheet Questions

Posted on 2013-06-24
12
270 Views
Last Modified: 2013-06-25
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.
0
Comment
Question by:lm1189
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 7
  • 3
  • 2
12 Comments
 
LVL 35

Assisted Solution

by:Robert Schutt
Robert Schutt earned 167 total points
ID: 39273852
Try replacing the for-each in your <Fields> output with:
            <xsl:for-each select="DataArea/Image/Header/*">
              <Field Name="{name(.)}" value="{./text()}" pass="True"/>
            </xsl:for-each>

Open in new window

0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 39273870
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"/>
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 39273880
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.
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 60

Assisted Solution

by:Geert Bormans
Geert Bormans earned 333 total points
ID: 39273893
Given you are new, and likely willing to learn.
It is also a best practice to break up templates and use apply-templates in favour of for-each

Here is how I would write this

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

Open in new window

0
 
LVL 35

Expert Comment

by:Robert Schutt
ID: 39273934
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?
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 39273949
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"/>
0
 
LVL 35

Expert Comment

by:Robert Schutt
ID: 39274045
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 ;-)
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 39274128
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 :-)
0
 

Author Comment

by:lm1189
ID: 39274799
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
0
 
LVL 60

Accepted Solution

by:
Geert Bormans earned 333 total points
ID: 39274835
Dataarea is not the root of your document

Fix it by changing the match in the root template

  <!-- Match the root -->
  <xsl:template match="/">

should become

 <!-- Match the root -->
  <xsl:template match="ProcessImage">
0
 

Author Comment

by:lm1189
ID: 39275102
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.
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 39275228
welcome
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The Client Need Led Us to RSS I recently had an investment company ask me how they might notify their constituents about their newsworthy publications.  Probably you would think "Facebook" or "Twitter" but this is an interesting client.  Their cons…
Introduction In my previous article (http://www.experts-exchange.com/Microsoft/Development/MS-SQL-Server/SSIS/A_9150-Loading-XML-Using-SSIS.html) I showed you how the XML Source component can be used to load XML files into a SQL Server database, us…
Although Jacob Bernoulli (1654-1705) has been credited as the creator of "Binomial Distribution Table", Gottfried Leibniz (1646-1716) did his dissertation on the subject in 1666; Leibniz you may recall is the co-inventor of "Calculus" and beat Isaac…
Attackers love to prey on accounts that have privileges. Reducing privileged accounts and protecting privileged accounts therefore is paramount. Users, groups, and service accounts need to be protected to help protect the entire Active Directory …

733 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question