Side-effect of xsl:sort on indentation - xslt-1.0

I need to sort elements BillOfMaterialIem based on value of attribute billOfMaterialItemID
Example :
<?xml version="1.0" encoding="UTF-8"?>
<PackageData documentCreateDate="2019-10-03" documentModificationDate="2019-10-03">
<Items>
<Item itemID="416664">
<BillOfMaterial>
<BillOfMaterialItem billOfMaterialItemID="417230" />
<BillOfMaterialItem billOfMaterialItemID="417231" />
<BillOfMaterialItem billOfMaterialItemID="416664-01"/>
<BillOfMaterialItem billOfMaterialItemID="110424" />
</BillOfMaterial>
</Item>
</Items>
</PackageData>
This is ok to copy everything and filter empty atttributes :
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()">
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="#*">
<xsl:if test="string-length(.)!=0">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:template>
Here is the template specific to element BillOfMaterial :
<xsl:template match="BillOfMaterial">
<xsl:copy>
<xsl:apply-templates select="BillOfMaterialItem" >
<xsl:sort select="#billOfMaterialItemID"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
Elements are sorted as expected but identation is killed in the output - despite indent="yes".
I don't get the reason for this side-effect.
What do I miss ?

<xsl:strip-space elements="*"/> makes it work
Thank you, michael.hor257k

Related

XSLT 1.0 Move of one Segment

