XSL Transform - select node id by string content - java

I have xml like this
<mets:mets xmlns:mets="http://www.loc.gov/METS/">
<mets:fileSec>
<mets:fileGrp ID="REP1">
<mets:file ID="FL1">
<mets:FLocat LOCTYPE="URL" xlin:href="1.jpg" xmlns:xlin="http://www.w3.org/1999/xlink"/>
</mets:file>
</mets:fileGrp>
<mets:fileGrp ID="REP2">
<mets:file ID="FL2">
<mets:FLocat LOCTYPE="URL" xlin:href="1.tif" xmlns:xlin="http://www.w3.org/1999/xlink"/>
</mets:file>
</mets:fileGrp>
<mets:fileGrp ID="REP3">
<mets:file ID="FL3">
<mets:FLocat LOCTYPE="URL" xlin:href="2.jpg" xmlns:xlin="http://www.w3.org/1999/xlink"/>
</mets:file>
</mets:fileGrp>
</mets:fileSec>
</mets:mets>
I want to get as output IDs only files with jpg extention -> FL1, FL3.
I have problems with my xsl file :
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:mets="http://www.loc.gov/METS/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:mods="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-0.xsd"
exclude-result-prefixes="xlin">
<xsl:output method="html" omit-xml-declaration="yes" indent="yes" />
<xsl:template match="/">
<xsl:for-each select="//mets:fileSec/mets:fileGrp/mets:file">
<xsl:variable name="currentID" select="#ID" />
<xsl:for-each select="//mets:fileSec/mets:fileGrp/mets:file/mets:FLocat">
<xsl:variable name="testVariable" select="#xlink:href" xmlns:xlink="http://www.w3.org/1999/xlink" />
<xsl:choose>
<xsl:when test="contains($testVariable, '.jpg')"><xsl:value-of select="$currentID"/>,</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
But after transform I get error output as : FL1,FL1,FL2,FL2,FL3,FL3,
Please, help me with XSL.
Thank you!

Would something like this work for you?
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:mets="http://www.loc.gov/METS/"
xmlns:xlin="http://www.w3.org/1999/xlink">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/">
<xsl:for-each select="mets:mets/mets:fileSec/mets:fileGrp/mets:file[contains(mets:FLocat/#xlin:href, '.jpg')]">
<xsl:value-of select="#ID"/>
<xsl:if test="position()!=last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Related

Can you declare / generate xslt params dynamically?

