I'm converting an XML document and want to dump the entire contents in a content node in the converter.
<xsl:template match="/">
<vce>
<document>
<content name="xml">
<xsl:copy-of select="." />
</content>
</document>
</vce>
</xsl:template>
This gives me a node with the name "XML" and my entire xml content within. However, this is removed when the normalization converter is run. Is there something special I need to do to index XML inside a content?
I was able to reference the converter: 'vse-converter-xml-to-vxml' to create a template that indexes the xml:
<xsl:template match="/">
<vce>
<document>
<content name="xml">
<xsl:apply-templates select="*" mode="xml-to-plain-text" />
</content>
</document>
</vce>
</xsl:template>
<xsl:template match="*" mode="xml-to-plain-text">
<xsl:text><![CDATA[<]]></xsl:text>
<xsl:value-of select="name()" />
<xsl:text> </xsl:text>
<xsl:choose>
<xsl:when test="text()|*|comment()">
<xsl:text>></xsl:text>
<xsl:apply-templates select="text()|*|comment()" mode="xml-to-plain-text" />
<xsl:text><![CDATA[</]]></xsl:text>
<xsl:value-of select="name()" />
<xsl:text>></xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>/></xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I need to take a data array from one node in XML and then use in XSL template.
Input data look like this:
<InpData>
<period number="1">
<Storage>
<Item weight="10.5" height="5" width="15" length="20"/>
<Item weight="20.75" height="4.5" width="7.3" length="18"/>
<Item weight="10.5" height="5" width="15" length="20"/>
</Storage>
<Transportation>
<Items>
<Item>
<DestRegion value="5"/>
<Sender name="Smith" company="BlueSky" />
<Date day="03" month="03" year="2017" />
<Item/>
<Item>
<DestRegion value="6"/>
<Sender name="Pith" company="BlueSky" />
<Date day="03" month="03" year="2017" />
<Item/>
<Item>
<DestRegion value="5"/>
<Sender name="Bill" company="BlueSky" />
<Date day="03" month="03" year="2017" />
<Item/>
<Items/>
</Transportation>
<period/>
<period number="2">
<period/>
</InpData>
And I need to combine nodes and to have output xml like that:
<period number="1">
<Items>
<Item weight="10.5" senderName="Smith"/>
<Item weight="20.75" senderName="Pith"/>
<Item weight="10.5" senderName="Bill"/>
<Items/>
<period/>
I made XSLT script:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="period">
<xsl:variable name="weight" select="Storage/Item/#weight"></xsl:variable>
<period number="{#number}">
<Items>
<xsl:apply-templates select="Transportation/Items/Item">
<xsl:with-param name="weight_1">
<xsl:value-of select="$weight" />
</xsl:with-param>
</xsl:apply-templates>
</Items>
</period>
</xsl:template>
<xsl:template match="Transportation/Items/Item">
<xsl:param name="weight_1"/>
<xsl:variable name="pos" select="position()"></xsl:variable>
<Item weight="{$weight_1[$pos]}" senderName="{Sender/#name}">
</xsl:template>
</xsl:transform>
But it doesn't work properly. I can send to template for example and it works if I will use just $weight_1 without indexes in the template. But I cannot use $weight_1[$pos] in the template.
Please tell me how can I use the array weight in XSLT properly?
Why can't you do simply:
<xsl:template match="period">
<period number="{#number}">
<Items>
<xsl:for-each select="Storage/Item">
<xsl:variable name="i" select="position()" />
<Item weight="{#weight}" senderName="{../../Transportation/Items/Item[$i]/Sender/#name}"/>
</xsl:for-each>
</Items>
</period>
</xsl:template>
(assuming that the only thing that links the items is their position in their parent element).
Added:
To do it the way you have started, you'd need to do something like:
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="/InpData">
<root>
<xsl:apply-templates select="period"/>
</root>
</xsl:template>
<xsl:template match="period">
<period number="{#number}">
<Items>
<xsl:apply-templates select="Transportation/Items/Item">
<xsl:with-param name="weights" select="Storage/Item/#weight"/>
</xsl:apply-templates>
</Items>
</period>
</xsl:template>
<xsl:template match="Item">
<xsl:param name="weights"/>
<xsl:variable name="i" select="position()" />
<Item weight="{$weights[$i]}" senderName="{Sender/#name}"/>
</xsl:template>
</xsl:stylesheet>
I've two xml files. Both are arrays of
person (having a lot of nodes underneath)
I need to convert this one
<result>
<sfobject>
<id></id>
<type>CompoundEmployee</type>
<person>
<lot of nodes/>
</person>
</sfobject>
<sfobject>
<id></id>
<type>CompoundEmployee</type>
<person>
<lot of nodes/>
</person>
</sfobject>
</result>
to
<queryCompoundEmployeeResponse>
<CompoundEmployee>
<id></id>
<person>
<lot of nodes/>
</person>
</CompoundEmployee>
<CompoundEmployee>
<id></id>
<person>
<lot of nodes/>
</person>
</CompoundEmployee>
</queryCompoundEmployeeResponse>
using xslt. I've this xslt for it.
<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()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="sfobject/*[1]">
<queryCompoundEmployeeResponse>
<CompoundEmployee>
<id>
<xsl:value-of select="#id" />
</id>
<xsl:copy-of select="/*/person[1]" />
<xsl:call-template name="identity" />
</id>
</CompoundEmployee>
</queryCompoundEmployeeResponse>
</xsl:template>
<xsl:template match="/*/sfobject[1]" />
<xsl:param name="removeElementsNamed" select="'type'"/>
</xsl:stylesheet>
This doesn't check out well. I've done this before in groovy, but now this has to be converted to xslt as the system changed. I'm new to xslt and I'm sure I am to use an advanced level of xslt here. Any pointers are highly appreciated.
Is xslt the right tool at all here? Or should I stick to groovy?
You have three rules you need to implement, it seems
Rename result to queryCompoundEmployeeResponse
Rename sfobject to whatever is held in type
Remove type from under sfobject
Happuly, each rule here can actually be implemented as a separate template.
So, for rule 1, you do this...
<xsl:template match="result">
<queryCompoundEmployeeResponse>
<xsl:apply-templates />
</queryCompoundEmployeeResponse>
</xsl:template>
For rule 2, do this...
<xsl:template match="sfobject">
<xsl:element name="{type}">
<xsl:apply-templates select="node()|#*" />
</xsl:element>
</xsl:template>
And for rule 3, do this...
<xsl:template match="sfobject/type" />
You then use the identity template to handle all other nodes and attributes.
Try this 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:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="result">
<queryCompoundEmployeeResponse>
<xsl:apply-templates />
</queryCompoundEmployeeResponse>
</xsl:template>
<xsl:template match="sfobject">
<xsl:element name="{type}">
<xsl:apply-templates select="node()|#*" />
</xsl:element>
</xsl:template>
<xsl:template match="sfobject/type" />
</xsl:stylesheet>
I need help where I can store two xslt variables in 1 array like variable to be used later
<xsl:variable name="edgeDeviceArray">
<Item><xsl:value-of select="$edgeDev1" /></Item>
<Item><xsl:value-of select="$edgeDev2" /></Item>
</xsl:variable>
But the output of the above seems to be concatenation. I would like to refer later like edgeDeviceArray[1]...
Here is a (non-working) fragment of my stylesheet demonstrating what I am trying to do
<xsl:variable name="edgeDev1"
select="$deviceDoc/x:config/t:devices/t:device[t:address=$edgeDev1IP]/t:name" />
<xsl:variable name="edgeDev2"
select="$deviceDoc/x:config/t:devices/t:device[t:address=$edgeDev2IP]/t:name" />
<xsl:variable name="xrSet" select="$xrDeviceDoc/x:config/t:devices/t:device-module/t:devices" />
<xsl:for-each select="$xrSet">
<xsl:variable name="asideDoc"
select="document(concat($edgeDevice[position()], '.xml'))" />
</xsl:for-each>
Here, I am reading the devices names from 1 doc based on certain attributes if they match.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:t="http://tail-f.com/ns/ncs"
xmlns:x="http://tail-f.com/ns/config/1.0"
xmlns:y="http://tail-f.com/ned/alu-sr"
xmlns:a="http://tail-f.com/ned/cisco-ios-xr"
xmlns:m="http://mask.data"
xmlns:im="http://inverse-mask.data" exclude-result-prefixes="xsl t x y"
xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8"
indent="yes" omit-xml-declaration="yes" />
<xsl:template match="/">
<xsl:variable name="deviceDoc" select="document('devices.xml')" />
<xsl:variable name="edgeDev1IP"
select="../y:sdp[y:sdp-id=$sdpSet[1]]/y:far-end" />
<xsl:variable name="edgeDev1"
select="$deviceDoc/x:config/t:devices/t:device[t:address=$edgeDev1IP]/t:name" />
<xsl:variable name="edgeDev2IP"
select="../y:sdp[y:sdp-id=$sdpSet[2]]/y:far-end" />
<xsl:variable name="edgeDev2"
select="$deviceDoc/x:config/t:devices/t:device[t:address=$edgeDev2IP]/t:name" />
<xsl:variable name="xrDeviceDoc" select="document('xrDevices.xml')" />
<xsl:variable name="xrSet"
select="$xrDeviceDoc/x:config/t:devices/t:device-module/t:devices" />
<xsl:variable name="edgeDeviceArray">
<item><xsl:value-of select="$edgeDev1" /></item>
<item><xsl:value-of select="$edgeDev2" /></item>
</xsl:variable>
<anurag><xsl:value-of select="exsl:node-set($edgeDeviceArray)/item[1]" /></anurag>
</xsl:template>
</xsl:stylesheet>
I would like to refer later like edgeDeviceArray[1]
That won't work for two reasons:
There's only one edgeDeviceArray. In order to refer to its first item, you would need to use something like $edgeDeviceArray/item[1];
In XSLT 1.0, your variable is a result tree fragment, and must be converted to a node-set before its contents can be addressed by XPath.
Here's a minimized example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="myVar">
<item>A</item>
<item>B</item>
</xsl:variable>
<xsl:template match="/">
<result>
<xsl:value-of select="exsl:node-set($myVar)/item[2]"/>
</result>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<result>B</result>
I'm trying to make a XSLT conversion that generates C code, the following XML should be converted:
<enum name="anenum">
<enumValue name="a"/>
<enumValue name="b"/>
<enumValue name="c" data="10"/>
<enumValue name="d" />
<enumValue name="e" />
</enum>
It should convert to some C code as following:
enum anenum {
a = 0,
b = 1,
c = 10,
d = 11,
e = 12
}
or alternatively (as the C preprocessor will handle the summation):
enum anenum {
a = 0,
b = 1,
c = 10,
d = c+1,
e = c+2
}
The core of my XSLT looks like:
<xsl:for-each select="enumValue">
<xsl:value-of select="name"/>
<xsl:text> = </xsl:text>
<xsl:choose>
<xsl:when test="string-length(#data)>0">
<xsl:value-of select="#data"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="position()-1"/>
</xsl:otherwise>
</xsl:choose>
<xsl:text>,
(for simplicity I skip some of the 'no comma at the last element' code)
This example will not generate the correct values for d and e
I've been trying to get it working for the variable d and e, but so far I'm unsuccessful.
Using constructions like:
<xsl:when test="string-length(preceding-sibling::enumValue[1]/#datavalue)>0">
<xsl:value-of select="preceding-sibling::enumValue/#data + 1"/>
</xsl:when>
...only work for the first one after the specified value (in this case d).
Who can help me? I'm probably thinking too much in a procedural way...
A nonrecursive solution, using keys:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output method="text"/>
<xsl:key name="koffsetEnums" match="enumValue[#data]"
use="generate-id()"/>
<xsl:template match="enum">
enum <xsl:value-of select="#name"/> {
<xsl:apply-templates select="enumValue"/>
}
</xsl:template>
<xsl:template match="enumValue">
<xsl:value-of select="concat(#name, ' = ')"/>
<xsl:variable name="voffsetValueId" select=
"generate-id((. | preceding-sibling::enumValue)
[#data][last()]
)"/>
<xsl:choose>
<xsl:when test="not($voffsetValueId)">
<xsl:value-of select="concat(position(),'
')"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vinitOffset" select=
"key('koffsetEnums', $voffsetValueId)/#data"
/>
<xsl:value-of select=
"$vinitOffset
+
count(preceding-sibling::enumValue)
-
count(key('koffsetEnums', $voffsetValueId)/preceding-sibling::enumValue)
"
/>
<xsl:text>
</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When the above transformation is applied on the originally provided XML document:
<enum name="anenum">
<enumValue name="a"/>
<enumValue name="b"/>
<enumValue name="c" data="10"/>
<enumValue name="d" />
<enumValue name="e" />
</enum>
the required result is produced:
enum anenum {
a = 1
b = 2
c = 10
d = 11
e = 12
}
A better solution with keys, avoiding most use of the preceding-sibling axis:
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output method="text"/>
<!-- -->
<xsl:key name="ksimpleEnValues" match="enumValue[not(#data)]"
use="generate-id(preceding-sibling::enumValue[#data][1])"/>
<!-- -->
<xsl:template match="enum">
enum <xsl:value-of select="#name"/>
{
<xsl:apply-templates select=
"key('ksimpleEnValues', '')
"/>
<xsl:apply-templates select="enumValue[#data]"/>
}
</xsl:template>
<!-- -->
<xsl:template match="enumValue">
<xsl:param name="pOffset" select="0"/>
<xsl:value-of select=
"concat(#name, ' = ', position()+$pOffset,'
')"/>
</xsl:template>
<!-- -->
<xsl:template match="enumValue[#data]">
<xsl:value-of select=
"concat(#name, ' = ', #data,'
')"/>
<!-- -->
<xsl:apply-templates select=
"key('ksimpleEnValues', generate-id())">
<xsl:with-param name="pOffset" select="#data"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
when applied on the originally-provided XML document:
<enum name="anenum">
<enumValue name="a"/>
<enumValue name="b"/>
<enumValue name="c" data="10"/>
<enumValue name="d" />
<enumValue name="e" />
</enum>
Produces the wanted result:
enum anenum
{
a = 1
b = 2
c = 10
d = 11
e = 12
}
Explanation:
The key named ksimpleEnValues indexes all enumValue elements that do not have the data attribute. The indexing is done by the generate-id() value of the first preceding enumValue element that has a data attribute.
Thus key('ksimpleEnValues', someId) is the nodeset containing all enumValue elements following the enumValue that has its generate-id() equal to someId, and all these enumValue elements are preceding the next enumValue with a data attribute, if such exists.
key('ksimpleEnValues', '') is the node-set of all enumValue elements that do not have a preceding enumValue element with a data attribute.
The template that matches enumValue takes an optional parameter $pOffset, in which the value of the data attribute from the immediate preceding enumValue element with this attribute, will be passed, otherwise the default value for $pOffset is 0.
The template matching enumValue elements that have a data attribute produces its enum-value (#name = #data) and then applies templates to all enumValue elements between itself and the next (if such exists) enumValue with a data attribute. The value of the data attribute is passed as the $pOffset parameter and it will be added to the relative position of each selected enumValue element when producing the output from its processing.
You can't change "variables" in xsl but you can use recursion. Don't use preceding-sibling predicates unless absolutely urgent as they will kill your performance.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="/" >
<xsl:call-template name="printEnum">
<xsl:with-param name="value" select="0"/>
<xsl:with-param name="position" select="1"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="printEnum">
<xsl:param name="position"/>
<xsl:param name="value" select="0"/>
<xsl:variable name="node" select="/enum/enumValue[$position]"/>
<xsl:variable name="enumValue">
<xsl:choose>
<xsl:when test="$node/#data">
<xsl:value-of select="$node/#data"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$value + 1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="concat($node/#name, ' = ', $enumValue, ' , ')"/>
<xsl:if test="/enum/enumValue[$position + 1]">
<xsl:call-template name="printEnum">
<xsl:with-param name="value" select="$enumValue"/>
<xsl:with-param name="position" select="$position + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>