First time I'm dealing with XSL Transforms and I need your help!
Basically, I just want to reformat an XML with a stylesheet. Applying the identity transform works, except for some text elements that are already XML formatted within CDATA tags.
Original XML:
<Views>
<View name="View 1">
<parameter name="MultiPanedView" value="1" dt="3">
</parameter>
<parameter name="Layout" dt="4095"><![CDATA[<?xml version="1.0" encoding="utf-8"?>
<xamDockManager version="9.2.20092.2186">
<contentPanes>
<contentPane name="Pane0eb6000a_b569_413b_92d6_07abd8fba376" location="Document" />
</contentPanes>
<panes />
<documents splitterOrientation="Vertical">
<splitPane name="viewsSplitPane" splitterOrientation="Vertical">
<tabGroup name="viewsTabGroupPane" selectedIndex="0">
<contentPane name="Pane0eb6000a_b569_413b_92d6_07abd8fba376" />
</tabGroup>
</splitPane>
</documents>
</xamDockManager>]]></parameter>
<Pane1>
</Pane1>
</View>
</Views>
Result:
<Views>
<View name="View 1">
<parameter name="MultiPanedView" value="1" dt="3">
</parameter>
<parameter><![CDATA[<?xml version="1.0" encoding="utf-8"?>
<xamDockManager version="9.2.20092.2186">
<contentPanes>
<contentPane name="Pane0eb6000a_b569_413b_92d6_07abd8fba376" location="Document" />
</contentPanes>
<panes />
<documents splitterOrientation="Vertical">
<splitPane name="viewsSplitPane" splitterOrientation="Vertical">
<tabGroup name="viewsTabGroupPane" selectedIndex="0">
<contentPane name="Pane0eb6000a_b569_413b_92d6_07abd8fba376" />
</tabGroup>
</splitPane>
</documents>
</xamDockManager>]]></parameter>
<Pane1>
</Pane1>
</View>
</Views>
XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<!-- IDENTITY TRANSFORM -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- RETAIN XML MARKUP INSIDE CDATA -->
<xsl:template match="parameter[#dt='4095']">
<xsl:copy>
<xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>
<xsl:value-of select="." disable-output-escaping="yes"/>
<xsl:text disable-output-escaping="yes">]]></xsl:text>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
As you can see, the text appears correctly, but the parameter's attributes are lost!
Thanks in advance!
Online XSL Transform available there:
http://xsltransform.net/3Ngx5b4
You're almost there. Just change the template like this
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes"/>
<!-- IDENTITY TRANSFORM -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- RETAIN XML MARKUP INSIDE CDATA -->
<xsl:template match="parameter[#dt='4095']">
<parameter name="Layout" dt="4095">
<xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>
<xsl:value-of select="." disable-output-escaping="yes"/>
<xsl:text disable-output-escaping="yes">]]></xsl:text>
</parameter>
</xsl:template>
</xsl:stylesheet>
Related
My incoming XML
<images>
<image id="i1">
<primary>true</primary>
<height>120</height>
</image>
<image id="i2">
<primary>false></primary>
<height>120</height>
<preferred>false</preferred>
</image>
</images>
Output xml expected
<images>
<image id="i1">
<primary>true</primary>
<height>120</height>
<preferred>true</preferred>
</image>
<image id="i2">
<primary>false></primary>
<height>120</height>
<preferred>false</preferred>
</image>
</images>
"preferred" is an optional element,When not present in the incoming xml need to added in the output xml with the value read from primary element .
Please help me with the xslt .
Here's an XSLT 1.0 stylesheet which has a template to handle the case that an image has no preferred child, and a template to copy everything else (an "identity" template):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- identity template -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- add preferred element where missing -->
<xsl:template match="image[not(preferred)]">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
<preferred><xsl:value-of select="primary"/></preferred>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You didn't mention which version of XSLT that you are using.
This will do the trick in XSLT 3.0. For other versions of XSLT, there are similar variants that should work as well.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
expand-text="yes">
<xsl:output method="xml" indent="yes" />
<xsl:mode on-no-match="shallow-copy" />
<xsl:template match="image" >
<xsl:copy>
<xsl:apply-templates select="#*,node()" />
<xsl:if test="not(exists(preferred))" >
<preferred>{primary}</preferred>
</xsl:if>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I am pretty much new to xsl.Currently facing a challenge in transforming the below sample xml to required output.
Sample XML:
<root>
<types>
<entity-type>
<field-type identifier="FieldType1" proxy-ref="TypeA">
<object-name>aclProxy</object-name>
<persistence-class-name>java.lang.Long</persistence-class-name>
</field-type>
<field-type identifier="FieldType2" proxy-ref="TypeB">
<object-name>aclName</object-name>
<persistence-class-name>java.lang.String</persistence-class-name>
</field-type>
</entity-type>
</types>
<custom>
<category identifier="parent" order="1">
<name>%category.parent</name>
</category>
<category identifier="texts" order="2">
<name>%category.Texts</name>
</category>
<field identifier="ArticleID" category-ref="parent" field-type-ref="FieldType1" proxy-entity-ref="ABC">
<name>%field.name</name>
<description>%field.Article.description</description>
</field>
<field identifier="ArticleName" category-ref="texts" field-type-ref="FieldType2" proxy-entity-ref="ABCD">
<name>%field.name.text</name>
<description>%field.Articletext.description</description>
</field>
</custom>
</root>
Output
<field identifier="ArticleID">
<name>%field.name</name>
<description>%field.Article.description</description>
<category-ref-name>%category.parent</category-ref-name>
<field-type-ref>Long</field-type-ref>
<proxy-entity-ref>TypeA</proxy-entity-ref>
</field>
<field identifier="ArticleName" >
<name>%field.name.text</name>
<description>%field.Articletext.description</description>
<category-ref-name>%category.Texts</category-ref-name>
<field-type-ref>String</field-type-ref>
<proxy-entity-ref>TypeB</proxy-entity-ref>
</field>
XSLT which I have been trying:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="category" match="category" use="#identifier" />
<xsl:key name="field" match="field" use="#category-ref" />
<xsl:apply-templates select="custom[not(key('field',category/#identifier))]"/>
<xsl:template match="/">
<xsl:text>something</xsl:text>
<xsl:value-of select="category/#identifier"/>
<xsl:apply-templates select="."/>
</xsl:template>
<xsl:template match="/">
<xsl:value-of select="field/#identifier"/>
<xsl:variable name="categoryparam" select="key('category', field/#category-ref)" />
<xsl:if test="$categoryparam">
<xsl:apply-templates select="$categoryparam"/>
<xsl:text>something</xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Basically the output should have only field(s) in it referring to the attributes and get the node values.
Any help is much appreciated.
Thanks in advance.
Let us start off with these 2 keys:
<xsl:key name="kCategory" match="category" use="#identifier" />
<xsl:key name="kFieldID" match="field-type" use="#identifier" />
you will need the values in category and field-type nodes later.
This template:
<xsl:template match="/">
<xsl:apply-templates select="root/custom/field"/>
</xsl:template>
will output the target field nodes
Having a template match with the field node to further manipuate the output:
<xsl:template match="field">
<xsl:copy>
<!-- copies the identifier attribute and all children -->
<xsl:copy-of select="#identifier|node()"/>
<category-ref-name>
<!-- gets the child name of the target category node
that matches the category-ref attribute -->
<xsl:value-of select="key('kCategory', #category-ref)/name"/>
</category-ref-name>
<field-type-ref>
<!-- gets the child persistence-class-name of the target
field-type node that matches the field-type-ref attribute,
with further substring manipulations -->
<xsl:value-of select="substring-after(key('kFieldID', #field-type-ref)/persistence-class-name, 'java.lang.')"/>
</field-type-ref>
<proxy-entity-ref>
<!-- gets the attribute proxy-ref of the target
field-type node that matches the field-type-ref
attribute -->
<xsl:value-of select="key('kFieldID', #field-type-ref)/#proxy-ref"/>
</proxy-entity-ref>
</xsl:copy>
</xsl:template>
The whole stylesheet is below:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="kCategory" match="category" use="#identifier" />
<xsl:key name="kFieldID" match="field-type" use="#identifier" />
<xsl:template match="/">
<xsl:apply-templates select="root/custom/field"/>
</xsl:template>
<xsl:template match="field">
<xsl:copy>
<xsl:copy-of select="#identifier|node()"/>
<category-ref-name>
<xsl:value-of select="key('kCategory', #category-ref)/name"/>
</category-ref-name>
<field-type-ref>
<xsl:value-of select="substring-after(key('kFieldID', #field-type-ref)/persistence-class-name, 'java.lang.')"/>
</field-type-ref>
<proxy-entity-ref>
<xsl:value-of select="key('kFieldID', #field-type-ref)/#proxy-ref"/>
</proxy-entity-ref>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
See it in action (http://xsltfiddle.liberty-development.net/gWmuiJc).
I am looking my xslt in below format:
<xml>
<apis>
<name>API Name</name>
<comment> Comment</comment>
<version>12</version>
</apis>
</xml>
XSLT Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:L7j="http://ns.l7tech.com/2012/08/jdbc-query-result" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<xml>
<xsl:apply-templates select="//L7j:col" />
</xml>
</xsl:template>
<xsl:template match="//L7j:col">
<api>
<xsl:element name="{#name}">
<xsl:value-of select="." /></xsl:element>
</api>
</xsl:template>
</xsl:stylesheet>
Input XML:
<?xml version="1.0" encoding="UTF-8"?>
<L7j:jdbcQueryResult xmlns:L7j="http://ns.l7tech.com/2012/08/jdbc-query-result">
<L7j:row>
<L7j:col name="name" type="java.lang.String">Policy for service #0b8bab6913cc588557b6973e94d1bfdd, WSTrustSoapService</L7j:col>
<L7j:col name="comment">
<![CDATA[NULL]]>
</L7j:col>
<L7j:col name="version" type="java.lang.Integer">18</L7j:col>
</L7j:row>
<L7j:row>
<L7j:col name="name" type="java.lang.String">Policy for service #0b8bab6913cc588557b6973e94d5893d, UUPRStub</L7j:col>
<L7j:col name="comment">
<![CDATA[NULL]]>
</L7j:col>
<L7j:col name="version" type="java.lang.Integer">16</L7j:col>
</L7j:row>
</L7j:jdbcQueryResult>
If I understand correctly, you want to do:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:L7j="http://ns.l7tech.com/2012/08/jdbc-query-result"
exclude-result-prefixes="L7j">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="/L7j:jdbcQueryResult">
<xml>
<xsl:apply-templates/>
</xml>
</xsl:template>
<xsl:template match="L7j:row">
<apis>
<xsl:apply-templates/>
</apis>
</xsl:template>
<xsl:template match="L7j:col">
<xsl:element name="{#name}">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Below is my requirement. Can we do this using XSLT? I want to convert value of AttributeName as tag under policy and corresponding AttributeValue as value.
Input :
<Policy>
<Attributes>
<AttributeName>is_policy_loan</AttributeName>
<AttributeValue>Yes</AttributeValue>
</Attributes>
<Attributes>
<AttributeName>is_policy_owners</AttributeName>
<AttributeValue>Yes</AttributeValue>
</Attributes>
<Attributes>
<AttributeName>is_policy_twoyears</AttributeName>
<AttributeValue>Yes</AttributeValue>
</Attributes>
</Policy>
Output :
<Policy>
<is_policy_loan>Yes</is_policy_loan>
<is_policy_owners>Yes</is_policy_owners>
<is_policy_twoyears>Yes</is_policy_twoyears>
</Policy>
The following xsl file will do the job:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- create the <AttributeName>AttributeValue</..> nodes -->
<xsl:template match="//Attributes">
<xsl:variable name="name" select="AttributeName" />
<xsl:element name="{$name}">
<xsl:value-of select="AttributeValue" />
</xsl:element>
</xsl:template>
<!-- wrap nodes in a `Policy` node -->
<xsl:template match="/">
<Policy>
<xsl:apply-templates/>
</Policy>
</xsl:template>
</xsl:stylesheet>
The way i would do,
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" encoding="UTF-8" omit-xml-declaration="yes" />
<xsl:template match="Policy">
<xsl:element name="Policy">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="Attributes">
<xsl:variable name="name" select="AttributeName" />
<xsl:element name="{$name}">
<xsl:value-of select="AttributeValue" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
output will be,
<Policy>
<is_policy_loan>Yes</is_policy_loan>
<is_policy_owners>Yes</is_policy_owners>
<is_policy_twoyears>Yes</is_policy_twoyears>
</Policy>
I have several records from the DB for a corresponding record in a file.
Example
Record no. XML
<XML_FILE_HEADER file_name="sample.txt" />
<XML_RECORD record_number="1" name="John Doe" Age="21"/>
<XML_RECORD record_number="2" name""Jessica Sanchez" Age="23"/>
<XML_FILE_FOOTER total_records="2"/>
Now for each record I have an xslt template that would create the output file in xml.
For record no 1:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xslt">
<xsl:output method="xml"/>
<xsl:template match="XML_FILE_HEADER">
<xsl:element name="File">
<xsl:attribute name="FileName"><xsl:value-of select="#file_name"/></xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
For records 2 and 3:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xslt">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="XML_RECORD">
<xsl:element name="Record">
<xsl:attribute name="Name"><xsl:value-of select="#name"/></xsl:attribute>
<xsl:element name="Details">
<xsl:attribute name="Age"><xsl:value-of select="#Age"/></xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
For record 4:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xslt">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="XML_FILE_FOOTER">
<xsl:element name="Totals">
<xsl:attribute name="Total Records"><xsl:value-of select="#total_records"/></xsl:attribute>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The problem with is is I would have an output of this after appending each record using the templates above:
<?xml version="1.0" encoding="UTF-8"?>
<File FileName="sample.txt"></File>
<Record Name="John Doe" Age="21"></Record>
<Record Name="Jessica Sanchez" Age="22"></Record>
<Totals Total Records="2"></Totals>
How would I be able to insert the Record and Totals elements under File? so that it would have an output like this:
<?xml version="1.0" encoding="UTF-8"?>
<File FileName="sample.txt">
<Record Name="John Doe" Age="21"></Record>
<Record Name="Jessica Sanchez" Age="22"></Record>
<Totals Total Records="2"></Totals>
</File>
Any help would be very much appreciated. Thanks.
As short and easy 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:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:apply-templates select="XML_FILE_HEADER"/>
</xsl:template>
<xsl:template match="XML_FILE_HEADER">
<File FileName="{#file_name}">
<xsl:apply-templates select="../*[not(self::XML_FILE_HEADER)]"/>
</File>
</xsl:template>
<xsl:template match="XML_RECORD">
<Record name="{#name}" Age="{#Age}"/>
</xsl:template>
<xsl:template match="XML_FILE_FOOTER">
<Totals TotalRecords="{#total_records}"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML (corrected to be well-formed) document:
<t>
<XML_FILE_HEADER file_name="sample.txt" />
<XML_RECORD record_number="1" name="John Doe" Age="21"/>
<XML_RECORD record_number="2" name="Jessica Sanchez" Age="23"/>
<XML_FILE_FOOTER total_records="2"/>
</t>
the wanted, correct result is produced:
<File FileName="sample.txt">
<Record name="John Doe" Age="21"/>
<Record name="Jessica Sanchez" Age="23"/>
<Totals TotalRecords="2"/>
</File>
Explanation:
Proper use of templates.
Proper use of xsl:apply-templates for ordering the results.
Proper use of AVT (Attribute Value Templates).
Avoided the use of xsl:element
No use of xsl:call-template.
Implemented in "push style" almost completely.
What you want is the <xsl:call-template name="templatename" /> element. This allows you to call a template from inside another template.
Something like
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xslt">
<xsl:output method="xml"/>
<xsl:template match="/XML_FILE/XML_FILE_HEADER">
<xsl:element name="File">
<xsl:attribute name="FileName">
<xsl:value-of select="#file_name"/>
</xsl:attribute>
<xsl:for-each select="/XML_FILE/XML_RECORD">
<xsl:call-template name="RecordTemplate" />
</xsl:for-each>
<xsl:call-template name="TotalTemplate" />
</xsl:element>
</xsl:template>
<xsl:template name="RecordTemplate">
<xsl:element name="Record">
<xsl:attribute name="Name"><xsl:value-of select="#name"/></xsl:attribute>
<xsl:attribute name="Age"><xsl:value-of select="#Age"/></xsl:attribute>
</xsl:element>
</xsl:template>
<xsl:template match="/XML_FILE/XML_FILE_FOOTER" name="TotalTemplate">
<xsl:element name="Totals">
<xsl:attribute name="Total Records"><xsl:value-of select="#total_records"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
of course your input you have to be XML valid (i.e. have a single root node) like so
<XML_FILE>
<XML_FILE_HEADER file_name="sample.txt" />
<XML_RECORD record_number="1" name="John Doe" Age="21"/>
<XML_RECORD record_number="2" name""Jessica Sanchez" Age="23"/>
<XML_FILE_FOOTER total_records="2"/>
</XML_FILE>