I'm trying to generate an xml with the help of xslt templates and I have a tricky thing to do:
I have to generate a certain number of elements that have same tags but with different values inside them. Example of xslt stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="NbOfBatches"/>
<xsl:param name="wholeTag"/>
<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/">
<Document xmlns="urn:something" xmlns:file="someUrn>
<FileAppHdr>
<file:NbOfBatches><xsl:value-of select="$NbOfBatches"></xsl:value-of></file:NbOfBatches>
<xsl:call-template name="selects">
<xsl:with-param name="i">1</xsl:with-param>
<xsl:with-param name="count"><xsl:value-of select="$NbOfBatches"/></xsl:with-param>
</xsl:call-template>
</FileAppHdr>
</Document>
</xsl:template>
<xsl:template name="selects">
<xsl:param name="i" />
<xsl:param name="count" />
<xsl:if test="$i <= $count">
<xsl:call-template name="credit">
<xsl:with-param name="param0"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="$i <= $count">
<xsl:call-template name="selects">
<xsl:with-param name="i">
<xsl:value-of select="$i + 1"/>
</xsl:with-param>
<xsl:with-param name="count">
<xsl:value-of select="$count"/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="credit">
<xsl:param name="param0"/>
<CreditTransfer>
<xsl:value-of select="$param0"/>
</CreditTransfer>
</xsl:template>
</xsl:stylesheet>
In this way, I can use Saxon or JAXP and call .setParameter() for NbOfBatches, it will generate that number of tags. However is there a way to generate / modify name of param0 inside CreditTransfer so that I can loop through data in Java and use .setParameter("param1", value1) and so on ?
Thank you
Rather than trying to pass structured data as a stylesheet parameter, why not generate that structured data as XML and pass that XML document as the source document of the transformation?
As an example, imagine a source XML document with param0 in each Batch:
<Batches>
<Batch>
<param0>it was the best of times</param0>
</Batch>
<Batch>
<param0>it was the worst of times</param0>
</Batch>
<Batch>
<param0>it was the age of wisdom</param0>
</Batch>
<Batch>
<param0>it was the age of foolishness</param0>
</Batch>
</Batches>
with a simplified version of your stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/">
<Document xmlns:file="someUrn">
<FileAppHdr>
<file:NbOfBatches><xsl:value-of select="count(//Batch)"/></file:NbOfBatches>
<xsl:for-each select="//Batch">
<xsl:call-template name="selects">
<xsl:with-param name="param0" select="param0" />
</xsl:call-template>
</xsl:for-each>
</FileAppHdr>
</Document>
</xsl:template>
<xsl:template name="selects">
<xsl:param name="param0" />
<xsl:call-template name="credit">
<xsl:with-param name="param0" select="$param0" />
</xsl:call-template>
</xsl:template>
<xsl:template name="credit">
<xsl:param name="param0"/>
<CreditTransfer>
<xsl:value-of select="$param0"/>
</CreditTransfer>
</xsl:template>
</xsl:stylesheet>
produces this output:
<Document xmlns:file="someUrn">
<FileAppHdr>
<file:NbOfBatches>4</file:NbOfBatches>
<CreditTransfer>it was the best of times</CreditTransfer>
<CreditTransfer>it was the worst of times</CreditTransfer>
<CreditTransfer>it was the age of wisdom</CreditTransfer>
<CreditTransfer>it was the age of foolishness</CreditTransfer>
</FileAppHdr>
</Document>
It's fairly trivial to generate XML from structured data in Java using the Saxon sapling classes. https://www.saxonica.com/html/documentation10/javadoc/net/sf/saxon/sapling/package-summary.html

Can I get the parent nodes to copy with the child node using xslt?

I am trying to include parent nodes of the node I am copying.
Here is the sample file:
<A>
<B>
<C1>Text Value</C1>
<C2></C2>
<C3></C3>
<C4></C4>
</B>
</A>
I would like the output to be:
<A>
<B>
<C1>Text Value</C1>
</B>
</A>
Here is my xslt:
<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:template match="/A/B/C1">
<xsl:copy>
<xsl:apply-templates select="/A/B/C1"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
However, my output does not show the parent nodes.
Edit:
C1 will always have the same Xpath. I would also like to include the Text of the node.
Here's one way to produce the result you show:
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="*"/>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="*[descendant-or-self::C1] | C1/text()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Edit: C1 will always have the same Xpath.
Well, then here's another:
<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="/">
<A>
<B>
<xsl:copy-of select="A/B/C1"/>
</B>
</A>
</xsl:template>
</xsl:stylesheet>

when replace the xml elements with the help of xsl stylesheet using java,not getting replaced

