Applescript send variable parameters to xslt file (updatedx2) - xslt

I have an applescript that is using xmltransform to transform a xml using and external xlst file. I would like to add a variable in applescript to be picked up by the xslt file.
The satimage dictionary talks about the use of xsl params as part of xmltransform but i cannot find any examples.
Using the template XMLTransform xmlFile with xsltFile in outputpath
how would i define the variables both in the applescript and in the following
xsl file example.
<xsl:stylesheet version="1.0">
<xsl:param name="vb1" value="'unknown'"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xmeml/list/name">
<xsl:value-of select="{$vb1}"/>
</xsl:template>
</xsl:stylesheet>
Applescript snippet
set vb1 to "Hello" as string
XMLTransform xmlFile with xsltFile1 in outputpathtemp xsl params vb1
The current applescript returns "cannot make vb1 into record".
I am now looking at using the following but it is returning NULL in the XML
set vb1 to {s:"'hello'"}
XMLTransform xmlFile with xsltFile1 in outputpathtemp xsl string params vb1
the input is
<xmeml>
<list>
<name>IMaName</name>
<!-- other nodes -->
</list>
</xmeml>
the current output is
<xmeml>
<list>
<name/>
<!-- other nodes -->
</list>
</xmeml>
Can anyone Help please?
many thanks.

When you are calling a XSLT with parameters, the parameters internally appear very similar to variables within the XSLT (and like parameters sent to a function), so if you pass a parameter in as a name:
<xsl:stylesheet version="1.0">
<xsl:parameter name="paramname" value="'unknown'"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xmeml/list/name">
<xsl:value-of select="{$paramname}"/>
</xsl:template>
</xsl:stylesheet>
when passing the parameter in this instance you would use the name 'paramname' (although maybe something else would be better!), and the value is there in case you don't pass anything. If you do, it would be replaced.

xsl params need a record or list
raw string parameter must be quoted
set vb1 to "'Hello'" -- added simple quote
XMLTransform xmlFile with xsltFile1 in outputpathtemp xsl params {vb1} -- {} = list

Here is the Answer:
applescript
XMLTransform xmlFile with xsltFile1 in outputpathtemp xsl params {"vb1", "'hello'"}
XSLT
<?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:param name="vb1" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/xmeml/list/name">
<xsl:element name="name">
<xsl:value-of select="$vb1"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Thanks.

Related

How do I rename an XML element with the value of its first attribute using XSLT?

I am new to XSLT and have XML structures that look like this:
<Loop LoopId="1000A" Name="SUBMITTER NAME">
.... a bunch of sub-elements, etc.
</Loop>
I am trying to write an XSLT that will convert them all to this:
(concatenate the value of the LoopId attribute to its parent element name)
<Loop1000A LoopId="1000A" Name="SUBMITTER NAME">
.... a bunch of sub-elements, etc.
</Loop1000A>
I have a style sheet that gets me almost all of the way there, but it is getting rid of the attribute LoopId and I don't know why - the style sheet below produces this result:
<Loop1000A Name="SUBMITTER NAME">
.... a bunch of sub-elements, etc.
</Loop1000A>
Is there a way to modify it so I keep the LoopId attribute?
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#LoopId"/>
<xsl:template match="*[#LoopId]">
<xsl:variable name="vRep" select="concat('Loop',#LoopId)"/>
<xsl:element name="{$vRep}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Thanks
Remove the template <xsl:template match="#LoopId"/> as that way you remove the LoopId attribute.
Change:
<xsl:element name="concat('Loop', #LoopId)">
to:
<xsl:element name="{concat('Loop', #LoopId)}">
See:
https://www.w3.org/TR/xslt/#attribute-value-templates

how to change a text of element in a parent tag using xslt

