I want to convert following code
================
<spirit:parameter>
<spirit:name>P3</spirit:name>
<spirit:test_value>8</spirit:test_value>
</spirit:parameter>
</spirit:parameters>
================
into
================
<spirit:parameter>
<spirit:name>P3</spirit:name>
<spirit:glue_value>8</spirit:glue_value>
</spirit:parameter>
</spirit:parameters>
================
I want to do it through XSLT. Please help
Your example input wasn't quite valid, so changed it slightly and added a namespace:
<?xml version="1.0" encoding="UTF-8"?>
<spirit:parameters xmlns:spirit="http://example.com/namespace/spirit">
<spirit:parameter>
<spirit:name>P3</spirit:name>
<spirit:test_value>8</spirit:test_value>
</spirit:parameter>
</spirit:parameters>
You essentially want to copy all nodes except 'spirit:test_value' which needs to be renamed:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:spirit="http://example.com/namespace/spirit">
<xsl:output omit-xml-declaration='yes'/>
<xsl:template match="spirit:parameters|spirit:parameter|spirit:name">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="spirit:test_value">
<spirit:glue_value>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</spirit:glue_value>
</xsl:template>
</xsl:stylesheet>
The '<xsl:copy-of select="#*"/>' sections there are optional, you'd need those if you want to copy across any attributes from the source XML.
Related
I am quite a newbie to XSLT and I need to figure out how to retrieve repeating elements from a XML file in a specific order based on their attribute value to a CSV file. My result document is a CSV file that I ultimately need to import its content to SQL Server tables. Therefore, the order of the retrieved elements within the CSV file does matter as they need to match the table columns defined as headers.
My problem occurs with the Project Content_Detail element that exists in different languages and can appear in any order in the XML source. I only need to extract the German version with the Title, Goal element first, followed by the English version elements.
I use SSIS with MS Visual Studio 2019 to transform my XML to a CSV file. MS Visual Studio supports only XSLT 1.0.
Here is my XML file (EDITED):
<?xml version="1.0" encoding="utf-8"?>
<dta:Projects xmlns:dta="http://domain.test/dta">
<dta:Project>
<dta:Core-Basis>
<dta:Goal>1672</dta:Goal>
</dta:Core-Basis>
<dta:Basisinfo>
<dta:Content>
<dta:Content_Detail Lang="de">
<dta:Title>Wirtschaft</dta:Title>
<dta:Aim>Steigerung</dta:Aim>
</dta:Content_Detail>
<dta:Content_Detail Lang="en">
<dta:Title>Economy</dta:Title>
</dta:Content_Detail>
</dta:Content>
</dta:Basisinfo>
</dta:Project>
<dta:Project >
<dta:Core-Basis>
<dta:Goal>2035</dta:Goal>
</dta:Core-Basis>
<dta:Basisinfo>
<dta:Content>
<dta:Content_Detail Lang="en">
<dta:Title>Environmental Protection</dta:Title>
<dta:Aim>Facilitation</dta:Aim>
</dta:Content_Detail>
<dta:Content_Detail Lang="de">
<dta:Title>Naturschutz</dta:Title>
</dta:Content_Detail>
</dta:Content>
</dta:Basisinfo>
</dta:Project >
</dta:Projects>
This is my XSLT file (EDITED). I tried to add numerous order and sort arguments to the xsl:if block but I couldn’t come up with a working solution yet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dta="http://domain.test/dta" exclude-result-prefixes="dta">
<xsl:output method="text" encoding="UTF-8" indent="no"/>
<xsl:template match="/">
<xsl:text>DTA_Goal;DTA_Title_de;DTA_Aim_de;DTA_Title_en;DTA_Aim_en;
</xsl:text>
<xsl:apply-templates mode="find_content"/>
</xsl:template>
<xsl:template match="text()|#*" mode="find_content"/>
<xsl:template match="dta:Project" mode="find_content">
<xsl:value-of select="concat(dta:Core-Basis/dta:Goal,';')"/>
<xsl:apply-templates mode="find_content"/>
</xsl:template>
<xsl:template match="dta:Content_Detail" mode="find_content">
<xsl:if test="#Lang='de'">
<xsl:value-of select="concat(dta:Title,';')"/>
<xsl:value-of select="concat(dta:Aim,';')"/>
</xsl:if>
<xsl:if test="#Lang='en'">
<xsl:value-of select="concat(dta:Title,';')"/>
<xsl:value-of select="concat(dta:Aim,'
')"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
This is my CSV output so far, not sorting the Content_detail based on their language value attribute:
DTA_Goal;DTA_Title_de;DTA_Aim_de;DTA_Title_en;DTA_Aim_en;
1672;Wirtschaft;Steigerung;Economy;
2035;Environmental Protection;Facilitation
Naturschutz;;
This is what I need:
DTA_Goal; DTA_Title_de; DTA_Aim_de; DTA_Title_en; DTA_Aim_en;
1672;Wirtschaft;Steigerung;Economy;;
2035;Naturschutz;;Environmental Protection;Facilitation;
As I am not a professional programmer, any help or comment also on the rest of my code is highly appreciated.
In your input, one of the dta:Project elements has a dta:Core-Basis child, while the other has dta:Core-Basisdaten. Assuming that's a mistake*, you could produce the needed output simply by:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dta="http://domain.test/dta">
<xsl:output method="text" encoding="UTF-8" />
<xsl:template match="/dta:Projects">
<!-- header -->
<xsl:text>DTA_Goal;DTA_Title_de;DTA_Aim_de;DTA_Title_en;DTA_Aim_en;
</xsl:text>
<!-- data -->
<xsl:for-each select="dta:Project">
<xsl:value-of select="dta:Core-Basis/dta:Goal"/>
<xsl:text>;</xsl:text>
<!-- de -->
<xsl:variable name="content-de" select="dta:Basisinfo/dta:Content/dta:Content_Detail[#Lang='de']" />
<xsl:value-of select="$content-de/dta:Title"/>
<xsl:text>;</xsl:text>
<xsl:value-of select="$content-de/dta:Aim"/>
<xsl:text>;</xsl:text>
<!-- en -->
<xsl:variable name="content-en" select="dta:Basisinfo/dta:Content/dta:Content_Detail[#Lang='en']" />
<xsl:value-of select="$content-en/dta:Title"/>
<xsl:text>;</xsl:text>
<xsl:value-of select="$content-en/dta:Aim"/>
<xsl:text>;</xsl:text>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
(*) If not, change:
<xsl:value-of select="dta:Core-Basis/dta:Goal"/>
to:
<xsl:value-of select="(dta:Core-Basis|dta:Core-Basisdaten)/dta:Goal"/>
P.S. Not sure why you need the trailing ; at the end of each line.
I am using the below code snippet to fetch the current date in XSLT:
<xsl:variable name="currentDate">
<xsl:variable name="datePattern">yyyy-MM-dd'T'HH:mm:ss Z</xsl:variable>
<xsl:value-of select="java:format(java:java.text.SimpleDateFormat.new($datePattern), java:java.util.Date.new())" />
</xsl:variable>
Requirement is, to check if ShippedDate attribute value is equal to today's date, then include <Monitor> element, else exclude.
<Order>
<Monitors>
<Monitor ShippedDate="2015-11-10T00:00:00-05:00"/>
<Monitor ShippedDate="2015-12-03T00:00:00-05:00"/>
</Monitors>
</Order>
Can somebody please help me with what function needs to be used to achieve this?
Requirement is, to check if ShippedDate is equal to today's date, then
include <Monitor> element, else exclude.
I would suggest you do it this way:
XSLT 1.0 (+EXSLT)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:date="http://exslt.org/dates-and-times"
extension-element-prefixes="date">
<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="Monitors">
<xsl:copy>
<xsl:apply-templates select="Monitor[substring(#ShippedDate, 1, 10) = substring(date:date(), 1, 10)]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Test input:
<Order>
<Monitors>
<Monitor ShippedDate="2015-12-01T00:00:00-05:00"/>
<Monitor ShippedDate="2015-12-02T00:00:00-05:00"/>
<Monitor ShippedDate="2015-12-03T00:00:00-05:00"/>
</Monitors>
</Order>
Result, when applied on December 2, 2015:
<?xml version="1.0" encoding="UTF-8"?>
<Order>
<Monitors>
<Monitor ShippedDate="2015-12-02T00:00:00-05:00"/>
</Monitors>
</Order>
Note:
This requires a processor that supports the EXSLT date:date() extension function (e.g. Xalan or Saxon 6.5);
Only the date component is compared. If you are running the transformation in a time zone that's different from the time zone used in the input, the result may be inaccurate. If you want to also consider the time zone, then there's more work to do.
Added:
As a continuation of this, if none of the ShippedDate satisfies
condition, then no output must be transformed by XSLT.
Try:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:date="http://exslt.org/dates-and-times"
extension-element-prefixes="date">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/Order">
<xsl:variable name="current" select="Monitors/Monitor[substring(#ShippedDate, 1, 10) = substring(date:date(), 1, 10)]"/>
<xsl:if test="$current">
<Order>
<Monitors>
<xsl:copy-of select="$current"/>
</Monitors>
</Order>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
How can I extract sub nodes to a single list for processing in a template?
Consider the following XML.
<document>
<menu></menu>
<feature>
<header>Header 1</header>
<article>article 1</article>
<article>article 2</article>
</feature>
<feature>
<header>Heading 2</header>
<article>article 1a</article>
<article>article 2a</article>
</feature>
</document>
I'd like to extract all the article nodes to a single list for processing in a template.
I need article nodes to be available at once, because I need to do calculations based on the number of articles there are.
you can try the following stylesheet:
<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:output indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:variable name="list">
<articles>
<xsl:copy-of select="descendant::article"/>
</articles>
</xsl:variable>
<xsl:variable name="vPass1" select="ext:node-set($list)"/>
<xsl:apply-templates select="$vPass1/*"/>
</xsl:template>
<xsl:template match="articles">
<xsl:copy>
<xsl:text>Number of articles: </xsl:text><xsl:value-of select="count(article)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when applied your input, it produces:
<articles>Number of articles: 4</articles>
I'm writing an XSLT data map from one XML schema to another XML schema. The target schema has a String field that will have a copy of the original XML in it. I know that I must convert the control characters in the XML to < and >. I have done that. The issue I'm having is that the data in the XML also has < and > in it as well. This causes problems because I'm sending the XML to a WCF-SQL adapter and when it hits the < in my data it attempts to convert that to < causing XML validation failure.
Here is a snippet of my XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="*" mode="serialize">
<xsl:text><</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>></xsl:text>
<xsl:apply-templates mode="serialize"/>
<xsl:text></</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>></xsl:text>
</xsl:template>
<xsl:template match="/">
<proc_Insert xmlns="http://schemas.microsoft.com/Sql/2008/05/TypedProcedures/dbo">
<StagingXML>
<xsl:variable name="nodestring">
<xsl:apply-templates select="*" mode="serialize"/>
</xsl:variable>
<xsl:value-of select="$nodestring"/>
</StagingXML>
</proc_Insert>
</xsl:template>
</xsl:stylesheet>
You need to quote the content, a truely ancient bit of code (predating xslt 1.0 I think:-) that does that is to be found
http://www.openmath.org/cdfiles2/xsl/verb.xsl
although there are newer versions around as well/
With a simple XML like this
<value>
<num>
<accession>111</accession>
<sequence>AAA</sequence>
<score>4000</score>
</num>
</value>
I want to know if it is possible to access to a particular node from a node previously stored in a variable. The XSLT code is very short and explains better what I want to say
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/value/num">
<xsl:variable name="node">
<xsl:copy-of select="current()"/>
</xsl:variable>
<root>
<xsl:copy-of select="$node"/>
</root>
</xsl:template>
</xsl:stylesheet>
So I store the node in the variable "node". Then I can print the contents of the node with $node.
(EDIT) XML output
<root>
<num>
<accession>111</accession>
<sequence>AAA</sequence>
<score>4000</score>
</num>
</root>
What I want to do is to print the contents of a sub-node, like this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/value/num">
<xsl:variable name="node">
<xsl:copy-of select="current()"/>
</xsl:variable>
<root>
<xsl:copy-of select="$node/accession"/>
</root>
</xsl:template>
</xsl:stylesheet>
But it gives an error (Component returned failure code: 0x80600008 [nsIXSLTProcessor.transformToFragment]) (check here)
(EDIT) The XML that I would want is
<root>
<accession>111</accession>
</root>
NOTE: The question is not how can I get this output. The question is how, using a variable as in the XSLT provided, can I get this output.
(EDIT:SOLVED)
Actually it is possible, but as pointed out in the comments, the value of a variable has to be assigned with the "select" attribute if a node-set is required. So this code was not working since the variable had a tree fragment instead of a node-set stored in it (read more information here)
Thanks!
Try this:
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/value">
<root>
<xsl:for-each select="num">
<xsl:variable name="node" select="current()" />
<xsl:copy-of select="$node/accession" />
</xsl:for-each>
</root>
</xsl:template>
</xsl:transform>
Note that I used xsl:transform instead of xsl:stylesheet. Also, consider using version 2.0 instead of 1.0 if you have a compliant processor, it adds a lot of useful features.
I still don't see your need for a variable, though.