cancel
Showing results for 
Search instead for 
Did you mean: 

Matching a Field and Adding an Attribute using XSLT

Former Member
0 Kudos

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.

Accepted Solutions (1)

Accepted Solutions (1)

engswee
Active Contributor
0 Kudos

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

JaySchwendemann
Active Contributor
0 Kudos

Damn , again being little too late

engswee
Active Contributor
0 Kudos

LOL , thanks for a light-hearted moment on SCN, amidst all the techno-mumbo-jumbo!

Former Member
0 Kudos

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.

engswee
Active Contributor
0 Kudos

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

Former Member
0 Kudos

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>

engswee
Active Contributor
0 Kudos

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 )

Former Member
0 Kudos

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.

Former Member
0 Kudos

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>

engswee
Active Contributor
0 Kudos

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

engswee
Active Contributor
0 Kudos

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

Former Member
0 Kudos

Thank you for this.

I edited the XSLT a little in order to get the required namespace declarations in the ase:aseXML field.

engswee
Active Contributor
0 Kudos

Excellent! Glad to hear that

Former Member
0 Kudos

Thank you very much for this, Eng Swee!

Answers (2)

Answers (2)

markangelo_dihiansan
Active Contributor
0 Kudos

Hi Yvonne,

Alternatively, you can use this blog for adding the attribute

As an example, here is what was done:

Output

Regards,

Mark

Former Member
0 Kudos

Hi Mark,

Thanks for this. I'll definitely try this out in another project. But, I can't use this now, because if I did this, I would have to update around 11 message mapping objects. On the other hand, if I just updated the XSLT mapping, and it works, I'm good to go.

JaySchwendemann
Active Contributor
0 Kudos

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

Former Member
0 Kudos

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.