I want to replace the element tag name using xslt. I have an output like this:
<gl-cor:documentInfo>
<gl-cor:entriesType contextRef="journal_context">DocumentID</gl-cor:entriesType>
<gl-cor:uniqueID contextRef="journal_context">RevisionID</gl-cor:uniqueID>
</gl-cor:documentInfo>
<gl-cor:entityInformation>
<gl-cor:entityPhoneNumber>
<gl-cor:phoneNumber contextRef="journal_context">779633</gl-cor:phoneNumber>
</gl-cor:entityPhoneNumber>
<gl-cor:entityFaxNumberStructure>
<gl-cor:entityFaxNumbercontextRef="journal_context">1234-56-89</gl-cor:entityFaxNumber>
</gl-cor:entityFaxNumberStructure>
</gl-cor:entityInformation>
And, I want my output to be looks like this:
<gl-cor:documentInfo>
<gl-cor:entriesType contextRef="journal_context">DocumentID</gl-cor:entriesType>
<gl-bus:uniqueID contextRef="journal_context">RevisionID</gl-cor:uniqueID>
</gl-cor:documentInfo>
<gl-cor:entityInformation>
<gl-bus:entityPhoneNumber>
<gl-bus:phoneNumber contextRef="journal_context">779633</gl-bus:phoneNumber>
</gl-bus:entityPhoneNumber>
<gl-bus:entityFaxNumberStructure>
<gl-bus:entityFaxNumbercontextRef="journal_context">1234-56-89</gl-bus:entityFaxNumber>
</gl-bus:entityFaxNumberStructure>
</gl-cor:entityInformation>
All the children of <gl-cor:entityInformation> should replace instead of gl-cor, it should be gl-bus. Is it possible to do this?
I tried to create a sample xslt but it didn't work. The error occurs in the <gl-bus:phoneNumber>, because I think it is contain a special characters? like "-" and ":".
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="gl-cor:entityInformation/gl-cor:entityPhoneNumber/gl-cor:phoneNumber">
<gl-bus:phoneNumber>
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</gl-bus:phoneNumber>
</xsl:template>
Can someone help me solve this problem? Thanks alot.
First of all gl-cor and gl-bus are namespace prefixes. Namespace prefixes are written before an XML element and seperated from the XML element with a :. So your problem is not because of the characters - and :, these are all valid characters, please also read these articles/tutorials:
http://www.w3schools.com/xml/xml_namespaces.asp
http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references#Predefined_entities_in_XML
To answer your problem we need to know what the namspace URIs are for gl-cor and gl-bus, but it should look like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:gl-cor="http://example.org/gl-cor" xmlns:gl-bus="http://example.org/gl-bus">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[ancestor::gl-cor:entityInformation]">
<xsl:element name="gl-bus:{local-name()}">
<xsl:apply-templates select="#*|node()" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The template *[ancestor::gl-cor:entityInformation] will match on all (grand)children of gl-cor:entityInformation.
NOTE
The namespaces in the XSLT should be updated and match to your input XML:
xmlns:gl-cor="http://example.org/gl-cor"
xmlns:gl-bus="http://example.org/gl-bus"

Updating XML node using XSLT

<Instance xsi:type="ButtonConfig">
<Name>ExitButton</Name>
<Height>89</Height>
<Width>120</Width>
<Margin>
<All>-1</All>
<Bottom>0</Bottom>
<Left>400</Left>
<Right>0</Right>
<Top>11</Top>
</Margin>
</Instance>
In the above xml, I need to change the Left Margin to 420. How do I do it using XSLT?
This is almost the “identify transform”, which simply duplicates the input document.
Here’s a simple stylesheet that mostly performs the identity transform, while overriding the output for a <Left/> within a <Margin/> within an <Instance/> that has a <Name/> containing ExitButton. Note that I had to add a namespace definition to your input XML for xsi, which I assume is elsewhere in the document.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Margin/Left[ancestor::Instance/Name[text()='ExitButton']]">
<Left>420</Left>
</xsl:template>
</xsl:stylesheet>
As any XSLT tutorial would tell you: For something simple like this, start with the identity stylesheet, which copies the document essentially unchanged... then add a template which implements the exception to that.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Margin/Left">
<xsl:copy>
<xsl:text>420</xsl:text>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Issue while applying apply templates