how to replace the child tag name with xsl.
here the below is my structure of xml.
<Checkpax xmlns="http://xml.api.com/test">
<customerLevel>
<customerDetails>
<paxDetails>
<surname>MUKHERJEE</surname>
<type>A</type>
<gender>M</gender>
</paxDetails>
<otherPaxDetails>
<givenName>JOY</givenName>
<title>MR</title>
<age>11</age>
</otherPaxDetails>
<otherPaxDetails>
<title>MR</title>
</otherPaxDetails>
</customerDetails>
<staffDetails>
<staffInfo/>
<staffCategoryInfo>
<attributeDetails>
<attributeType>NA</attributeType>
</attributeDetails>
</staffCategoryInfo>
</staffDetails>
<productLevel>
<legLevel>
<legLevelIndicator>
<statusDetails>
<indicator>abc</indicator>
<action>1</action>
</statusDetails>
</legLevelIndicator>
</legLevel>
</productLevel>
<CustomerLevel>
<legLevel>
<legLevelIndicator>
<statusDetails>
<indicator>cde</indicator>
<action>1</action>
</statusDetails>
</legLevelIndicator>
</legLevel>
</CustomerLevel>
</customerLevel>
</Checkpax>
The below is my XSL file
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="customerLevel/productLevel/legLevel/legLevelIndicator/statusDetails">
<statusInformation>
<xsl:apply-templates select="#*|node()" />
</statusInformation>
</xsl:template>
</xsl:stylesheet>
here the statusDetails name should be changed as staffInformation inside the ProductLevel/LeglevelIndicator . Kindly give me the suggestion for doing this.
The below is expected result
<Checkpax xmlns="http://xml.api.com/test">
<customerLevel>
<customerDetails>
<paxDetails>
<surname>MUKHERJEE</surname>
<type>A</type>
<gender>M</gender>
</paxDetails>
<otherPaxDetails>
<givenName>JOY</givenName>
<title>MR</title>
<age>11</age>
</otherPaxDetails>
<otherPaxDetails>
<title>MR</title>
</otherPaxDetails>
</customerDetails>
<staffDetails>
<staffInfo/>
<staffCategoryInfo>
<attributeDetails>
<attributeType>NA</attributeType>
</attributeDetails>
</staffCategoryInfo>
</staffDetails>
<productLevel>
<legLevel>
<legLevelIndicator>
<statusInformation>
<indicator>abc</indicator>
<action>1</action>
</statusInformation>
</legLevelIndicator>
</legLevel>
</productLevel>
<CustomerLevel>
<legLevel>
<legLevelIndicator>
<statusDetails>
<indicator>cde</indicator>
<action>1</action>
</statusDetails>
</legLevelIndicator>
</legLevel>
</CustomerLevel>
</customerLevel>
</Checkpax>
the statusDetails name should be changed as staffInformation inside
the ProductLevel/LeglevelIndicator
Try it this way:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://xml.api.com/test"
exclude-result-prefixes="ns0">
<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>
<xsl:template match="ns0:productLevel/ns0:legLevel/ns0:legLevelIndicator/ns0:statusDetails">
<staffInformation xmlns="http://xml.api.com/test">
<xsl:apply-templates/>
</staffInformation>
</xsl:template>
</xsl:stylesheet>

XSLT Creating namespace dynamically

My source XML is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:DriverResponse xmlns:ns2="com/rsa/eosago/schema-1.1" xmlns:ns3="com/rsa/dkbm/schema-1.5">
<IDCheckDriver>84714718</IDCheckDriver>
<ErrorList>
<ErrorInfo>
<Code>2</Code>
<Message>Принят в обработку</Message>
</ErrorInfo>
</ErrorList>
</ns2:DriverResponse>
Wanted output:
<?xml version="1.0" encoding="UTF-8"?>
<rsa:DriverStatusRequest xmlns:rsa="com/rsa/eosago/schema-1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<InsurerID>18800000</InsurerID>
<IDCheckDriver>84714718</IDCheckDriver>
</rsa:DriverStatusRequest>
I expect this output using this XSL wich is called from Apache Camel:
<?xml version="1.0" encoding="UTF-8"?>
<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:param name="RSA-InsurerID"/>
<xsl:param name="EOSAGO-schema-version"/>
<xsl:param name="schema" select="concat('com/rsa/eosago/schema-', $EOSAGO-schema-version)"/>
<xsl:variable name="schemaUrlDummy">
<xsl:element name="ns2:dummy" namespace="{$schema}"/>
</xsl:variable>
<xsl:variable name="NS" select="ext:node-set($schemaUrlDummy)/*/namespace::ns2"/>
<xsl:template match="/">
<xsl:element name="rsa:DriverStatusRequest" namespace="com/rsa/eosago/schema-{$EOSAGO-schema-version}">
<InsurerID>
<xsl:value-of select="$RSA-InsurerID"/>
</InsurerID>
<xsl:element name="IDCheckDriver">
<xsl:copy-of select="namespace::*[not(name()='ns2')]"/>
<xsl:copy-of select="$NS"/>
<xsl:value-of select="ns2:DriverResponse/IDCheckDriver"/>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Camel settings:
<setHeader headerName="RSA-InsurerID">
<constant>18800000</constant>
</setHeader>
<setHeader headerName="EOSAGO-schema-version">
<constant>1.2</constant>
</setHeader>
<to uri="xslt:com/rsa/eosago/xsl/DriverResponse2DriverStatusRequest.xsl"/>
The problem is that i get this error stacktrace
Failed to resolve endpoint: xslt://com/rsa/eosago/xsl/DriverResponse2DriverStatusRequest.xsl due to: Namespace prefix 'ns2' is undeclared.
Failed to resolve endpoint: xslt://com/rsa/eosago/xsl/DriverResponse2DriverStatusRequest.xsl due to: Namespace prefix 'ns2' is undeclared.
javax.xml.transform.TransformerConfigurationException: Namespace prefix 'ns2' is undeclared.
Why compiler throws exception on namespace that was created dinamically?
This is very confusing. Why can't you do simply:
<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="*"/>
<xsl:param name="RSA-InsurerID"/>
<xsl:param name="EOSAGO-schema-version"/>
<xsl:template match="/*">
<xsl:element name="rsa:DriverStatusRequest" namespace="com/rsa/eosago/schema-{$EOSAGO-schema-version}">
<InsurerID>
<xsl:value-of select="$RSA-InsurerID"/>
</InsurerID>
<IDCheckDriver>
<xsl:value-of select="IDCheckDriver"/>
</IDCheckDriver>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Working demo: http://xsltransform.net/pPzifqv

