I have a problem. I have an xml file as below :
Main.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<msrsw>
<software>
<chapter>
<name> Hello world</sample>
<xref type="xml">C:\ABC\NestedXML.xml</xref>
</chapter>
</software>
</msrsw>
NestedXml.xml :
<?xml version="1.0" encoding="ISO-8859-1"?>
<msrsw>
<software>
<chapter>
<name> Nested XML </sample>
<age>14</age>
<country>Canada</country>
</chapter>
</software>
</msrsw>
I am using Apache FOP to produce PDF document (Using Java). FOP needs src and dest params. Src being Main.xml.
My XSLT is as below:
<xsl:param name="xmlFileName" />
<xsl:param name="XMLFile" select="document($xmlFileName)"/>
<xsl:template name="Chapters_1_2_Template">
<xsl:apply-templates select="$XMLFile/*" mode="chapter" />
</xsl:template>
<xsl:template match="node()" mode="chapter">
<xsl:for-each select="node()">
<xsl:if test="current()[name() = 'xref']">
<xsl:apply-templates select="current()[name() = 'xref']" mode="x" />
</xsl:if>
<xsl:if test="current()[name() = 'name']">
<xsl:apply-templates select="current()[name() = 'name']" mode="name" />
</xsl:if>
<xsl:if test="current()[name() = 'age']">
<xsl:apply-templates select="current()[name() = 'age']" mode="age" />
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="name" mode="name">
<xsl:value-of select="current()" />
</xsl:template>
<xsl:template match="age" mode="age">
<xsl:value-of select="current()" />
</xsl:template>
<xsl:template match="country" mode="country">
<xsl:value-of select="current()" />
</xsl:template>
<xsl:template match="xref" mode="x">
<xsl:param name="XMLFile" select="document(current())"/>
<xsl:call-template name="Chapters_1_2_Template" />
</xsl:template>
When I execute the above code, i get the following error in the line <xsl:apply-templates select="$XMLFile/*" mode="chapter" />:
Invalid token XMLFile.
When I remove $XMLFile - <xsl:apply-templates select="*" mode="chapter" /> , it works fine for me with Main.xml file's content.
When I just display the value of current() in xref template, it is parses the NestedXml.xml file and displayes string values in PDF.
Expected Output is:
Hello world
Nested XML
14
Canada
My requirement is to embed the node-set of NestedXML.xml within the<xref></xref> tags and recursively applyTemplates on that node-set.
But the document() function gives me the parsed String content of NestedXml.xml.
I do not want to use the document() function and just get the String value of NestedXml.xml. I need to parse all the tags of NestedXml.xml using the same XSLT.
Please suggest me where am I going wrong. Is this approach right? Is there any other way to do this?
Or it is not possible to do in this way using XSLT and that XSLT allows including only the parsed String values?
If it is that you want just some text nodes, the stylesheet needn't be that complex. Try this simpler stylesheet:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="text" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="msrsw/software/chapter/name"/>
<xsl:apply-templates select="document(msrsw/software/chapter/xref)/*"/>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="normalize-space()"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Related
Hi I'm trying to transform an XML to another XML using xsl. The XSL transformation is working fine when tested in Oxygen developer and in VM also.
But still I'm getting an Warning like "Illegal value used for attribute name" at line number 20 and 23.
XSL which i used is:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cl="http://tyryt.com/abc/abc_order"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:java="http://xml.apache.org/xslt/java"
exclude-result-prefixes="cl xsd java" version="1.0">
<xsl:output indent="yes" method="xml" encoding="UTF-8" omit-xml-declaration="no"/>
<xsl:template name="generateInsightsHeader">
<xsl:for-each select="/">
<xsl:element name="pon">
<xsl:value-of select="*/cl:HDR/cl:PON"/>
</xsl:element>
<xsl:element name="version">
<xsl:value-of select="*/cl:HDR/cl:VER"/>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template name="copyWholeXml" match="*">
<xsl:if test="local-name() != 'brmserrorxml'">
<xsl:element name="{local-name()}">
<xsl:for-each select="#*">
<xsl:if test="../#protocoltype != ''">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:if>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:element>
</xsl:if>
</xsl:template>
<xsl:template match="/">
<IntermediateXml xmlns:json="http://json.org/">
<xsl:choose>
<xsl:when test="cl:SAMPLE_ORDER">
<xsl:call-template name="generateInsightsHeader"/>
<xsl:call-template name="copyWholeXml"/>
</xsl:when>
</xsl:choose>
</IntermediateXml>
</xsl:template>
</xsl:stylesheet>
and Sample XML i used is:
<?xml version="1.0"?>
<SAMPLE_ORDER class="1" index="0" xmlns="http://tyryt.com/abc/abc_order" service="SAMPLE_ORDER">
<header>
<interfaceid>INTERFACE_REQUIRED</interfaceid>
<actionrequired>Transmit</actionrequired>
</header>
<HDR>
<MESSAGE_ID>229364ASE</MESSAGE_ID>
<DATE>2017-09-04T12:29:11+05:30</DATE>
<PON>229364ASE10</PON>
<VER>01</VER>
</HDR>
<LOCAL_MESSAGE>
<LOCAL_ADMIN>
<LOCQTY>111</LOCQTY>
<AN>JHGKJ679868</AN>
<CC>005F</CC>
<AUTHORIZATION>
<DDD>20171228</DDD>
<ACTL>01234567899</ACTL>
</AUTHORIZATION>
</LOCAL_ADMIN>
<LSR_BILL>
<ACNA>WEE</ACNA>
</LSR_BILL>
<CONTACT>
<INIT>ASDDFAD</INIT>
<INIT_TEL_NO>2543654576</INIT_TEL_NO>
<INIT_EMAIL>abc#gmail.com</INIT_EMAIL>
</CONTACT>
</LOCAL_MESSAGE>
<LS>
<LS_SVC_DET>
<SVC_DET_GRP>
<LOCNUM>145</LOCNUM>
<LNUM>78945</LNUM>
<LNA>D</LNA>
<ECCKT>98698KJGKUJGUKJH</ECCKT>
</SVC_DET_GRP>
</LS_SVC_DET>
</LS>
</SAMPLE_ORDER>
For Transformation i'm using Xalan jar.
Please help me to solve this problem.
Thanks in Advance
The root node has no name, or more precisely, the name of the document's root node is the empty string ''. So when you call your template "copyWholeXml" and the current context node is the root node, local-name() evaluates to '', and <xsl:element name="{local-name()}"> is invalid, because you cannot generate a new XML element with an empty string as the name.
When you call your template "copyWholeXml" make sure that you are not entering the named template with the document root as the context node
need help in making subsequent element as attribute to preceding element if subsequent element contains the preceding element name using XSLT.
For below example, <emp_id> contains the preceding the element name so need to convert this element as attribute to element. can you anyone help for this?. I tried using subsequent functions in xslt but not working. Thanks in advance.
Sample XML:
<root>
<emp>test</emp>
<emp_id>1234</emp_id>
<college>something</college>
</root>
Expected out:
<root>
<emp id="1234">test</emp>
<college>something</college>
</root>
In the given example, you could do:
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:copy-of select="#*"/>
<xsl:apply-templates select="following-sibling::*[starts-with(name(), name(current()))]" mode="attr"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="*" mode="attr">
<xsl:attribute name="{substring-after(name(), '_')}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
<xsl:template match="*[preceding-sibling::*[name()=substring-before(name(current()), '_')]]"/>
</xsl:stylesheet>
to achieve the expected result.
There is probably a more elegant way, but we need to have some more rules, not just a single example.
Added:
Assuming that every element whose name contains a _ has a "parent" element whose attribute it should become, you can start by applying templates to only the "parent" elements (i.e. elements whose name does not contain a '_').
Then use a key to collect the "child" elements that need to be converted to attributes.
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:key name="atr" match="*" use="substring-before(name(), '_')" />
<xsl:template match="/root">
<xsl:copy>
<xsl:apply-templates select="*[not(contains(name(), '_'))]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:for-each select="key('atr', name())">
<xsl:attribute name="{substring-after(name(), '_')}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Apologises if duplicate. I want to perform a concat operation on the values of the xml elements.
I have xml say Input.xml
<collection>
<one>
<part>1</part>
</one>
<two>
<part>2</part>
</two>
<three>
<part>3</part>
</three>
</collection>
I want the output like :
<collection>
<one>
<part>001</part>
</one>
<two>
<part>002</part>
</two>
<three>
<part>003</part>
</three>
</collection>
how to write xslt for it
Quick hacky XSLT , tested it online it seems to work.
<?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 select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="#*|text()|comment()|processing-instruction">
</xsl:template>
<xsl:template match="//part">
<part>
<xsl:text>00</xsl:text>
<xsl:value-of select="."></xsl:value-of>
</part>
</xsl:template>
</xsl:stylesheet>
Your possible solution might be:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="part">
<xsl:value-of select="format-number(., '000')"/>
</xsl:template>
<!-- identity copy -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Format the text in part to a 3 digit number format
I have a requirement of masking few fields in XML of CDATA inside XML with XSLT.
So the resultant XML should be same like the input XML but few fileds are masked with XSLT.
I followed this link which is masking as expected but producing XML is in different format.
I tried many other solutions from SO, they are almost outputing the new XML/HTML in other format which is different from the input XML.
Please check the following example for better understading.
Input XML with CDATA content.
<XML>
<LogLevel>info</LogLevel>
<Content><![CDATA[ <Msg>
<AccountNo>2701000098983</AccountNo>
<ApplName>Testing</ApplName>
</Msg>]]></Content>
<Date>20140909</Date>
</XML>
Output XML should be:
<XML>
<LogLevel>info</LogLevel>
<Content><![CDATA[ <Msg>
<AccountNo>XXXXXXXXXX983</AccountNo>
<ApplName>Testing</ApplName>
</Msg>]]></Content>
<Date>20140909</Date>
</XML>
Edit:
I used the following XSLT
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:choose>
<xsl:when test="contains(.,'<AccountNo>')">
<!-- This is the CDATA that I want to mask and write back out as CDATA -->
<xsl:variable name="tcontent">
<xsl:value-of
select="substring-after(substring-before(.,'</AccountNo>'),'<AccountNo>') " />
</xsl:variable>
<xsl:text disable-output-escaping="yes"><![CDATA[<AccountNo></xsl:text>
<xsl:call-template name="maskVariable">
<xsl:with-param name="tvar" select="$tcontent" />
</xsl:call-template>
<xsl:text disable-output-escaping="yes"></AccountNo>]]></xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:copy />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="maskVariable">
<xsl:param name="tvar" />
<xsl:variable name="length" select="string-length($tvar)" />
<xsl:choose>
<xsl:when test="$length > 3">
<xsl:value-of
select="concat ('************', substring($tvar,$length - 1, 2))" />
</xsl:when>
<xsl:when test="$length > 1">
***
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
Output of using this XSLT is :
<LogLevel>info</LogLevel>
<Content><![CDATA[<AccountNo>************02</AccountNo>]]></Content>
<Date>20140909</Date>
Here in output, only masked output of is displaying.
How to make other part of the code to get displayed ?
Please give me some idea how to do it ?
Any help is highly appreciated.
Why don't you try:
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>
<xsl:template match="Content">
<xsl:copy>
<xsl:value-of select="substring-before(.,'<AccountNo>')" />
<xsl:text><AccountNo></xsl:text>
<xsl:variable name="acct-num" select="substring-before(substring-after(.,'<AccountNo>'), '</AccountNo>')" />
<xsl:value-of select="concat('************', substring($acct-num, string-length($acct-num) - 2))" />
<xsl:text></AccountNo></xsl:text>
<xsl:value-of select="substring-after(.,'</AccountNo>')" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Applied to your input, the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<XML>
<LogLevel>info</LogLevel>
<Content> <Msg>
<AccountNo>************983</AccountNo>
<ApplName>Testing</ApplName>
</Msg></Content>
<Date>20140909</Date>
</XML>
Alternatively, you could use:
<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" cdata-section-elements="Content"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Content">
<xsl:copy>
<xsl:variable name="content">
<xsl:value-of select="substring-before(.,'<AccountNo>')" />
<xsl:text><AccountNo></xsl:text>
<xsl:variable name="acct-num" select="substring-before(substring-after(.,'<AccountNo>'), '</AccountNo>')" />
<xsl:value-of select="concat('************', substring($acct-num, string-length($acct-num) - 2))" />
<xsl:text></AccountNo></xsl:text>
<xsl:value-of select="substring-after(.,'</AccountNo>')" />
</xsl:variable>
<xsl:value-of select="$content" disable-output-escaping="yes"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
to produce:
<?xml version="1.0" encoding="UTF-8"?>
<XML>
<LogLevel>info</LogLevel>
<Content><![CDATA[ <Msg>
<AccountNo>************983</AccountNo>
<ApplName>Testing</ApplName>
</Msg>]]></Content>
<Date>20140909</Date>
</XML>
although this might not work with every processor (tested to work with Xalan 2.7.1: http://xsltransform.net/jyH9rMk).
The stuff inside the CDATA section is XML disguised as text. XSLT is good at transforming XML, it's not so good at transforming text, especially text with a complex grammar. So my approach would be: extract the text from the outer XML document, parse it as XML, transform it using XSLT (real XSLT that works on nodes rather than on markup), serialize it back to text, then stuff the text back into the original (outer) XML document.
Raw XSLT 1.0 can't do this within a single stylesheet. You need the functions parse() and serialize() to turn lexical XML into a node tree, and back again. These are available as extensions in some processors (such as Saxon), they become available as standard functions in XPath 3.0, and they can be written as simple extension functions (e.g. in Javascript) code in most other processors.
i have multiple Xml files, in a List<File>. What i want is to transform those xml into one Xml with an Xsl :
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="testsuites">
<xsl:call-template name="summary"/>
</xsl:template>
<xsl:template name="summary">
<xsl:variable name="testCount" select="sum(testsuite/#tests)"/>
<xsl:variable name="errorCount" select="sum(testsuite/#errors)"/>
<xsl:variable name="failureCount" select="sum(testsuite/#failures)"/>
<xsl:variable name="timeCount" select="sum(testsuite/#time)"/>
<xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount) div $testCount"/>
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="$failureCount > 0">Failure</xsl:when>
<xsl:when test="$errorCount > 0">Error</xsl:when>
</xsl:choose>
</xsl:attribute>
<Build>
<NombreTest><xsl:value-of select="$testCount"/></NombreTest>
<Failures><xsl:value-of select="$failureCount"/></Failures>
<Erreurs><xsl:value-of select="$errorCount"/></Erreurs>
<PercentSucces><xsl:call-template name="display-percent">
<xsl:with-param name="value" select="$successRate"/>
</xsl:call-template></PercentSucces>
<ExecTime><xsl:call-template name="display-time">
<xsl:with-param name="value" select="$timeCount"/>
</xsl:call-template> </ExecTime>
</Build>
</xsl:template>
<xsl:template match="failure">
<xsl:call-template name="display-failures"/>
</xsl:template>
<xsl:template match="error">
<xsl:call-template name="display-failures"/>
</xsl:template>
<xsl:template name="display-time">
<xsl:param name="value"/>
<xsl:value-of select="format-number($value,'0.000')"/>
</xsl:template>
<xsl:template name="display-percent">
<xsl:param name="value"/>
<xsl:value-of select="format-number($value,'0.00%')"/>
</xsl:template>
<xsl:template name="display-failures">
<xsl:choose>
<xsl:when test="not(#message)">N/A</xsl:when>
<xsl:otherwise>
<xsl:value-of select="#message"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
My problem is that when i am looping and apply the transform with a TransformerFactory it always erase the output XML. I want to edit the output instead.
I know that i can do it in java with a temporary XML and after merge it, but i'm almost sure that it is possible in XSL?
Thanks for helping
You need to pass all document URLs within a single external parameter and you will typically have a transformation like this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pdocNames">
<name>doc1.xml</name>
<name>doc2.xml</name>
<name>doc3.xml</name>
</xsl:param>
<!-- you can directly use $pdocNames/name
if the param is provided externally -->
<xsl:variable name="vDocNames" select=
"document('')/*/xsl:param[]#name='pdocNames']/name"/>
<xsl:template match="/">
<combinedDocs>
<xsl:copy-of select="document($vDocNames)"/>
</combinedDocs>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used), it performs the following:
Obtains the name elements that contain the provides in the parameter $pdocNames document URIs. These elements are contained in the variable vDocNames.
Creates the top element for the output document (in this case named combinedDocs).
Copies all XML documents, whose URIs are in the name elements contained in the vDocNames variable. The standard XSLT function document() is used here.
Do note:
The URLs of all wanted XML documents must be passed externally via a parameter to the transformation. It is vendor-dependent how to pass a parameter to the transformation. You need to read the documentation provided for your particular XSLT processor.
You have to load your documents with document(URI) XSLT function
See also: http://www.w3schools.com/Xsl/func_document.asp