I have a xml which I want to get in the expected format as shown below. I am trying to use apply templates concept to this. But some how, I am not able to see the expected result.
<?xml version="1.0" encoding="UTF-8"?>
<SOAP:ENV>
<SOAP:HEADER/>
<SOAP:BODY>
<OutputResponse>
<RespStructure ID="1">
<RespStatus>Success</RespStatus>
<RespMessage>
<Country>Australia</Country>
<Capital>Canberra</Capital>
</RespMessage>
<RespMessage>
<Country>England</Country>
<Capital>London</Capital>
</RespMessage>
<RespMessage>
<Country>China</Country>
<Capital>Beijing</Capital>
</RespMessage>
</RespStructure>
</OutputResponse>
</SOAP:BODY>
</SOAP:ENV>
Now in this message, I am having RespMessage and RespStatus both as part of RespStructure. But Respstatus is a single nodeset where RespMessage is a multiple values nodeset(Country, Capital). When I am using apply templates, either only first RespMessage is getting selected (2nd repetition not appearing) or Respstatus not giving it's value. I try to get below output.
<?xml version="1.0" encoding="UTF-8"?>
<SOAP:ENV>
<SOAP:HEADER/>
<SOAP:BODY>
<OutputResponse>
<RespStructure ID="1">
<TransactionStatus>Success</TransactionStatus>
<ListOfCountries>
<SelectedCountry>Australia</SelectedCountry>
<FIrstSelectedCapital>Canberra</FIrstSelectedCapital>
</ListOfCountries>
<ListOfCountries>
<SelectedCountry>England</SelectedCountry>
<FIrstSelectedCapital>London</FIrstSelectedCapital>
</ListOfCountries>
<ListOfCountries>
<SelectedCountry>China</SelectedCountry>
<FIrstSelectedCapital>Beijing</FIrstSelectedCapital>
</ListOfCountries>
</RespStructure>
</OutputResponse>
</SOAP:BODY>
</SOAP:ENV>
I am changing all the names of fields, but I should get all multiple nodes without any missing.
I used below code snippet from forum, but I failed to apply. How can I populate RespStatus using below snippet. When I gave different template, both are not called and only one got printed. I tried to change apply templates to more specific nodeset also.
<xsl:template match="#*|node()">
<xsl:apply-templates select="#*|node()" />
</xsl:template>
<xsl:template match="Body">
<SOAPENV>
<Header/>
<OutputResponse>
<xsl:apply-templates select="#*|node()" />
</OutputResponse>
</SOAPENV>
</xsl:template>
<xsl:template match="RespMessage">
<ListOfCountries>
<SelectedCountry><xsl:value-of select="Country" /></SelectedCountry>
<FIrstSelectedCapital><xsl:value-of select="Capital" /></FIrstSelectedCapital>
</ListOfCountries>
</xsl:template>
Thanks for response. Apologies for any spell mistakes.
Looking at your XSL snippet, it should match, and transform all RespMessage elements, unless there is some other part of the XSLT, which you have not shown, that is affecting things.
One issue you do have, and this might be just how you have editted your question, is that your original XML uses the namespace prefix "SOAP", but you have not declared a namespace for this prefix. I would expect the first line to be something like this
<SOAP:ENV xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
Without this, you will not be able to apply your XSLT on it all.
The use of namespaces also means the second template, matching body, won't match anything
<xsl:template match="Body">
(Actually, it won't match anything because it is case sensitive, and in the original XML it is SOAP:BODY)
Looking at the rest of the XSLT, if you want to copy across existing elements without changing, you should be using the identity template. The first template in your XSLT should look like this
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
With this in place, all you need is to add a template to match, and transform RespStatus.
<xsl:template match="RespStatus">
<xsl:element name="TransactionStatus">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="RespStatus">
<xsl:element name="TransactionStatus">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
<xsl:template match="RespMessage">
<xsl:element name="ListOfCountries">
<SelectedCountry><xsl:value-of select="Country" /></SelectedCountry>
<FIrstSelectedCapital><xsl:value-of select="Capital" /></FIrstSelectedCapital>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

remove elements based on external file

I have an external setting file which has some nodes holiding attribute values of main xml document. I need to remove certian nodes from mian xml file if the attribute value is there in the setting file.
My setting file looks like this:
setting.xml
<xml>
<removenode titlename="abc" subtitlename="xyz"></removenode>
<removenode titlename="dvd" subtitlename="dvd"></removenode>
</xml>
Main.xml
<xml>
<title titlename="abc">
<subtitle subtitlename="xyz"></subtitle>
</title>
<title titlename="book">
<subtitle subtitlename="book sub title"></subtitle>
</title>
</xml>
Need a script which look for setting.xml file and remove the title element if titlename and subtitlename found in main.xml. The output should be
output.xml
<xml>
<title titlename="book">
<subtitle subtitlename="book sub title"></subtitle>
</title>
</xml>
I tried using document to read setting.xml file but not able to find how to do the match on main.xml file
<xsl:variable name="SuppressionSettings" select="document('Setting.xml')" />
<xsl:variable name="SuppressSetting" select="$SuppressionSettings/xml/removenode" />
.
Any hint how to implement it?
The key is to use an identity/copy pattern and, before each output, check the current (context) node isn't prohibited by the suppression rules nodeset.
<!-- get suppression settings -->
<xsl:variable name='suppression_settings' select="document('http://www.mitya.co.uk/xmlp/settings.xml')/xml/removenode" />
<!-- begin identity/copy -->
<xsl:template match="node()|#*">
<xsl:if test='not($suppression_settings[#titlename = current()/#titlename and #subtitlename = current()/subtitle/#subtitlename])'>
<xsl:copy>
<xsl:apply-templates select='node()|#*' />
</xsl:copy>
</xsl:if>
</xsl:template>
You can run it here (see output source - the 'abc' title node is omitted):
http://www.xmlplayground.com/9oCYKp
This XSLT indicated below works for the given document.
Note that I'm storing the contents of Setting.xml in a variable as you did, however, I'd then use that variable directly in my queries.
An important issue here is that in the match element of a template, variables cannot be used. Therefore, my template matches any <title> elements and then determines in an <xsl:choose> element whether the attributes match any values given in the settings file - if so, the <title> element will be omitted in the output.
As an explanation for why that test attribute in the <xsl:when> does what it should, imagine a comparison of someAttribute = someOtherAttribute not as a restriction that the attribute someAttribute must have the same value as the attribute someOtherAttribute, but rather as the condition that there must be any two attributes someAttribute and someOtherAttribute with the same value.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="SuppressionSettings" select="document('Setting.xml')" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//title">
<xsl:choose>
<xsl:when test="(#titlename = $SuppressionSettings/xml/removenode/#titlename) and (subtitle/#subtitlename = $SuppressionSettings/xml/removenode/#subtitlename)"/>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Here's a more generic answer where the names of the attributes are not hard coded into the XSLT. Like O. R. Mapper pointed out, in XSLT 1.0 you can't use variable references in the match, so I put the document() directly in the predicate. This may not be as efficient as using a variable and then testing the variable.
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[#* = document('setting.xml')/*/removenode/#*]"/>
</xsl:stylesheet>
XML Output (using your 2 xml files with main.xml as the input)
<xml>
<title titlename="book">
<subtitle subtitlename="book sub title"/>
</title>
</xml>