Generate xpath of XML using XSLT for all Nodes

I was trying to find a way to convert the XML tags to their respective unique address like xPath. Nd I found an XSLT, where XML is processed and the unique address is created only for the nodes which doesnt have child elements and with attributes. [link]:Generate/get xpath from XML node java
XSLT :
<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:variable name="vApos">'</xsl:variable>
<xsl:template match="*[#* or not(*)] ">
<xsl:if test="not(*)">
<xsl:apply-templates select="ancestor-or-self::*" mode="path"/>
<xsl:value-of select="concat('=',$vApos,.,$vApos)"/>
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:apply-templates select="#*|*"/>
</xsl:template>
<xsl:template match="*" mode="path">
<xsl:value-of select="concat('/',name())"/>
<xsl:variable name="vnumPrecSiblings" select=
"count(preceding-sibling::*[name()=name(current())])"/>
<xsl:if test="$vnumPrecSiblings">
<xsl:value-of select="concat('[', $vnumPrecSiblings +1, ']')"/>
</xsl:if>
</xsl:template>
<xsl:template match="#*">
<xsl:apply-templates select="../ancestor-or-self::*" mode="path"/>
<xsl:value-of select="concat('[#',name(), '=',$vApos,.,$vApos,']')"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
when passed to this
<?xml version="1.0" encoding="UTF-8"?>
<root>
<main>
<tag1>001</tag1>
<tag2>002</tag2>
<tag3>
<tag4>004</tag4>
</tag3>
<tag2>002</tag2>
<tag5>005</tag5>
</main>
</root>
produces
/root/main/tag1='001' /root/main/tag2='002' /root/main/tag3/tag4='004' /root/main/tag2[2]='002' /root/main/tag5='005'
So I need the xslt to generate in the following way
/root
/root/main
/root/main/tag1
/root/main/tag2
/root/main/tag3
/root/main/tag3/tag4
/root/main/tag2[2]
/root/main/tag5
Also I dont need values. So please help me with this
Your result could be produced rather simply by:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="*">
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="name()" />
<xsl:variable name="i" select="count(preceding-sibling::*[name()=name(current())])"/>
<xsl:if test="$i">
<xsl:value-of select="concat('[', $i + 1, ']')"/>
</xsl:if>
<xsl:if test="position()!=last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:apply-templates select="*"/>
</xsl:template>
</xsl:stylesheet>
Note that this does not process attributes (or any other type of nodes other than elements).

Categories

Resources