I have some XML this looks like this:
<region class="TableInfo">
text
</region>
<region>
text
</region>
I want to write XSL that only preserves that part without the class="TableInfo".
I've tried a number of different ways, including:
<xsl:for-each select="region[class!='TableInfo']">
</xsl:for-each>
and
<xsl:for-each select="region">
<xsl:if test="not(class='TableInfo')">
</xsl:if>
</xsl:for-each>
and several variations thereof. it seems like it's somehow evaluating as a value rather than a string, because when I set it up as an != test, all the content gets deleted, and when I set it up as a not(), nothing gets deleted. any help?
thanks!
<xsl:for-each select="region[not(#class='TableInfo')]">
</xsl:for-each>
You forgot the # on class, so you were trying to check for class elements instead of the attributes. And apparently the != is not working as well, so I swapped in the not() function instead.
From a stylistic point, I would also suggest looking into using templates that match the region elements so you can use apply-templates instead of a for-each.
The identity rule is your friend (and of course, you need to specify the attribute class, not a "class" element):
<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="/*"><xsl:apply-templates/></xsl:template>
<xsl:template match="region[#class='TableInfo']"/>
</xsl:stylesheet>
When this transformation is applied on the provided XML (fragment wrapped into a single top element to make it a well-formed XML document):
<region>
text
</region>
Related
I'm dealing with a problem on how to map repeating xml elements, the moment I imported the XML below as an XML map in excel, I see only 1 for the 4 records i need "<club_LIST>" of course this doesn't produce the 4 entries in the output XML.
Any idea how can this be solved in Excel ?
I from Microsoft support:
Additionally, the contents of an XML mapping cannot be exported if the contents contain one of the following XML schema constructs:
List of lists One list of items contains a second list of items.
Any way around you could suggest to produce the xml?
Here below the sample of my data:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<PartnersProfile xmlns:xsi="http://www.w3.org/2001/xmlschema-instance">
<ID>10</ID>
<NAME>Table 10</NAME>
<Record>
<PARTNER_ID>1</PARTNER_ID>
<DESCRIPTION>customer</DESCRIPTION>
<subscription>2004</subscription>
<club_LIST>1</club_LIST>
<club_LIST>4</club_LIST>
<club_LIST>6</club_LIST>
<club_LIST>9</club_LIST>
</Record>
<Record>
<PARTNER_ID>1</PARTNER_ID>
<DESCRIPTION>customer</DESCRIPTION>
<subscription>2004</subscription>
<club_LIST>1</club_LIST>
<club_LIST>4</club_LIST>
<club_LIST>6</club_LIST>
<club_LIST>9</club_LIST>
</Record>
</PartnersProfile>
Consider transforming the XML to an itemized version by each club_LIST with repeating values for ancestor elements. You can run the below XSLT 1.0 by many tools and programming languages including Perl (from your profile) or Excel VBA.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/PartnersProfile">
<xsl:copy>
<xsl:apply-templates select="descendant::club_LIST"/>
</xsl:copy>
</xsl:template>
<xsl:template match="club_LIST">
<Record>
<xsl:copy-of select="ancestor::PartnersProfile/*[name()!='Record']"/>
<xsl:copy-of select="ancestor::Record/*[name()!='club_LIST']"/>
<club_LIST><xsl:apply-templates select="node()"/></club_LIST>
</Record>
</xsl:template>
</xsl:stylesheet>
Online Demo
I need to make XML out of response in XML format.
For some fields, I need to be able to change the value. I also need to remove all fields that are not mapped.
I get responses that look like this
<response>
<DOCUMENTS>
<DOCUMENT>
<KODA>AA</KODA>
<ITEMS>
<ITEM>B</ITEM>
<ITEM>C</ITEM>
</ITEMS>
</DOCUMENT>
</DOCUMENTS>
</response>
and want to make of it something that looks like below
<EXPORT>
<DOCUMENTS>
<DOCUMENT>
<KODDZ>SS</KODDZ>
<VALUES>
<VALUE>B</VALUE>
<VALUE>C</VALUE>
</VALUES>
</DOCUMENT>
</DOCUMENTS>
</EXPORT>
I have been trying using the XSLT example from this question but without success.
Basically what would be best for me would be a solution that takes some kind of map of fields for example:
map_of_values = {"response/DOCUMENTS/DOCUMENT/KODA":"EXPORT/DOCUMENTS/DOCUMENT/KODZA",
"response/DOCUMENTS/DOCUMENT/ITEMS/ITEM":EXPORT/DOCUMENTS/DOCUMENT/VALUES/VALUE}
What would be the best way to achieve a static map kind of solution?
In terms of XSLT you seem to want something along the lines of
<xsl:template match="response">
<EXPORT>
<xsl:apply-templates/>
</EXPORT>
</xsl:template>
<xsl:template match="KODA">
<KODDZ>
<xsl:apply-templates/>
</KODDZ>
</xsl:template>
<xsl:template match="KODA/text()[. = 'AA']">
<xsl:text>SS</xsl:text>
</xsl:template>
<xsl:template match="ITEMS">
<VALUES>
<xsl:apply-templates/>
</VALUES>
</xsl:template>
<xsl:template match="ITEM">
<VALUE>
<xsl:apply-templates/>
</VALUE>
</xsl:template>
plus the identity transformation template, of course.
DITA 3.6
Oxygen XML Editor 23.1
The "DITA for Print" book nor any other source, so far, has helped me produce a pdf where the topics are consecutive rather than having a blank page after them. To each topicref in the ditamap, I added outputclass="page-break-avoid". To each topic element in each file, I added outputclass="page-break-avoid".
Should I add something in an xsl file? Can you point me to the answer?
The blank page is generated from the definition fo:page-sequence/#force-page-number property.
To change not to generate blank-page, you can override org.dita.pdf2/cfg/fo/attrs/commons-attr.xsl.
<xsl:attribute-set name="__force__page__count">
<xsl:attribute name="force-page-count">
<xsl:choose>
<xsl:when test="/*[contains(#class, ' bookmap/bookmap ')]">
<xsl:value-of select="'even'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'auto'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:attribute-set>
Change the value even to auto will satisfy your needs.
Steps to customize
I'm not familiar with PDF2 plug-in. So this may not the standard way. But I could customize it by following steps.
Make customization XSL file
[DITA-OT]/plugins/org.dita.pdf2/Customization/fo/attrs/commons-attr.xsl
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:rx="http://www.renderx.com/XSL/Extensions"
version="2.0">
<xsl:attribute-set name="__force__page__count">
<xsl:attribute name="force-page-count">
<xsl:choose>
<xsl:when test="/*[contains(#class, ' bookmap/bookmap ')]">
<xsl:value-of select="'auto'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'auto'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:attribute-set>
</xsl:stylesheet>
Include it into [DITA-OT]/plugins/org.dita.pdf2/Customization/fo/attrs/custom.xsl
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
version="2.0">
<xsl:include href="commons-attr.xsl"/>
</xsl:stylesheet>
Customize [DITA-OT]/plugins/org.dita.pdf2/Customization/catalog.xml
<?xml version="1.0" encoding="utf-8"?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system">
<!-- Custom attributes entry -->
<uri name="cfg:fo/attrs/custom.xsl" uri="fo/attrs/custom.xsl"/>
</catalog>
Specify customization.dir property with [DITA-OT]\plugins\org.dita.pdf2\Customization in Oxygen transformation scenario.
Apply transformation scenario from Oxygen.
I've got the following temp/topic.fo. The fo:page-sequence/#force-page-count became auto and in the result PDF, the redundant blank pages are removed.
I run this XSL script thru Saxon-js. It updates a cost field on the main input XHTML using the XML received in the transform call using the stylesheetParams. All good. The problem is that no syntax checking is done on the param-XML (you can see what it looks like in the commented-out line). It is on the XHTML and the transform will generate an error but not on the param-XML. It just allows it to enter and then the key-function just doesn't update the XHTML. Is there a way to do the checking for properly formed XML parameter in the same transform call, or do I have to use 2 transform calls: call transform on the param-XSL to syntax-check, then call this main transform to update the XHTML?
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:output method="html" omit-xml-declaration="yes" encoding="UTF-8" include-content-type="no"/>
<xsl:param name="cost-data"/>
<!-- <supplier><product><key>3</key><pcost uptype="1"><key>21341</key><cost>12.99</cost></pcost></product></supplier> -->
<!-- </xsl:param> -->
<xsl:key name="cost" match="product/pcost[#uptype = 1]/cost" use="'cost' || ancestor::product/key"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="td[#name][key('cost', #name, fn:parse-xml($cost-data))]/text()">{key('cost', ../#name, fn:parse-xml($cost-data))}%</xsl:template>
<xsl:template match="/" name="xsl:initial-template">
<xsl:next-match/>
</xsl:template>
</xsl:stylesheet>
If your stylesheet code is
<xsl:param name="cost-data">
<supplier><product><key>3</key><pcost uptype="1"><key>21341</key>
<cost>12.99</cost></pcost></product></supplier>
</xsl:param>
then that's well-formed XML and no error should be reported.
However, fn:parse-xml($cost-data) is wrong. The value of the parameter is a node tree, not a string, and fn:parse-xml() expects lexical XML in a string. The effect of calling fn:parse-xml() on this node tree will be to first atomize the node, producing the untyped atomic value "32134112.99", and then attempt to parse this string "32134112.99" as lexical XML, which should fail.
To avoid confusion like this it's good practice to always declare the expected type of your parameters, for example as="xs:string" or as="document-node()".
If you want the default value of $cost-data to be a string containing lexical XML, try
<xsl:param name="cost-data" as="xs:string"><![CDATA[
<supplier><product><key>3</key><pcost uptype="1"><key>21341</key>
<cost>12.99</cost></pcost></product></supplier>
]]></xsl:param>
all on one line without whitespace (or if you need whitespace for readability, put it before a ">" delimiter).
Note: the literal answer to your question is: No. Saxon-JS doesn't perform this checking. The XML parser performs it, long before Saxon-JS gets to see the data.
I'm working on XSLT 1.0 version I have declared an int variable that have to increase when one (if condition) condition in true.
So can anyone tell me how to increament that variable or any soluton to that?
e.g.
<xsl:variable name="count" select="-1" />
is a variable declared. Then after that I'm checking one condition
<xsl:if test="$serviceP=$new">
// if condition id true the i have to increment the variable value (i.e count).
//and again have compare that count varibale value in if condition
<xsl:if test="$count < 2">
// runs when condition is true.
</xsl:if>
</xsl:if>
thanks in advance.
XSLT is a functional language and as such has immutable variables -- once assigned their contents cannot be changed.
You need to stop thinking procedurally. Explain your problem and many people will show you how to solve this problem without incrementing any variable.
For example, the solution can be as simple as this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
Latest Service:
<xsl:apply-templates select="service[#new='true'][1]"/>
New Services:
<xsl:apply-templates select="service[#new='true'][position() >1]"/>
</xsl:template>
</xsl:stylesheet>
On more complex situations, you may write a template that calls itself recursively, passing a specific parameter with value 1-greater than the value it has in the current template call.