link target could not be resolved - xslt

I am creating a table of contents and a bookmark tree using Saxon (9.1) and FOP (0.95). Everything works fine, except for the following FOP output:
WARNING: 1 link target could not be resolved and now point to the top of the page or is dysfunctional.
In the resulting PDF, all links (from both TOC and bookmark tree) point to the first chapter. Why is that? Thank you for any help.
XML input file:
<?xml version="1.0"?>
<document>
<header>
<title>This is the title of the document</title>
<author>Mathias Mueller</author>
<date>29/10/2013</date>
</header>
<body>
<chapter level="1">
<chaptitle>This is the title of the first chapter.</chaptitle>
<p>All Saints Day (also known as All Hallows, Solemnity of All Saints...</p>
</chapter>
<chapter level="2">
<chaptitle>This is the title of the second chapter.</chaptitle>
<p>In Western Christian theology, the day commemorates ...</p>
</chapter>
<chapter level="2">
<chaptitle>This is the title of the third chapter.</chaptitle>
<p>The feast of All Saints achieved great prominence in the ninth century, in the reign of the Byzantine Emperor, Leo VI the Wise...</p>
</chapter>
</body>
<documentProperties>
<orientation>portrait</orientation>
<format>A4</format>
</documentProperties>
</document>
XSLT 2.0-Stylesheet (only attribute-sets defining block properties are left out)
<xsl:template match="node()|#*">
<xsl:apply-templates select="node()|#*"/>
</xsl:template>
<xsl:template match="document">
<xsl:element name="fo:root" xmlns="http://www.w3.org/1999/XSL/Format">
<xsl:call-template name="docPr"/>
<xsl:call-template name="bmt"/>
<!--page sequence for TOC-->
<xsl:element name="fo:page-sequence">
<xsl:attribute name="master-reference">A4portrait</xsl:attribute>
<xsl:element name="fo:flow">
<xsl:attribute name="flow-name">xsl-region-body</xsl:attribute>
<xsl:call-template name="toc"/>
</xsl:element>
</xsl:element>
<!--rest-->
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template name="docPr">
<xsl:element name="fo:layout-master-set">
<xsl:if test="documentProperties/orientation eq 'portrait' and documentProperties/format eq 'A4'">
<xsl:element name="fo:simple-page-master" use-attribute-sets="A4portrait">
<xsl:element name="fo:region-body">
<xsl:attribute name="margin-top">25mm</xsl:attribute>
<xsl:attribute name="margin-bottom">20mm</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:if>
</xsl:element>
</xsl:template>
<xsl:template name="bmt">
<xsl:element name="fo:bookmark-tree">
<xsl:for-each select="body/chapter">
<xsl:element name="fo:bookmark">
<xsl:attribute name="internal-destination">{generate-id()}</xsl:attribute>
<xsl:element name="fo:bookmark-title">
<xsl:value-of select="chaptitle"/>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
<xsl:template name="toc">
<xsl:element name="fo:block">
<xsl:attribute name="break-before">page</xsl:attribute>
<xsl:element name="fo:block" use-attribute-sets="title2-block">
<xsl:text>Table of Contents</xsl:text>
</xsl:element>
<xsl:for-each select="body/chapter">
<xsl:element name="fo:block">
<xsl:attribute name="text-align-last">justify</xsl:attribute>
<xsl:element name="fo:basic-link">
<xsl:attribute name="internal-destination">{generate-id()}</xsl:attribute>
<xsl:value-of select="count(preceding::chapter) + 1"/>
<xsl:text> </xsl:text>
<xsl:value-of select="chaptitle"/>
<xsl:element name="fo:leader">
<xsl:attribute name="leader-pattern">dots</xsl:attribute>
</xsl:element>
<xsl:element name="fo:page-number-citation">
<xsl:attribute name="ref-id">{generate-id()}</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
<xsl:template match="chapter">
<xsl:element name="fo:page-sequence">
<xsl:attribute name="master-reference">A4portrait</xsl:attribute>
<xsl:attribute name="id">{generate-id()}</xsl:attribute>
<xsl:element name="fo:flow">
<xsl:attribute name="flow-name">xsl-region-body</xsl:attribute>
<xsl:apply-templates/>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="chaptitle">
<xsl:element name="fo:block" use-attribute-sets="title1-block">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="p">
<xsl:element name="fo:block" use-attribute-sets="normal-block">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>

I think the problem is the way you call the XSLT generate-id() function. The syntax with brackets {} usually only works inside "" quotes. What now happens is that all ids are assigned the literal string {generate-id()}. I would suggest that you replace these occurences by <xsl:value-of select="generate-id()"/> and try again.

Related

XSLT element loop

My XSLT is like
<!DOCTYPE stylesheet [
<!ENTITY hyphen "<xsl:text>-</xsl:text>">
<!ENTITY cr "<xsl:text>
</xsl:text>">
]>
<xsl:stylesheet id="test"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext"
version="1.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="vc_NDCROI" select="'NDC05'" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()|#*" mode="mPass2">
<xsl:copy>
<xsl:apply-templates select="node()|#*" mode="mPass2"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="vrtfPass1Result">
&cr;
<xsl:comment>ACS&T
Receipt Confirmation
</xsl:comment>
&cr;
<xsl:element name="Batch">
<xsl:element name="ASN">
<xsl:for-each
select="dcsextractdata/dataheaders/dataheader[record_type = 'ORH']">
<xsl:apply-templates select="." mode="orh_receipt" />
</xsl:for-each>
</xsl:element>
</xsl:element>
</xsl:variable>
<xsl:apply-templates mode="mPass2"
select="ext:node-set($vrtfPass1Result)/*" />
</xsl:template>
<xsl:template match="/*[local-name()='Batch']" mode="mPass2">
&cr;
<Batch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsl:copy-of select="node()|#*"/>
</Batch>
</xsl:template>
<xsl:template match="dataheader" mode="orh_receipt">
<xsl:element name="BatchReferences">
<xsl:element name="SenderCode"><xsl:value-of select="site_user_def_type_7"/></xsl:element>
<xsl:element name="ReceiverCode"><xsl:value-of select="client_user_def_type_7" /> </xsl:element>
</xsl:element>
<xsl:element name="Supplier">
<xsl:element name="EanCode"><xsl:value-of select="client_name" /> </xsl:element>
</xsl:element>
<xsl:element name="Customer">
<xsl:element name="EanCode"></xsl:element>
<xsl:element name="EDI1Code"></xsl:element>
<xsl:element name="EDI2Code"></xsl:element>
<xsl:element name="EDI3Code"></xsl:element>
<xsl:element name="EDI4Code"></xsl:element>
</xsl:element>
<xsl:element name="Consignee">
<xsl:element name="EanCode"><xsl:value-of select="add_user_def_type_1" /> </xsl:element>
</xsl:element>
<xsl:choose>
<xsl:when test="user_def_chk_1 != 'Y'">
<xsl:element name="Consignor">
<xsl:element name="EanCode"></xsl:element>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:element name="Consignor">
<xsl:element name="EanCode">5060133990239</xsl:element>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
<xsl:element name="DeliverFrom">
<xsl:element name="EanCode"></xsl:element>
</xsl:element>
<xsl:element name="DeliverTo">
<xsl:element name="EanCode"><xsl:value-of select="add_user_def_type_1" /> </xsl:element>
</xsl:element>
<xsl:element name="DocumentNumber"><xsl:value-of select="user_def_type_1" /> </xsl:element>
<xsl:element name="DocumentDate"><xsl:value-of select="creation_date" /> </xsl:element>
<xsl:element name="ShippingDate"><xsl:value-of select="creation_date" /> </xsl:element>
<xsl:choose>
<xsl:when test="user_def_chk_1 != 'Y'">
<xsl:element name="Carrier">
<xsl:element name="SuppliersCode"></xsl:element>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:element name="Carrier">
<xsl:element name="SuppliersCode">5060133990239</xsl:element>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
<xsl:element name="OrderNumber">
<xsl:element name="Customers"><xsl:value-of select="order_id" /> </xsl:element>
</xsl:element>
<xsl:element name="OrderDate">
<xsl:element name="Customers"><xsl:value-of select="order_date" /> </xsl:element>
</xsl:element>
<xsl:element name="ShipmentNumber"><xsl:value-of select="user_def_type_2" /> </xsl:element>
<xsl:element name="PickListNumber"><xsl:value-of select="user_def_type_3" /> </xsl:element>
<xsl:element name="Package">
<xsl:element name="Markings">
<xsl:element name="Barcode"><xsl:value-of select="user_def_type_1" /> </xsl:element>
</xsl:element>
</xsl:element>
<xsl:for-each select="datalines/dataline[record_type = 'ORL']" >
<xsl:apply-templates select="." mode="lines" />
</xsl:for-each>
<xsl:template match="dataline" mode="lines">
<xsl:element name="Product">
<xsl:element name="LineNumber"><xsl:value-of select="user_def_num_1" /> </xsl:element>
<xsl:element name="EANCode"><xsl:value-of select="ean" /> </xsl:element>
<xsl:element name="SuppliersCode"><xsl:value-of select="sku_id" /> </xsl:element>
<xsl:element name="Quantity">
<xsl:element name="Amount"><xsl:value-of select="qty_shipped" /> </xsl:element>
</xsl:element>
<xsl:element name="CreationDate">0001-01-01</xsl:element>
<xsl:element name="ExpiresDate"></xsl:element>
<xsl:element name="CustomerPalStatus"><xsl:value-of select="condition_id" /> </xsl:element>
<xsl:element name="Markings">
<xsl:element name="Barcode"><xsl:value-of select="batch_id" /> </xsl:element>
<xsl:element name="InitialPalletQty"></xsl:element>
<xsl:element name="Batch"><xsl:value-of select="batch_id" /> </xsl:element>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:template>
</xsl:stylesheet>
I have loop for lines , I have package tag starting from before marking tag and right now package tag is closing after marking tag but should be close after product tag and before ASN close tag.Please help.
You need to understand your schema.
Currently, ASN tag opens and closes at top, Package opens and closes towards bottom (wrapped around Markings), and Product opens and closes at the very bottom.
For Package to close after Product and before ASN, requires ASN to be wrapped around Package and Product. And Package wrapped around Product.
So move <xsl:element name = ...> and corresponding </xsl:element> tags around to the needed hierarchy:
<xsl:element name="ASN">
...
<xsl:element name="Package">
...
<xsl:element name="Product">
...
</xsl:element> <!-- Product's closing -->
</xsl:element> <!-- Package's closing -->
</xsl:element> <!-- ASN's closing -->
</xsl:template>
</xsl:template>
</xsl:stylesheet>

xsl:if or xsl:applytemplate which one has better memroy usage

Lets say that I have a input like following.
<country>
<name>countryname</name>
<capital>captialname</capital>
<population>19000</population>
</country>
I'm trnsforming element names to lets say upper code using an xsl. Child elements of country may not occur sometimes. So I can write my transformation as follows.
<xsl:template match="country">
<xsl:element name="COUNTRY">
<xsl:apply-templates select="name" />
<xsl:apply-templates select="capital" />
<xsl:apply-templates select="population" />
</xsl:element>
</xsl:template>
<xsl:template match="name">
<xsl:element name="NAME">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
<xsl:template match="capital">
<xsl:element name="CAPITAL">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
<xsl:template match="population">
<xsl:element name="POPULATION">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
or I can do it as follows.
<xsl:template match="country">
<xsl:element name="COUNTRY">
<xsl:if test="name">
<xsl:element name="NAME">
<xsl:value-of select="." />
</xsl:element>
</xsl:if>
<xsl:if test="capital">
<xsl:element name="CAPITAL">
<xsl:value-of select="." />
</xsl:element>
</xsl:if>
<xsl:if test="population">
<xsl:element name="POPULATION">
<xsl:value-of select="." />
</xsl:element>
</xsl:if>
</xsl:element>
I'm wondering which way it'd use less memory. The actual code I have goes around seven levels deep inside templates. So what I need to know is if I don't use use templates for simple elements will if improve memory usage.
As per my understanding first one is good. Just change:
<xsl:apply-templates select="name" />
<xsl:apply-templates select="capital" />
<xsl:apply-templates select="population" />
To
<xsl:apply-templates/>
only and don't worry about child element if they are not coming sometimes XSLT would take care it.
I think you are approaching from the wrong way. Your current XSLT templates isn't very flexible. You would have contantly amend it for new elements being added.
Instead you should be using the identity transform, and including a template to match any generic element, outut the name as upper case.
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()[not(self::*)]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{upper-case(local-name())}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Note the the following template matches any element and converts the name to upper case
<xsl:template match="*">
Whereas the other template match will match attributes or any other nodes other than elements, and just copies them as-is
<xsl:template match="#*|node()[not(self::*)]">
Note that this solution would not work if the XML had namespaces defined, but it wouldn't take much tweaking to get it to cope.

XSLT: xsi:schemaLocation is not showing on XML

I am trying to do a transformation but I am having an issue with the xsi:schemaLocation not showing on the XML result. I have searched all over why this is happening and I just can't seem to find it. Please help
Input XML
<asx:abap version="1.0" xmlns:asx="http://www.sap.com/abapxml">
<asx:values>
<_--5CCLASS_--3DZCL_T4_NOM_--5CTYPE_--3DTT_SAP_REQ>
<item>
<TECHNICAL_KEY>00006000000000326391</TECHNICAL_KEY>
<TSCARRIER>CPL</TSCARRIER>
<MATERIAL>V2</MATERIAL>
<PIPELINE_CYCLE>45</PIPELINE_CYCLE>
<PIPELINE_SEQUENCE>1</PIPELINE_SEQUENCE>
<CYCLE_YEAR>2012</CYCLE_YEAR>
<SCD/>
<NOM_NUMBER>CITV2451</NOM_NUMBER>
<DATE_TIME>2012-06-20T00:00:00</DATE_TIME>
<CREATE_DATE_TIME>2012-06-06T18:01:18</CREATE_DATE_TIME>
<TOTAL_ITEMS>1 </TOTAL_ITEMS>
<T4NOMNR/>
<ITEM_NUMBER>0000000010</ITEM_NUMBER>
<ITEM_DETAIL_CHANGE_CODE/>
<PIPELINE_EVENT>Delivery</PIPELINE_EVENT>
<PIPELINE_EVENT_AFFECT>Decrease</PIPELINE_EVENT_AFFECT>
<QUANTITY>25000.00</QUANTITY>
<UOM>BR</UOM>
<LOCATION>AHT</LOCATION>
<LOCATION_PARTNER>AHT</LOCATION_PARTNER>
<CONTRACT_PARTNER/>
<SUPPLIER/>
<TANKAGE_PARTNER>KM1</TANKAGE_PARTNER>
<CONSIGNEE_PARTNER>CIT</CONSIGNEE_PARTNER>
<CONS_SUPPLIER>Consignee</CONS_SUPPLIER>
<TRANSPURIND>Add</TRANSPURIND>
<VERSION>1</VERSION>
<PARTROLEIND>Shipper</PARTROLEIND>
<SHIPPARTID>CIT</SHIPPARTID>
<PART_IDENTIFIER>AssignedByCarrier</PART_IDENTIFIER>
<PROD_IDENTIFIER>AssignedByMarketplace</PROD_IDENTIFIER>
<PART_TANKAGE>Tankage</PART_TANKAGE>
<CARRIER_STRING>Carrier</CARRIER_STRING>
<SHIPNOMNRSTRING>ShipperNominationNumber</SHIPNOMNRSTRING>
</item>
</_--5CCLASS_--3DZCL_T4_NOM_--5CTYPE_--3DTT_SAP_REQ>
</asx:values>
XSLT Code
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns="http://www.api.org/pidXML/v1.2" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:pidx="http://www.api.org/pidXML/v1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.api.org/pidXML/v1.2 https://www.transport4.com/xml/schemas/PipelineNominationCreateV1-3.xsd">
<xsl:template match="/">
<xsl:element name="pidx:PipelineNominationCreate">
<xsl:attribute name="pidx:documentIdentifier"><xsl:value-of select="//TECHNICAL_KEY"/></xsl:attribute>
<xsl:attribute name="pidx:transactionPurposeIndicator"><xsl:value-of select="//TRANSPURIND"/></xsl:attribute>
<xsl:attribute name="pidx:version"><xsl:value-of select="//VERSION"/></xsl:attribute>
<xsl:element name="pidx:PipelineNominationCreateProperties">
<xsl:element name="pidx:CreatedByPartner">
<xsl:element name="pidx:PartnerInformation">
<xsl:attribute name="partnerRoleIndicator"><xsl:value-of select="//PARTROLEIND"/></xsl:attribute>
<xsl:element name="pidx:PartnerIdentifier">
<xsl:attribute name="partnerIdentifierIndicator"><xsl:value-of select="//PART_IDENTIFIER"/></xsl:attribute>
<xsl:value-of select="//SHIPPARTID"/>
</xsl:element>
</xsl:element>
<xsl:element name="pidx:EventDateTime">
<xsl:value-of select="//CREATE_DATE_TIME"/>
</xsl:element>
</xsl:element>
<xsl:element name="pidx:PartnerInformation">
<xsl:attribute name="partnerRoleIndicator"><xsl:value-of select="//PARTROLEIND"/></xsl:attribute>
<xsl:element name="pidx:PartnerIdentifier">
<xsl:attribute name="partnerIdentifierIndicator"><xsl:value-of select="//PART_IDENTIFIER"/></xsl:attribute>
<xsl:value-of select="//SHIPPARTID"/>
</xsl:element>
</xsl:element>
<xsl:element name="pidx:PartnerInformation">
<xsl:attribute name="partnerRoleIndicator"><xsl:value-of select="//CARRIER_STRING"/></xsl:attribute>
<xsl:element name="pidx:PartnerIdentifier">
<xsl:attribute name="partnerIdentifierIndicator"><xsl:value-of select="//PART_IDENTIFIER"/></xsl:attribute>
<xsl:value-of select="//TSCARRIER"/>
</xsl:element>
</xsl:element>
<xsl:element name="pidx:ProductInformation">
<xsl:element name="pidx:ProductIdentifier">
<xsl:attribute name="assigningOrganization"><xsl:value-of select="//PROD_IDENTIFIER"/></xsl:attribute>
<xsl:value-of select="//MATERIAL"/>
</xsl:element>
</xsl:element>
<xsl:element name="pidx:PipelineCycle">
<xsl:value-of select="//PIPELINE_CYCLE"/>
</xsl:element>
<xsl:element name="pidx:PipelineCycleYear">
<xsl:value-of select="//CYCLE_YEAR"/>
</xsl:element>
<xsl:element name="pidx:PipelineSequence">
<xsl:value-of select="//PIPLELINE_SEQUENCE"/>
</xsl:element>
<xsl:element name="pidx:PipelineSCD">
<xsl:value-of select="//SCD"/>
</xsl:element>
<xsl:element name="pidx:ReferenceInformation">
<xsl:attribute name="referenceInformationIndicator"><xsl:value-of select="//SHIPNOMNRSTRING"/></xsl:attribute>
<xsl:element name="pidx:ReferenceNumber">
<xsl:value-of select="//NOM_NUMBER"/>
</xsl:element>
<xsl:element name="pidx:Description">
</xsl:element>
</xsl:element>
</xsl:element>
<xsl:element name="pidx:PipelineNominationCreateDetails">
<xsl:for-each select="//item">
<xsl:element name="pidx:PipelineNominationCreateLineItem">
<xsl:element name="pidx:LineItemNumber">
<xsl:value-of select="ITEM_NUMBER"/>
</xsl:element>
<xsl:element name="pidx:LineItemNominationQuantity">
<xsl:element name="pidx:Quantity">
<xsl:value-of select="QUANTITY"/>
</xsl:element>
<xsl:element name="pidx:UnitOfMeasureCode">
<xsl:value-of select="UOM"/>
</xsl:element>
</xsl:element>
<xsl:element name="pidx:PartnerInformation">
<xsl:attribute name="partnerRoleIndicator"><xsl:value-of select="CONS_SUPPLIER"/></xsl:attribute>
<xsl:element name="pidx:PartnerIdentifier">
<xsl:attribute name="partnerIdentifierIndicator"><xsl:value-of select="//PART_IDENTIFIER"/></xsl:attribute>
<xsl:value-of select="CONSIGNEE_PARTNER"/>
</xsl:element>
</xsl:element>
<xsl:element name="pidx:PartnerInformation">
<xsl:attribute name="partnerRoleIndicator"><xsl:value-of select="PART_TANKAGE"/></xsl:attribute>
<xsl:element name="pidx:PartnerIdentifier">
<xsl:attribute name="partnerIdentifierIndicator"><xsl:value-of select="//PART_IDENTIFIER"/></xsl:attribute>
<xsl:value-of select="TANKAGE_PARTNER"/>
</xsl:element>
</xsl:element>
<xsl:element name="pidx:ScheduleStartDateTime">
<xsl:value-of select="DATE_TIME"/>
</xsl:element>
<xsl:element name="pidx:ScheduleRequestDateTime">
</xsl:element>
<xsl:element name="pidx:PipelineEventInformation">
<xsl:element name="pidx:PipelineEvent">
<xsl:value-of select="PIPELINE_EVENT"/>
</xsl:element>
<xsl:element name="pidx:PipelineEventVolumeAffect">
<xsl:value-of select="PIPELINE_EVENT_AFFECT"/>
</xsl:element>
</xsl:element>
<xsl:element name="pidx:CustodyLocationInformation">
<xsl:element name="pidx:CustodyLocationIdentifier">
<xsl:attribute name="identifierIndicator"><xsl:value-of select="//PART_IDENTIFIER"/></xsl:attribute>
<xsl:value-of select="LOCATION_PARTNER"/>
</xsl:element>
</xsl:element>
<xsl:element name="pidx:ReferenceInformation">
<xsl:attribute name="referenceInformationIndicator">
</xsl:attribute>
<xsl:element name="pidx:ReferenceNumber">
</xsl:element>
<xsl:element name="pidx:Description">
</xsl:element>
</xsl:element>
<xsl:element name="pidx:Comment">
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:element>
<xsl:element name="pidx:PipelineNominationCreateSummary">
<xsl:element name="pidx:TotalLineItems">
<xsl:value-of select="//TOTAL_ITEMS"/>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Result XML with missing schemaLocation
<?xml version="1.0" encoding="iso-8859-1"?>
<pidx:PipelineNominationCreate xmlns:pidx="http://www.api.org/pidXML/v1.2" xmlns="http://www.api.org/pidXML/v1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" pidx:documentIdentifier="00006000000000326391" pidx:transactionPurposeIndicator="Add" pidx:version="1">
<pidx:PipelineNominationCreateProperties>
<pidx:CreatedByPartner>
<pidx:PartnerInformation partnerRoleIndicator="Shipper">
<pidx:PartnerIdentifier partnerIdentifierIndicator="AssignedByCarrier">CIT</pidx:PartnerIdentifier>
</pidx:PartnerInformation>
<pidx:EventDateTime>2012-06-07T15:55:35</pidx:EventDateTime>
</pidx:CreatedByPartner>
<pidx:PartnerInformation partnerRoleIndicator="Shipper">
<pidx:PartnerIdentifier partnerIdentifierIndicator="AssignedByCarrier">CIT</pidx:PartnerIdentifier>
</pidx:PartnerInformation>
<pidx:PartnerInformation partnerRoleIndicator="Carrier">
<pidx:PartnerIdentifier partnerIdentifierIndicator="AssignedByCarrier">CPL</pidx:PartnerIdentifier>
</pidx:PartnerInformation>
<pidx:ProductInformation>
<pidx:ProductIdentifier assigningOrganization="AssignedByMarketplace">V2</pidx:ProductIdentifier>
</pidx:ProductInformation>
<pidx:PipelineCycle>45</pidx:PipelineCycle>
<pidx:PipelineCycleYear>2012</pidx:PipelineCycleYear>
<pidx:PipelineSequence/>
<pidx:PipelineSCD/>
<pidx:ReferenceInformation referenceInformationIndicator="ShipperNominationNumber">
<pidx:ReferenceNumber>CITV2451</pidx:ReferenceNumber>
<pidx:Description/>
</pidx:ReferenceInformation>
</pidx:PipelineNominationCreateProperties>
<pidx:PipelineNominationCreateDetails>
<pidx:PipelineNominationCreateLineItem>
<pidx:LineItemNumber>0000000010</pidx:LineItemNumber>
<pidx:LineItemNominationQuantity>
<pidx:Quantity>25000.0</pidx:Quantity>
<pidx:UnitOfMeasureCode>BR</pidx:UnitOfMeasureCode>
</pidx:LineItemNominationQuantity>
<pidx:PartnerInformation partnerRoleIndicator="Consignee">
<pidx:PartnerIdentifier partnerIdentifierIndicator="AssignedByCarrier">CIT</pidx:PartnerIdentifier>
</pidx:PartnerInformation>
<pidx:PartnerInformation partnerRoleIndicator="Tankage">
<pidx:PartnerIdentifier partnerIdentifierIndicator="AssignedByCarrier">KM1</pidx:PartnerIdentifier>
</pidx:PartnerInformation>
<pidx:ScheduleStartDateTime>2012-06-20T00:00:00</pidx:ScheduleStartDateTime>
<pidx:ScheduleRequestDateTime/>
<pidx:PipelineEventInformation>
<pidx:PipelineEvent>Delivery</pidx:PipelineEvent>
<pidx:PipelineEventVolumeAffect>Decrease</pidx:PipelineEventVolumeAffect>
</pidx:PipelineEventInformation>
<pidx:CustodyLocationInformation>
<pidx:CustodyLocationIdentifier identifierIndicator="AssignedByCarrier">AHT</pidx:CustodyLocationIdentifier>
</pidx:CustodyLocationInformation>
<pidx:ReferenceInformation referenceInformationIndicator="">
<pidx:ReferenceNumber/>
<pidx:Description/>
</pidx:ReferenceInformation>
<pidx:Comment/>
</pidx:PipelineNominationCreateLineItem>
</pidx:PipelineNominationCreateDetails>
<pidx:PipelineNominationCreateSummary>
<pidx:TotalLineItems>1 </pidx:TotalLineItems>
</pidx:PipelineNominationCreateSummary>
</pidx:PipelineNominationCreate>
Any help would be appreciated.
Thanks!
You have defined xsi:schemaLocation in your XSLT document, but nowhere in your XSLT do you attempt to create it in the XML document. It will not automatically be added to your output document. You will need to create it.
You can copy the xsi:schemaLocation attribute from the XSLT document like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns="http://www.api.org/pidXML/v1.2"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:pidx="http://www.api.org/pidXML/v1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.api.org/pidXML/v1.2 https://www.transport4.com/xml/schemas/PipelineNominationCreateV1-3.xsd">
<xsl:template match="/">
<xsl:element name="pidx:PipelineNominationCreate">
<xsl:copy-of select="document('')/*/#xsi:schemaLocation"/>
Have you tried adding/copying the xsi:schemaLocation attribute explicitely in the XSLT, like you would add/copy any other attribute? For example, after the line
<xsl:element name="pidx:PipelineNominationCreate">
try adding this:
<xsl:copy-of select="/node()/#*[local-name()='schemaLocation']"/>
This should copy any schemaLocation attributes (regardless of their namespace prefix) from the root node of the original document.
Like other people have said, the attribute isn't present in the output because you make no attempt to put it there.
Were you aware that your code could be much less verbose if you use literal result elements? For example, this:
<xsl:element name="pidx:PipelineNominationCreate">
<xsl:attribute name="pidx:documentIdentifier"><xsl:value-of select="//TECHNICAL_KEY"/></xsl:attribute>
<xsl:attribute name="pidx:transactionPurposeIndicator"><xsl:value-of select="//TRANSPURIND"/></xsl:attribute>
<xsl:attribute name="pidx:version"><xsl:value-of select="//VERSION"/></xsl:attribute>
<xsl:element name="pidx:PipelineNominationCreateProperties">
<xsl:element name="pidx:CreatedByPartner">
<xsl:element name="pidx:PartnerInformation">
<xsl:attribute name="partnerRoleIndicator"><xsl:value-of select="//PARTROLEIND"/></xsl:attribute>
<xsl:element name="pidx:PartnerIdentifier">
<xsl:attribute name="partnerIdentifierIndicator"><xsl:value-of select="//PART_IDENTIFIER"/></xsl:attribute>
<xsl:value-of select="//SHIPPARTID"/>
</xsl:element>
</xsl:element>
<xsl:element name="pidx:EventDateTime">
<xsl:value-of select="//CREATE_DATE_TIME"/>
</xsl:element>
</xsl:element>
can be rewritten like this:
<pidx:PipelineNominationCreate
pidx:documentIdentifier="{//TECHNICAL_KEY}"
pidx:transactionPurposeIndicator="{//TRANSPURIND}"
pidx:version="{//VERSION}">
<pidx:PipelineNominationCreateProperties>
<pidx:CreatedByPartner>
<pidx:PartnerInformation partnerRoleIndicator="{//PARTROLEIND}">
<pidx:PartnerIdentifier
partnerIdentifierIndicator="{//PART_IDENTIFIER}">
<xsl:value-of select="//SHIPPARTID"/>
</pidx:PartnerIdentifier>
</pidx:PartnerInformation>
<pidx:EventDateTime>
<xsl:value-of select="//CREATE_DATE_TIME"/>
</pidx:EventDateTime>
which is so much more readable.

Why would the XSLT to update Web.Config partly fail?

I have an XSLT file that is used to write to my Web.Config as part of a build process for Umbraco. Writing things such as the "umbracoConfigurationStatus" value works fine, as does writing a new profile membership section.
However the part that tries to update or create a UmbracoMembershipProvider section is causing me all sorts of grief. It won't update or write. What am I missing?
Here's the bulk of the XSLT file:
<xsl:template match="/configuration/appSettings/add[#key='umbracoConfigurationStatus']/#value">
<xsl:attribute name="value">4.7.1.1</xsl:attribute>
</xsl:template>
<xsl:template match="/configuration/system.web">
<xsl:copy>
<xsl:call-template name="copy-children" />
<xsl:element name="profile">
<xsl:attribute name="defaultProvider">
<xsl:text>UmbracoMemberProfileProvider</xsl:text>
</xsl:attribute>
<xsl:attribute name="enabled">
<xsl:text>true</xsl:text>
</xsl:attribute>
<xsl:attribute name="inherits">
<xsl:text>umbraco.cms.businesslogic.member.MemberProfile</xsl:text>
</xsl:attribute>
<xsl:element name="providers">
<xsl:element name="clear" />
<xsl:element name="add">
<xsl:attribute name="name">
<xsl:text>UmbracoMemberProfileProvider</xsl:text>
</xsl:attribute>
<xsl:attribute name="type">
<xsl:text>umbraco.providers.members.UmbracoProfileProvider, umbraco.providers</xsl:text>
</xsl:attribute>
</xsl:element>
</xsl:element>
<xsl:element name="properties">
<xsl:element name="clear" />
<xsl:element name="add">
<xsl:attribute name="name">
<xsl:text>auth_guid</xsl:text>
</xsl:attribute>
<xsl:attribute name="allowAnonymous">
<xsl:text>false</xsl:text>
</xsl:attribute>
<xsl:attribute name="provider">
<xsl:text>UmbracoMemberProfileProvider</xsl:text>
</xsl:attribute>
<xsl:attribute name="type">
<xsl:text>System.String</xsl:text>
</xsl:attribute>
</xsl:element>
<xsl:element name="add">
<xsl:attribute name="name">
<xsl:text>firstName</xsl:text>
</xsl:attribute>
<xsl:attribute name="allowAnonymous">
<xsl:text>false</xsl:text>
</xsl:attribute>
<xsl:attribute name="provider">
<xsl:text>UmbracoMemberProfileProvider</xsl:text>
</xsl:attribute>
<xsl:attribute name="type">
<xsl:text>System.String</xsl:text>
</xsl:attribute>
</xsl:element>
<xsl:element name="add">
<xsl:attribute name="name">
<xsl:text>lastName</xsl:text>
</xsl:attribute>
<xsl:attribute name="allowAnonymous">
<xsl:text>false</xsl:text>
</xsl:attribute>
<xsl:attribute name="provider">
<xsl:text>UmbracoMemberProfileProvider</xsl:text>
</xsl:attribute>
<xsl:attribute name="type">
<xsl:text>System.String</xsl:text>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:copy>
</xsl:template>
<xsl:template name="copy-children">
<xsl:copy-of select="./*"/>
</xsl:template>
<xsl:template match="/configuration/system.web/membership/providers">
<xsl:copy>
<xsl:call-template name="copy-children" />
<xsl:element name="add">
<xsl:attribute name="name">
<xsl:text>UmbracoMembershipProvider</xsl:text>
</xsl:attribute>
<xsl:attribute name="type">
<xsl:text>umbraco.providers.members.UmbracoMembershipProvider</xsl:text>
</xsl:attribute>
<xsl:attribute name="enablePasswordRetrieval">
<xsl:text>false</xsl:text>
</xsl:attribute>
<xsl:attribute name="enablePasswordReset">
<xsl:text>false</xsl:text>
</xsl:attribute>
<xsl:attribute name="requiresQuestionAndAnswer">
<xsl:text>false</xsl:text>
</xsl:attribute>
<xsl:attribute name="defaultMemberTypeAlias">
<xsl:text>SiteMember</xsl:text>
</xsl:attribute>
<xsl:attribute name="umbracoApprovePropertyTypeAlias">
<xsl:text>isApproved</xsl:text>
</xsl:attribute>
<xsl:attribute name="umbracoLockPropertyTypeAlias">
<xsl:text>isLocked</xsl:text>
</xsl:attribute>
<xsl:attribute name="passwordFormat">
<xsl:text>Hashed</xsl:text>
</xsl:attribute>
</xsl:element>
</xsl:copy>
</xsl:template>
<!-- Default templates to match anything else -->
<xsl:template match="#*">
<xsl:copy/>
</xsl:template>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
The state I'm trying to get to is:
<add name="UmbracoMembershipProvider" type="umbraco.providers.members.UmbracoMembershipProvider" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" defaultMemberTypeAlias="SiteMember" umbracoApprovePropertyTypeAlias="isApproved" umbracoLockPropertyTypeAlias="isLocked" passwordFormat="Hashed" />
I think this because you have a template to match the configuration/system.web element...
<xsl:template match="/configuration/system.web">
... but within this, you do not tell the XSLT processor to continue matching any templates against the descendants. This means, the template for configuration/system.web/membership/providers won't be matched.
You do call the named template copy-children though, but all this does is an xsl:copy-of which will copy the children nodes exactly, but won't match any templates that may exist for them.
Change the copy-children template to the following, and see how you get on.
<xsl:template name="copy-children">
<xsl:apply-templates select="*"/>
</xsl:template>
As a side note, there is no real need to use xsl:element and xsl:attribute to create elements and attributes if you are always using fixed names. For example
<xsl:element name="add">
<xsl:attribute name="name">
<xsl:text>UmbracoMembershipProvider</xsl:text>
</xsl:attribute>
<xsl:element>
This could simply be replaced with the following, which could cut down the size of your XSLT
<add name="UmbracoMembershipProvider" />
EDIT: As well as changing the copy-children template as described above, another issue you may have is with this matching template.
<xsl:template match="/configuration/system.web/membership">
<xsl:attribute name="MyAttrTest">the test value</xsl:attribute>
</xsl:template>
Here you are matching an element, but replacing it with an attribute. This is fine providing (as described in the error message) you have not added text or child elements previously. I suspect you should be doing this instead....
<xsl:template match="/configuration/system.web/membership">
<xsl:copy>
<xsl:attribute name="MyAttrTest">the test value</xsl:attribute>
</xsl:copy>
</xsl:template>
This will add create a copy of the membership element and add an attribute to it.
You should double-check all instances of where this may be occurring in your XSLT.

XSLT blank xmlns="" after transform

I'm using an XSLT to transform from one XML standard to another. The particular resulting XML standard contains a root element which is part of a namespace and a child node which is part of another namepsace.
The transform successfully reflects these namespaces but the child's child now contains a blank xmlns attribute. How can I prevent this xmlns=""?
XSLT Snippet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:apply-templates select="REQUEST_GROUP" />
</xsl:template>
<xsl:template match="REQUEST_GROUP">
<ONCORE_ERECORD xmlns="http://test.com">
<xsl:apply-templates select="REQUEST/PRIA_REQUEST/PACKAGE"/>
<PAYMENT PaymentType="ACH" />
<TRANSACTION_INFO _AgentKey="" _AgentPassword="" />
</ONCORE_ERECORD>
</xsl:template>
<xsl:template match="PACKAGE">
<DOCUMENT_RECORDATION xmlns="http://test2.org">
<xsl:apply-templates select="PRIA_DOCUMENT"/>
</DOCUMENT_RECORDATION>
</xsl:template>
<xsl:template match="PRIA_DOCUMENT">
<PRIA_DOCUMENT _PRIAVersion="1.2">
<xsl:attribute name="_Type">
<xsl:value-of select="#RecordableDocumentType"/>
</xsl:attribute>
<xsl:attribute name="_Code"/>
<xsl:apply-templates select="GRANTOR" />
<xsl:apply-templates select="GRANTEE" />
<xsl:choose>
<xsl:when test="count(PROPERTY) = 0">
<PROPERTY>
<xsl:attribute name="_StreetAddress">
<xsl:value-of select="#StreetAddress"/>
</xsl:attribute>
<xsl:attribute name="_StreetAddress2">
<xsl:value-of select="#StreetAddress2"/>
</xsl:attribute>
<xsl:attribute name="_City">
<xsl:value-of select="#City"/>
</xsl:attribute>
<xsl:attribute name="_State">
<xsl:value-of select="#State"/>
</xsl:attribute>
<xsl:attribute name="_PostalCode">
<xsl:value-of select="#PostalCode"/>
</xsl:attribute>
<xsl:attribute name="_County">
<xsl:value-of select="#County"/>
</xsl:attribute>
<xsl:apply-templates select="LEGAL_DESCRIPTION"/>
</PROPERTY>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="PROPERTY" />
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="count(PARTIES) = 0">
<PARTIES>
<_RETURN_TO_PARTY _UnparsedName="" _StreetAddress="" _StreetAddress2="" _City="" _State="" _PostalCode="" />
</PARTIES>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="PARTIES" />
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates select="EXECUTION" />
<xsl:apply-templates select="CONSIDERATION" />
<xsl:apply-templates select="RECORDABLE_DOCUMENT/_ASSOCIATED_DOCUMENT" />
<xsl:apply-templates select="EMBEDDED_FILE" />
</PRIA_DOCUMENT>
Source XML:
<REQUEST_GROUP PRIAVersionIdentifier="2.4">
<REQUEST>
<PRIA_REQUEST _Type="RecordDocuments">
<PACKAGE>
<PRIA_DOCUMENT PRIAVersionIdentifier="2.4" RecordableDocumentSequenceIdentifier="1" RecordableDocumentType="Mortgage">
Resulting XML:
<?xml version="1.0" encoding="utf-8"?>
<ONCORE_ERECORD xmlns="http://test.com">
<DOCUMENT_RECORDATION xmlns="http://test2.org">
<PRIA_DOCUMENT _PRIAVersion="1.2" _Type="Mortgage" _Code="" xmlns="">
This is happening because PRIA_DOCUMENT is in the default namespace, while its parent DOCUMENT_RECORDATION is in a non-default namespace. You must put the PRIA_DOCUMENT in the same namespace as its parent, otherwise the serializer is required to generate xmlns="".
.
.
<xsl:template match="PRIA_DOCUMENT">
<PRIA_DOCUMENT _PRIAVersion="1.2" xmlns="http://pria.org">
.
.
.
See Michael Kay's "XSLT 2.0 and XPATH 2.0, 4th edition", page 475 where he discusses this exact situation.
I found a solution that worked, though it may not have been the most efficient way to achieve the desired results.
I simply changed all literal element declarations to:
</xsl:element>
and declared the namespace. The resulting xslt is as follows:
<xsl:template match="REQUEST_GROUP">
<xsl:element name="ONCORE_ERECORD" namespace="http://test.com">
<xsl:apply-templates select="REQUEST/PRIA_REQUEST/PACKAGE"/>
<xsl:element name="PAYMENT" namespace="http://test.com">
<xsl:attribute name="PaymentType">
<xsl:value-of select="'ACH'"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="TRANSACTION_INFO" namespace="http://test.com">
<xsl:attribute name="_AgentKey">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_AgentPassword">
<xsl:value-of select="''"/>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="PACKAGE">
<xsl:element name="DOCUMENT_RECORDATION" namespace="http://test2.org">
<xsl:apply-templates select="PRIA_DOCUMENT"/>
</xsl:element>
</xsl:template>
<xsl:template match="PRIA_DOCUMENT">
<xsl:element name="PRIA_DOCUMENT" namespace="http://test2.org">
<xsl:attribute name="_PRIAVersion">
<xsl:value-of select="'1.2'"/>
</xsl:attribute>
<xsl:attribute name="_Type">
<xsl:value-of select="#RecordableDocumentType"/>
</xsl:attribute>
<xsl:attribute name="_Code"/>
<xsl:apply-templates select="GRANTOR" />
<xsl:apply-templates select="GRANTEE" />
<xsl:choose>
<xsl:when test="count(PROPERTY) = 0">
<xsl:element name="PROPERTY" namespace="http://test2.org">
<xsl:attribute name="_StreetAddress">
<xsl:value-of select="#StreetAddress"/>
</xsl:attribute>
<xsl:attribute name="_StreetAddress2">
<xsl:value-of select="#StreetAddress2"/>
</xsl:attribute>
<xsl:attribute name="_City">
<xsl:value-of select="#City"/>
</xsl:attribute>
<xsl:attribute name="_State">
<xsl:value-of select="#State"/>
</xsl:attribute>
<xsl:attribute name="_PostalCode">
<xsl:value-of select="#PostalCode"/>
</xsl:attribute>
<xsl:attribute name="_County">
<xsl:value-of select="#County"/>
</xsl:attribute>
<xsl:apply-templates select="LEGAL_DESCRIPTION"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="PROPERTY" />
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="count(PARTIES) = 0">
<xsl:element name="PARTIES" namespace="http://test2.org">
<xsl:element name="_RETURN_TO_PARTY" namespace="http://test2.org">
<xsl:attribute name="_UnparseName">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_StreetAddress">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_StreetAddress2">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_City">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_State">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_PostalCode">
<xsl:value-of select="''"/>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="PARTIES" />
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates select="EXECUTION" />
<xsl:apply-templates select="CONSIDERATION" />
<xsl:apply-templates select="RECORDABLE_DOCUMENT/_ASSOCIATED_DOCUMENT" />
<xsl:apply-templates select="EMBEDDED_FILE" />
</xsl:element>
</xsl:template>
I was having a similar issue even declaring the namespace on the child elements but was still ending up with
xmlns=""
I thought it was due to the xslt transformation but the string result of the transform was correct and it was when I then transformed the string to a org.w3c.dom.Document that the default namespaces were being added.
Making the DocumentBuilderFactory namespace aware fixed this
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document metadataDOM = db.parse(new ByteArrayInputStream(stringWriter.toString().getBytes()));
Put the calling template and the applied template in the same namespace.
You're re-defining the default namespace with each of those nodes with the 'xmlns=' declaration. Because the PRIA_DOCUMENT doesn't have a namespace, the output needs to redeclare it as empty, or it would have the same namespace as it's parent. I'd recommend adding in a named namespace to those elements that have one defined, for example:
<pria:DOCUMENT_RECORDATION xmlns:pria="http://pria.org">
and
<as:ONCORE_ERECORD xmlns:as="http://aptitudesolutions.com">
With these named namespaces in place, the blank declaration on the PRIA_DOCUMENT element becomes unnecessary, and is not added.