on 02-12-2015 2:48 PM
Hi Experts,
My build requires the use of a standard XSD imported as an external definition. This XSD is used for multiple inbound and outbound interfaces.
Now, I am not allowed to edit the XSD. However, I need to add the attribute "xsi:type" to a specific field<ReportParameter> using XSLT.
If my input XML looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Message xmlns:ase="urn:reportheader:one" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance>
<Header>
<MessageID>DummyID</MessageID>
<MessageDate>2015-02-12</MessageDate>
</Header>
<Transaction>
<MainTransaction transactionID="12765FFH">
<ReportName>My Report</ReportName>
<ReportNumber>17624</ReportNumber>
<ReportParameter>
<Parameter1>Dummy Parameter</Parameter1>
<Parameter2>Dummy Parameter2</Parameter2>
<Parameter3>Dummy Parameter3</Parameter3>
</ReportParameter>
<ReportDescription>
<RHeader>Report Header</RHeader>
<RDescription>Description of report submitted</RDescription>
</ReportDescription>
</MainTransaction>
</Transaction>
</Message>
The output xml should look like this:
<?xml version="1.0" encoding="utf-8"?>
<Message xmlns:ase="urn:reportheader:one" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance>
<Header>
<MessageID>DummyID</MessageID>
<MessageDate>2015-02-12</MessageDate>
</Header>
<Transaction>
<MainTransaction transactionID="12765FFH">
<ReportName>My Report</ReportName>
<ReportNumber>17624</ReportNumber>
<ReportParameter xsi:type="NewEntryReport">
<Parameter1>Dummy Parameter</Parameter1>
<Parameter2>Dummy Parameter2</Parameter2>
<Parameter3>Dummy Parameter3</Parameter3>
</ReportParameter>
<ReportDescription>
<RHeader>Report Header</RHeader>
<RDescription>Description of report submitted</RDescription>
</ReportDescription>
</MainTransaction>
</Transaction>
</Message>
How can I search for <ReportParameter> and add the attribute "xsi:type" with value "NewEntryReport"?
I've tried matching the field name like this: <xsl:template match="ReportParameter">
but it doesn't work.
When I tried 'match="\"', I ended up deleting everything except for the "ReportParameter" segment.
I've also tried using "for-each", but I ended up creating a new copy of the "ReportParameter" segment.
Please help.
Hi Yvonne
Looks like you have a missing closing " for the declaration of the xsi namespace.
After fixing the input XML I ran the following XSLT on Online XSLT Test Tool and it works fine.
You need to declare the namespace for xsi also in the stylesheet.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:template match="ReportParameter">
<ReportParameter xsi:type="NewReportEntry">
<xsl:apply-templates select="@* | node()"/>
</ReportParameter>
</xsl:template>
<!-- Match all other attributes (@*) and other node types (elements, comments) -->
<!-- Copy the current element, and select child attributes and nodes -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Rgds
Eng Swee
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hey guys,
Thanks for your reply. But, I already tried this one and, it didn't work.
Ah, sorry. A little background: I'm using SAP PI 7.4 single stack, and for some reason I don't quite know, using <xsl:template match="ReportParameter"> (or any other field name) does not work. I've also tried using <xsl:template match="//ReportParameter"> but that doesn't work either. It won't let me match a field name.
Hi Yvonne
I'm not sure why it's not working for you. I'm on PO 7.4 SP8 and it works perfectly fine for me.
Here is the XSLT code when viewed via the Imported Archive browser (which is exactly the same as above)
Here is the result when I execute it from the Operation Mapping.
Can you please check that the SAP XML Toolkit is unchecked?
If it does not work, can you please paste your exact XSLT code here. And also provide screenshot of your output. Probably we can then try to figure out if something is missing that is causing the difference in behavior.
Rgds
Eng Swee
Hi Eng Swee Yeoh,
Thank you for this.
My XSLT code includes adding a custom SOAP Envelope as well as other things. Adding the xsi:type attribute is the latest template addition to it (last template below).
This is how the code looks:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:brav="http://tempuri.net" xmlns:ase="urn:reportheader:one" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0" exclude-result-prefixes="xsi xsl soapenv ns0 ns1 ase brav">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*" mode="copy">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:apply-templates select="@*|node()" mode="copy" />
</xsl:element>
</xsl:template>
<xsl:template match="@*|text()|comment()" mode="copy">
<xsl:copy/>
</xsl:template>
<xsl:template match="/">
<soapenv:Envelope xmlns:brav="http://tempuri.net" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<brav:MessagePayload>
<ase:aseXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ase="urn:reportheader:one">
<xsl:apply-templates select="//Header" mode="copy"/>
<xsl:apply-templates select="//Transaction" mode="copy"/>
</ase:aseXML>
</brav:MessagePayload>
</soapenv:Body>
</soapenv:Envelope>
</xsl:template>
<xsl:template match="ReportParameter">
<NMIStandingData xsi:type="ase:NewEntryReport">
<xsl:apply-templates select="*" mode="copy"/>
</NMIStandingData>
</xsl:template>
</xsl:stylesheet>
Again.. the rabbit hole gets deeper and deeper
Now it makes sense because there are other templates that would have match the ReportParameter node first. Therefore it did not reach that particular match.
I'll have a look at your code and see what could/should be changed. I'm assuming I can still use the input XML from your original post to test it out. If that's not the case, please reply and if possible provide a more accurate source (so that I don't go on another wild goose chase )
Hi Eng Swee,
I am also encountering the same issue as Yvonne's. Your answers are really helpful. However, the problem still persists. Regarding this, can you confirm whether the difference in namespace is an issue here or not? One more thing, how does an XSLT processor process a stylesheet? Is there an order of precedence on when templates are being executed, or just the conventional righ-to-left, top-to-bottom approach? Thank you for your prompt response.
Yes, the same input XML as the one originally posted can still be used:
<?xml version="1.0" encoding="utf-8"?>
<Message xmlns:ase="urn:reportheader:one" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance>
<Header>
<MessageID>DummyID</MessageID>
<MessageDate>2015-02-12</MessageDate>
</Header>
<Transaction>
<MainTransaction transactionID="12765FFH">
<ReportName>My Report</ReportName>
<ReportNumber>17624</ReportNumber>
<ReportParameter>
<Parameter1>Dummy Parameter</Parameter1>
<Parameter2>Dummy Parameter2</Parameter2>
<Parameter3>Dummy Parameter3</Parameter3>
</ReportParameter>
<ReportDescription>
<RHeader>Report Header</RHeader>
<RDescription>Description of report submitted</RDescription>
</ReportDescription>
</MainTransaction>
</Transaction>
</Message>
Hi Yvonne
I've reworked your XSLT logic and came up with the below. I'm not sure what some of your match templates were for, but my guess is your overall intention is to add a SOAP envelope and change the ReportParameter to NMIStanding with an additional attribute.
<xsl:stylesheet xmlns:brav="http://tempuri.net" xmlns:ase="urn:reportheader:one" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="*[local-name()='Message']">
<soapenv:Envelope xmlns:brav="http://tempuri.net" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<brav:MessagePayload>
<ase:aseXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ase="urn:reportheader:one">
<xsl:apply-templates select="Header"/>
<xsl:apply-templates select="Transaction"/>
</ase:aseXML>
</brav:MessagePayload>
</soapenv:Body>
</soapenv:Envelope>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ReportParameter">
<NMIStandingData xsi:type="ase:NewReportEntry">
<xsl:apply-templates select="@* | node()"/>
</NMIStandingData>
</xsl:template>
</xsl:stylesheet>
Here are the results when testing from the Operation Mapping.
If it still does not work, hopefully this is the base where you can start off and continue tweaking. If you hit any more roadblocks, feel free to holler.
Rgds
Eng Swee
Hi Bernard
Welcome to SCN!
To be honest, I'm not the resident expert on XSLT. I only use it sparingly and only when the benefits of XSLT outweighs other options. To answer your question, I turned to Mr Google which led me to the article below
How XSLT Works — Lenz Consulting Group, Inc.
If you read the conflict resolution and priority section, it might give you a better picture.
Regarding your issue, if you still cannot resolve it, I would suggest you to open a new thread and provide as much details as possible - as you can see from above, sometimes if we don't have the full picture, we can sometimes provide a solution that might not be suitable. I wouldn't be able to confirm if you issue is because of namespace difference or not without actual details of your input XML, XSLT logic, etc.
Rgds
Eng Swee
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
How about this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="xml" indent="yes"/>
<!-- matches on each book -->
<xsl:template match="/Message/Transaction/MainTransaction/ReportParameter">
<xsl:copy>
<xsl:attribute name="xsi:type">NewEntryReport</xsl:attribute>
<!-- identity template - copies content forward -
This includes Authors and Author children-->
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
HTH
Cheers
Jens
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Jens,
Thanks for this alternative. If it affected only one structure it would be fine, but the XSLT will be reused by some 11 mappings with varying structures, but all of them have the "ReportParameter" which requires the additional attribute. I've tried the double slash ("//") as a substitute for the first parts of the xpath and to make it more dynamic (match="//ReportParameters"), but that doesn't work for the template match part of the code.
User | Count |
---|---|
81 | |
10 | |
10 | |
9 | |
7 | |
6 | |
6 | |
5 | |
4 | |
4 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.