I get the following error after my xslt has been processed:
There are 1 schema validation error(s):
1. Error Msg:The element 'BusinessObjectList' has incomplete content. List of possible elements expected: 'BusinessObject'. Line Number: 1, Line Position: 40, Severity:Error
I am trying to troubleshoot this issue and just require some clarifications. From my understanding of this error, there is a missing element called BusinessObject. So, I am not too sure if i have to incorporate this missing element or replace the existing element with this? another question too is, how to refer to Line 1, Line Position 40 in my xslt file?
Below is how the beginning of the xslt looks like:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:template match="/">
<BusinessObjectList SchemaVersion="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="HierarchicalObjects-1.0.xsd">
You haven't told us how you are running the transformation, but it seems to be configured so that on completion the result document is validated against the schema at HierarchicalObjects-1.0.xsd. Presumably that schema says that the BusinessObjectList must contain at least N BusinessObject elements (perhaps N is 1, we don't know), and your transformation output includes less than N.
You either need to produce output that conforms to the schema, or you need to change the way you are processing to avoid the validation step.
The line/column number probably refers to a line/column in the result document, not in the stylesheet.
Related
I am a novice XSLT developer. I have been asked to fix an issue on a project where the original developer is no longer with us. In the XSLT, there is a for-each loop using a key and a count
<xsl:for-each select="ns0:BOM[count(. | key('subsat', ns0:BomText01)[1]) = 1][ns0:BomText01]">
...
This is the key:
<xsl:key name="subsat" match="ns0:Parts/ns0:BOM[ns0:FindNum!='0']" use="ns0:BomText01" />
In the XML file being transformed, there are two sibling nodes that represent sub-parts:
<ns0:BOM referentId="10000:65091335:65359080">
<ns0:BomText01>3069260-303-SUB0027</ns0:BomText01>
<ns0:ItemNumber>My_part_1</ns0:ItemNumber>
<ns0:ItemType>Part</ns0:ItemType>
<ns0:Qty>67</ns0:Qty>
</ns0:BOM>
<ns0:BOM referentId="10000:65102551:86713230">
<ns0:BomText01>3069260-303-SUB0027</ns0:BomText01>
<ns0:ItemNumber>My_part_2</ns0:ItemNumber>
<ns0:ItemType>Part</ns0:ItemType>
<ns0:Qty>67</ns0:Qty>
</ns0:BOM>
However, the loop is only picking up the first node (My_part_1). I suspect it's because of the count=1 but I really don't know. And I don't know how to modify it. Ideas? If I need to include more data, let me know.
Assuming that the relevant part of your XSLT looks something like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="ns0" version="1.0">
<xsl:key name="subsat" match="ns0:BOM[ns0:FindNum!='0']" use="ns0:BomText01"/>
<xsl:template match="ns0:Parts">
<xsl:for-each
select="ns0:BOM[count(. | key('subsat', ns0:BomText01)[1]) = 1][ns0:BomText01]">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
It will only print the first of the elements because it is selecting the BOM elements which have an unique BomText01 value. That's the expected result.
If the BomText01 is an ID field (as it seems it is) and you expected to get both result (perhaps, because their ItemNumber contains different values), the error is possibly in your source (which assigned equal IDs when it should not have done so).
If you change one of those values in the source, you should be able to select both and verify this.
I'm attempting to create an XSLT mapping that properly converts a fairly large integer value coming through in a text field into the appropriate integer value. The problem is that since 1.0 only supports converting to type number, I get a value like 1.234567890E9 back for input of "1234567890"
I'm using Altova MapForce with XSLT1.0 as the coding platform. XSLT2.0 doesn't appear to be an option, as the XSLT has to be processed using a pre-existing routine that only supports XSLT1.0
By default Mapforce generates
<xsl:value-of select="string(floor(number(string(.))))"/>
and I've tried every combination of functions I can think of, but always get a float for large values.
Further testing shows the problem lies in Mapforce, which insists on using the number() function when mapping from text to int.
Let me try and move this forward by answering a question that you did not ask, but perhaps should have. Suppose you have the following input:
XML
<input>
<value>1234567890000000.9</value>
<value>9876543210000000</value>
</input>
and you want to make sure that the input values (which are all numbers, but some of them are not integers) are converted to integers at the output, you could apply the following transformation:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<output>
<xsl:for-each select="input/value">
<value><xsl:value-of select="format-number(., '#')"/></value>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
to obtain the following output:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<value>1234567890000001</value>
<value>9876543210000000</value>
</output>
Note that the results here are rounded, not floored.
Are you sure that mapforce isn't using xslt-2.0?
If I do in XSLT-1.0 (with either saxon or Altova's processor):
<xsl:value-of select="number('1234567890')"/>
I get -> 1234567890
If I use XSLT-2.0 I get -> 1.23456789E9
So I think it is very strange that an XSLT 1 transformation supposedly returns you the floating point representation of the number.
Formatting the number with format-number(1.23456789E9,'#') will always give you 1234567890 in both XSLT-1.0 and 2.0. Edit: saxon will not convert 1.23456789E9 to number in xslt-1.0, altova's processor however will.
The problem lies within Mapforce, so I've decided to let mapforce generate it's code, then overwrite it for this one field that's causing all the trouble.
#Tobias #Michael Thanks to you both for your help. I've +1'ed both your answers and a few comments since your help led to the answer.
this is a self-answered post, since it took me some time to find the reason for the following XTSE0120.
I'm quite new to XSLT and made a mistake and put some text outside <xsl:template>:
bellack#bellack-TP-T430u:~$ cat -n /tmp/x.xsl
1 <?xml version="1.0" encoding="UTF-8" ?>
2 <xsl:stylesheet
3 version="2.0"
4 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
5 >
6 <xsl:output method="text"/>
7 <xsl:template name="build" match="/">
8 <!--
9 a lot of xslt code lines
10 -->
11 </xsl:template>
12 ****** misplaced text ****************
13 </xsl:stylesheet>
As this is not allowed, I got error XTSE120, see http://www.w3.org/TR/xslt-30/#err-XTSE0120:
bellack#bellack-TP-T430u:~/workspace/tembanking/generator/crud$ java -jar ../lib/saxon9.jar -xsl:/tmp/x.xsl -s:/dev/null
Error at xsl:template on line 7 of file:/tmp/x.xsl:
XTSE0120: No character data is allowed between top-level elements
Failed to compile stylesheet. 1 error detected.
But the Error is reported for line 7, and since x.xsl had some hundred lines, I was searching for something wrong around line 7 in vain, where the error was in fact at the end of the file.
I don't know if other XSLT processors than saxon also give this misleading error message.
Hope that helps somebody ...
Udo
Saxon only keeps line number information for element nodes, not for text nodes, so we have difficulty reporting an accurate position for the rare case where the error is in a text node.
I'm trying to count the elements my transformation generates (must use XLST1). For example, my transformation creates:
<Parent>
<ElementX Att1="2"/>
<ElementY Att1="1"/>
<ElementZ Att1="6"/>
</Parent>
I need to print 3 within the same transformation, because there are 3 child elements.
Can this be done?
Thanks.
It would help a lot if you provide some extract of your XSLT.
I cn't give you a XSLT code without it. I'll try to give some "way" to the answer :
One solution could be to store the output into a nodeset (use the XSLT 1.0 extension which provides the nodeset() function) and apply the XPath count() function on this variable. After that just output your variable with xsl:value-of, and your count result the same way.
Here is a demo how to do this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<xsl:apply-templates/>
</xsl:variable>
<xsl:value-of select="count(ext:node-set($vrtfPass1)/*/*)"/>
</xsl:template>
<xsl:template match="/*">
<Parent>
<ElementX Att1="2"/>
<ElementY Att1="1"/>
<ElementZ Att1="6"/>
</Parent>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used in this Demo), the wanted, correct result is produced:
3
Explanation:
A general way to process the result of the transformation (in a single transformation), is to organize it in two passes where we save the result of the first pass in a variable.
In the second pass we access the result and do the additional processing.
Do note that in XSLT 1.0 if the variable that captures the result of the first pass is of the infamous RTF (Result Tree Fragment) type and needs to be converted to a regular tree in order of any nodes inside this tree to be accessible (xsl:copy-of and string() are still allowed on an RTF).
This conversion to a regular tree is done by an extension function, which most often has the name node-set and always belongs to a vendor-defined namespace. In this demo we are using the node-set() extension function that belongs to the EXSLT namespace -- because most XSLT 1.0 processors implement EXSLT.
For more information on multi-pass processing, see this: Two phase processing: Do not output empty tags from phase-1 XSLT 2.0 processing
I'm an experienced programmer, but a novice to XSLT and am finding it quite baffling. I apologize if this is a question that's been asked before, but I'm so frustrated by XSLT that I'm not even sure what to search for...
I have a problem that if a certain XML element appears only once, I want its contents output, but if it occurs more than once, I want only the contents of those that have a certain attribute.
For example, suppose I have one XML file (call it "file 1") that contains
<food>
<snack>Chips</snack>
<snack type="nuts">Peanuts</snack>
</food>
and another XML file ("file 2") that contains
<food>
<snack>Cheese puffs</snack>
</food>
I need an XSLT that outputs only "Peanuts" (but not "Chips") upon processing file 1, but still outputs "Cheese puffs" for file 2 (i.e. I can't just select only those elements that have a "type" attribute, that would be too easy).
This is probably simple, but I'm stuck...
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"*[snack/#type]/snack[not(#type)]"/>
</xsl:stylesheet>
produces the wanted results in both cases.
Explanation:
The identity rule/template copies every node "as-is".
The second template is overriding the identity template for any snack element without a type attribute that has sibling snack elements that have a type attribute. This template has empty body, which effectively "deletes" the matched element from (being copied to) the output.
if a certain XML element appears only once, I want its contents output, but if it occurs more than once, I want only the contents of those that have a certain attribute.
A direct translation of that would be
if (count(snack) = 1) then snack else snack[#type='nuts']
which is valid XPath 2.0 syntax - if you need to do it in 1.0 then it translates fairly directly (though verbosely) into an equivalent xsl:choose.
If you want something even more concise than the above, you can also write in XPath 2.0
(snack[#type='nuts'], snack)[1]
which builds a list containing first the snacks with type='nuts', then all the snacks, and then selects the first item from this list.