please tell me, how can i move the Segment "BGM" from "CNT" to "GRP"?
Actually XML:
<?xml version="1.0"?>
<SEEDELFOR>
<AG/>
<CNT>
<TRANSMISSION_DATE>20190307</TRANSMISSION_DATE>
<TRANSMISSION_TIME>113300</TRANSMISSION_TIME> </INTERCHANGE_CONTROL_NUMBER>
</SENDER>
</SENDER_QUALIFIER>
</RECEIVER>
</RECEIVER_QUALIFIER>
</SYNTAX_IDENTIFIER>
</SYNTAX_VERSION>
<BGM>227</BGM>
<GRP>
</IDENTIFIER_BY>
</IDENTIFIER_SU>
</DATE_4>
</REF_ON>
</GRP>
</CNT>
</SEEDELFOR>
Correct output of my XML Should be:
<?xml version="1.0"?>
<SEEDELFOR>
<AG/>
<CNT>
<TRANSMISSION_DATE>20190307</TRANSMISSION_DATE>
<TRANSMISSION_TIME>113300</TRANSMISSION_TIME>
<SENDER></SENDER>
</SENDER_QUALIFIER>
</RECEIVER>
</RECEIVER_QUALIFIER>
</SYNTAX_IDENTIFIER>
</SYNTAX_VERSION>
<GRP>
<BGM>227</BGM>
</GRP>
</CNT>
</SEEDELFOR>
Here´s my XSLT: I tried to copy, but i think it´s better to move the segment?
Edit: Thats my full XSLT: Maybe i have to cominate with other copy segments?
<?xml version="1.0" encoding="utf-8"?>
<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:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!--Copy all of CNT != BGM-->
<xsl:template match="CNT">
<xsl:copy>
<xsl:apply-templates select="*[local-name()!='BGM']" />
</xsl:copy>
</xsl:template>
<!--copy BGM from CNT to GRP-->
<xsl:template match="GRP">
<xsl:element name="GRP">
<xsl:apply-templates />
<xsl:copy-of select="../BGM" />
</xsl:element>
</xsl:template>
<!--copy the data from ADD/CONTACT with Qualifier EM to GRP, so it can be used in the mapping to set EMAIL from Customer-->
<xsl:template match="GRP">
<xsl:copy>
<xsl:for-each select ="./ADD/CONTACT">
<xsl:if test="./QUALIFIER='EM'">
<CONTACT_EMAIL>
<xsl:value-of select="NUMBER"/>
</CONTACT_EMAIL>
</xsl:if>
<xsl:if test="./QUALIFIER='TE'">
<CONTACT_TEL>
<xsl:value-of select="NUMBER"/>
</CONTACT_TEL>
</xsl:if>
<xsl:if test="./QUALIFIER='FX'">
<CONTACT_FAX>
<xsl:value-of select="NUMBER"/>
</CONTACT_FAX>
</xsl:if>
</xsl:for-each>
<!--copy all other nodes-->
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!--delete IC node-->
<xsl:template match="IC" />
<!--delete FILTER_SET node-->
<xsl:template match="FILTER_SET" />
<!--delete SEE_AG node-->
<xsl:template match="SEE_AG" />
<!--delete ME node-->
<xsl:template match="ME" />
<!--delete ADD node-->
<xsl:template match="ADD" />
<!-- delete segment (ADD) with specified QUALIFER (SU) -->
<xsl:template match="ADD[QUALIFIER='SU']"/>
<!--delete TRANSPORT_DETAILS node-->
<xsl:template match="TRANSPORT_DETAILS" />
<!--delete PACKAGE_DETAILS node-->
<xsl:template match="PACKAGE_DETAILS" />
<!--delete AMOUNT_DETAILS node-->
<xsl:template match="AMOUNT_DETAILS" />
<!--delete IC node-->
<xsl:template match="CONTACT" />
<!-- delete empty nodes -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(#*|*|comment()|processing-instruction()) and normalize-space()='']"/>
<!-- delete empty nodes -->
</xsl:stylesheet>
Thanks for your help.
Best regards
Julian
The idiomatic approach to "moving" a node from one place to another is:
copy everything as is,
except the node in question, and
copy the node to its new place:
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:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- remove BGM -->
<xsl:template match="BGM"/>
<xsl:template match="GRP">
<xsl:copy>
<xsl:apply-templates/>
<!-- add BGM -->
<xsl:copy-of select="../BGM" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If you need to modify other nodes, add templates matching them as necessary.
Seems your XML is not well formed. If I have got it right then you can find below xslt to achieve your requirement:
XML can be:
<?xml version="1.0"?>
<SEEDELFOR>
<AG />
<CNT>
<TRANSMISSION_DATE>20190307</TRANSMISSION_DATE>
<TRANSMISSION_TIME>113300</TRANSMISSION_TIME>
<INTERCHANGE_CONTROL_NUMBER />
<SENDER />
<SENDER_QUALIFIER />
<RECEIVER />
<RECEIVER_QUALIFIER />
<SYNTAX_IDENTIFIER />
<SYNTAX_VERSION />
<BGM>227</BGM>
<GRP>
<IDENTIFIER_BY />
<IDENTIFIER_SU />
<DATE_4 />
<REF_ON />
</GRP>
</CNT>
</SEEDELFOR>
An the solution would be:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="CNT">
<xsl:copy>
<xsl:apply-templates select="*[local-name()!='BGM']" />
</xsl:copy>
</xsl:template>
<xsl:template match="GRP">
<xsl:element name="GRP">
<xsl:apply-templates />
<xsl:copy-of select="../BGM" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
http://xsltransform.net/eieE3Q5
<!-- remove BGM at CNT-->
<xsl:template match="BGM"/>
<!--copy the data from ADD/CONTACT with Qualifier EM to GRP, so it can be used in the mapping to set EMAIL from Customer-->
<xsl:template match="GRP">
<xsl:copy>
<xsl:for-each select ="./ADD/CONTACT">
<xsl:if test="./QUALIFIER='EM'">
<CONTACT_EMAIL>
<xsl:value-of select="NUMBER"/>
</CONTACT_EMAIL>
</xsl:if>
<xsl:if test="./QUALIFIER='TE'">
<CONTACT_TEL>
<xsl:value-of select="NUMBER"/>
</CONTACT_TEL>
</xsl:if>
<xsl:if test="./QUALIFIER='FX'">
<CONTACT_FAX>
<xsl:value-of select="NUMBER"/>
</CONTACT_FAX>
</xsl:if>
</xsl:for-each>
<!--copy BGM from CNT to GRP -->
<xsl:copy-of select="../BGM" />
<!--copy all other nodes-->
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>

How to make element with <xsl:attribute> when using <sql:query>

I am using saxonee-sql to connect to a database. How do I make the output xml file like this:
<Partners>
<Partner type="Supplier">
<PartnerName>name 1</PartnerName>
<PartnerDuns>duns 1</PartnerDuns>
</Partner>
<Partner type="Buyer">
<PartnerName>name 2</PartnerName>
<PartnerDuns>dums 2</PartnerDuns>
</Partner>
</Partners>
And this is the xslt:
<xsl:variable name="partner">
<sql:query connection="$connection" table="Partner" column="PartnerDuns,PartnerName,type" row-tag="Partner" />
</xsl:variable>
<Partners>
<xsl:copy-of select="$partner" />
</Partners>
The current output is
<Partners>
<Partner>
<PartnerName>name 1</PartnerName>
<PartnerDuns>duns 1</PartnerDuns>
<type>Supplier</type>
</Partner>
<Partner>
<PartnerName>name 2</PartnerName>
<PartnerDuns>dums 2</PartnerDuns>
<type>Buyer</type>
</Partner>
</Partners>
I suppose you can simply transform the original result with e.g.
<xsl:template match="Partner/*">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="Partner/type">
<xsl:attribute name="{name()}" select="."/>
</xsl:template>
<xsl:template match="Partner">
<xsl:copy>
<xsl:apply-templates select="type, (* except type)"/>
</xsl:copy>
</xsl:template>
and then use
<xsl:variable name="partner">
<sql:query connection="$connection" table="Partner" column="PartnerDuns,PartnerName,type" row-tag="Partner" />
</xsl:variable>
<Partners>
<xsl:apply-templates select="$partner/*" />
</Partners>
instead of the copy-of.

XSLT code for converting attributes to child elements and putting the value of current element into its another new child element

I am unaware of XSLT,Please help:
I have the following XML:
<OLifE>
<Holding id="1234">
<HoldingKey>1397650618090</HoldingKey>
<HoldingTypeCode tc="2">Policy</HoldingTypeCode>
<HoldingStatus tc="2">Inactive</HoldingStatus>
<CarrierAdminSystem>PAS</CarrierAdminSystem>
</Holding>
</OLifE>
I want the output like this:
<OLifE>
<Holding>
<id>1234</id>
<HoldingKey>1397650618090</HoldingKey>
<HoldingTypeCode>
<tc>2</tc>
<value>Policy</value>
</HoldingTypeCode>
<HoldingStatus>
<tc>2</tc>
<value>Inactive</value>
</HoldingStatus>
<CarrierAdminSystem>PAS</CarrierAdminSystem>
</Holding>
</OLifE>
please note that, all attributes are needed to be converted to child elements but, for elements that have a tc attribute specified:
<HoldingTypeCode tc="2">Policy</HoldingTypeCode>
need to be handled carefully.
I DO NOT want the output like:
<HoldingTypeCode>
<tc>2</tc>Policy</HoldingTypeCode>
I have the following XSLT code which needs the slight modification:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml"/>
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:for-each select="#*">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
Please help.
Assuming you want a generic solution, how about:
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:strip-space elements="*"/>
<!-- modified identity transform -->
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!-- attributes to elements -->
<xsl:template match="#*">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<!-- avoid mixed content -->
<xsl:template match="text()[../#*]">
<value>
<xsl:value-of select="."/>
</value>
</xsl:template>
</xsl:stylesheet>
Edit
However, the root element in my original XML has got xmlns="some web
address" attribute.
In such case, change the first template to:
<!-- modified identity transform -->
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
This is assuming you want all the output nodes to be in no namespace.
I think you can define a template like this to be called where you want to output the attributes :
<xsl:template name="transform.attrs">
<xsl:for-each select="#*">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:template>
For the most general case you use almost the same template as before, just call the new template for attributes:
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:call-template name="transform.attrs">
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
And for the specific elements, bearing the #tc attribute:
<xsl:template match="*[#tc]">
<xsl:element name="{name()}">
<xsl:call-template name="transform.attrs" />
<value>
<xsl:apply-templates select="*|text()"/>
</value>
</xsl:element>
</xsl:template>

Copy a node from parent to child

I would like to copy a parent node into the child but I'm unsure on how to proceed.
I apologize in advanced for the weird looking source file, I'm new to this forum and have no clue on how to properly paste an XML file.
My XML source file is like this:
<?xml version="1.0"?>
<IncidentLogUpload>
<Header>
<BatchID>2013</BatchID>
<SystemID>2013</SystemID>
<DateTime>12/20/2013 3:37 PM</DateTime>
</Header>
<Item>
<IncidentLogs>
<IncidentLog>
<IncidentSource>Source</IncidentSource>
<Property>Property</Property>
<Location>B1</Location>
<SubLocation/>
<DailyLogID>IN2013</DailyLogID>
<IncidentID>IN2013</IncidentID>
<Reference/>
<DateTimeOccured>12/19/2013 8:17 PM</DateTimeOccured>
<IncidentType>Surveillance</IncidentType>
<Specific>Observation</Specific>
<Category>POI</Category>
<IncidentDetails>0400</IncidentDetails>
<RelatedIncidentNo/>
<DateTimeReported>12/19/2013 8:17 PM</DateTimeReported>
<ParticipantSubjectProfiles>
<ParticipantSubjectProfile>
<FirstName>James</FirstName>
<MiddleName></MiddleName>
<LastName>Henderson</LastName>
<ParticipantType>Subject</ParticipantType>
<MembershipNumber></MembershipNumber>
<DriversLicense></DriversLicense>
<PassportNumber></PassportNumber>
<IncidentID>IN2013</IncidentID>
</ParticipantSubjectProfile>
</ParticipantSubjectProfiles>
<ParticipantPersonnelProfiles>
<ParticipantPersonnelProfile>
<BusinessUnit>Games</BusinessUnit>
<FirstName>Edison</FirstName>
<MiddleName>John</MiddleName>
<LastName>Costabile</LastName>
<CSELNumber/>
<StaffID>000408</StaffID>
<DriversLicense/>
<AffBUKey>GamesIN2013</AffBUKey>
<ParticipantType>Personnel</ParticipantType>
</ParticipantPersonnelProfile>
</ParticipantPersonnelProfiles>
</IncidentLog>
</IncidentLogs>
</Item>
<Footer>
<NumberOfRecords>5</NumberOfRecords>
</Footer>
</IncidentLogUpload>
I would like to copy the <Property> node to both <ParticipantSubjectProfile> and <ParticipantPersonnelProfile>. The end result should be like this:
<ParticipantSubjectProfiles>
<ParticipantSubjectProfile>
<FirstName>James</FirstName>
<MiddleName></MiddleName>
<LastName>Henderson</LastName>
<ParticipantType>Subject</ParticipantType>
<MembershipNumber></MembershipNumber>
<DriversLicense></DriversLicense>
<PassportNumber></PassportNumber>
<IncidentID>IN2013</IncidentID>
<Property>Property</Property>
</ParticipantSubjectProfile>
</ParticipantSubjectProfiles>
<ParticipantPersonnelProfiles>
<ParticipantPersonnelProfile>
<BusinessUnit>Games</BusinessUnit>
<FirstName>Edison</FirstName>
<MiddleName>John</MiddleName>
<LastName>Costabile</LastName>
<CSELNumber/>
<StaffID>000408</StaffID>
<DriversLicense/>
<AffBUKey>GamesIN2013</AffBUKey>
<ParticipantType>Personnel</ParticipantType>
<Property>Property</Property>
</ParticipantPersonnelProfile>
</ParticipantPersonnelProfiles>
Please help! Thank you!
Edited
<xsl:template match="/">
<ParticipantSubjectProfiles>
<xsl:for-each select="IncidentLogUpload/Item/IncidentLogs/IncidentLog/ParticipantSubjectProfiles">
<ParticipantSubjectProfile>
<FirstName>James</FirstName>
<MiddleName></MiddleName>
<LastName>Henderson</LastName>
<ParticipantType>Subject</ParticipantType>
<MembershipNumber></MembershipNumber>
<DriversLicense></DriversLicense>
<PassportNumber></PassportNumber>
<IncidentID>IN2013</IncidentID>
<Property> <xsl:value-of select="/IncidentLogUpload/Item/IncidentLogs/IncidentLog/Property"/></Property>
</ParticipantSubjectProfile>
</xsl:for-each>
</ParticipantSubjectProfiles>
</xsl:template>
</xsl:stylesheet>
Try this template out:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<ParticipantSubjectProfiles>
<xsl:for-each select="//ParticipantSubjectProfiles/ParticipantSubjectProfile">
<xsl:copy>
<xsl:apply-templates/>
<xsl:apply-templates select="../preceding-sibling::Property"></xsl:apply-templates>
</xsl:copy>
</xsl:for-each>
</ParticipantSubjectProfiles>
<ParticipantPersonnelProfiles>
<xsl:for-each select="//ParticipantPersonnelProfiles/ParticipantPersonnelProfile">
<xsl:copy>
<xsl:apply-templates/>
<xsl:apply-templates select="../preceding-sibling::Property"></xsl:apply-templates>
</xsl:copy>
</xsl:for-each>
</ParticipantPersonnelProfiles>
</xsl:template>
</xsl:stylesheet>

How to systematically set "ReadOnly" attribute to files harvested using heat?

I am harvesting a directory using heat, however, I couldnt find an option to set the "ReadOnly" attributes for all files harvest using heat.
Does anyone have know any way of doing it in heat?
Apply an XSLT transform to the fragment generated by heat and add ReadOnly="yes" to every File element. This XSLT does the thing:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:wix="http://schemas.microsoft.com/wix/2006/wi">
<xsl:template match="wix:File">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:attribute name="ReadOnly">
<xsl:text>yes</xsl:text>
</xsl:attribute>
<xsl:apply-templates select="*" />
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:apply-templates select="* | text()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#* | text()">
<xsl:copy />
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>