10-14-2008 7:40 PM
Hi Experts,
I am trying to do deep nesting in a XSLT transformation, but canu2019t get it to work. I also looked into Simple Transformations as an alternative but there the XML would have to be adjusted with the table tags for the loop which is definitely not an option for us.
Here is what I try to achieve, I built a basic example to show what it needs to do:
XML input File:
<?xml version="1.0" encoding="utf-8"?>
<HEADER>
<PERSON>
<NAME>Some Name</NAME>
<ADDRESS>
<NUMBER>111</NUMBER>
<STREET>Test Street</STREET>
</ADDRESS>
</PERSON>
</HEADER>
The required ABAP output should be an internal table of the following type:
Table column 1: NAME
Table column 2: a table with the following structure:
Nested table column 1: NUMBER
Nested table column 2: STREET
I tried the following with a nested structure rather than a nested table which did work fine.
But once I change the XSLT to accept more than one ADDRESS and change the ABAP data structures to accept a table rather than a structure, the nested table data is just not filled in. Unfortunately I was not able to find any example in the SAP system or on the net that is using a nested table and does work. I also played around with the [Airplus example|https://www.sdn.sap.com/irj/sdn/wiki?path=/display/snippets/readdatafromXMLfileviaXSLT+program&] and was able to get that to work, but as soon as I introduce a nested tables on a level below the first level the data for that part is no longer populated.
What am I missing here?
Please find below the coding for the first example with the nested structure and the second example with the nested table (which doesnu2019t work).
ABAP report with nested structure (does work):
REPORT zwa01_trans.
* Data Definitions --------------------------------------------------
TYPES: BEGIN OF ty_address,
number TYPE char10,
street TYPE char40,
END OF ty_address.
TYPES: BEGIN OF ty_person,
name TYPE char20,
address TYPE ty_address,
END OF ty_person.
TYPES: BEGIN OF ty_xml_line,
data(256) TYPE x,
END OF ty_xml_line.
DATA: gt_in TYPE TABLE OF ty_xml_line,
gt_out TYPE TABLE OF ty_person.
CALL FUNCTION 'GUI_UPLOAD'
EXPORTING
filename = 'C:Documents and SettingswackerbauermMy Documents6 - TEMP estxml03.xml'
TABLES
data_tab = gt_in.
TRY.
CALL TRANSFORMATION zwa01_test03
SOURCE XML gt_in
RESULT xml_output = gt_out.
CATCH cx_xslt_exception.
EXIT.
ENDTRY.
BREAK-POINT.
XSLT Program with nested structure (does work):
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sap="http://www.sap.com/sapxsl" version="1.0">
<xsl:template match="HEADER">
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
<asx:values>
<XML_OUTPUT>
<PERSON>
<NAME>
<xsl:value-of select="PERSON/NAME"/>
</NAME>
<ADDRESS>
<NUMBER>
<xsl:value-of select="PERSON/ADDRESS/NUMBER"/>
</NUMBER>
<STREET>
<xsl:value-of select="PERSON/ADDRESS/STREET"/>
</STREET>
</ADDRESS>
</PERSON>
</XML_OUTPUT>
</asx:values>
</asx:abap>
</xsl:template>
</xsl:transform>
XML file (does work):
<?xml version="1.0" encoding="utf-8"?>
<HEADER>
<PERSON>
<NAME>Some Name</NAME>
<ADDRESS>
<NUMBER>111</NUMBER>
<STREET>Test Street</STREET>
</ADDRESS>
</PERSON>
</HEADER>
Now I tried to expand this into a nested table and changed the XSLT as follows (does not return nested table values):
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sap="http://www.sap.com/sapxsl" version="1.0">
<xsl:template match="HEADER">
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
<asx:values>
<XML_OUTPUT>
<PERSON>
<NAME>
<xsl:value-of select="PERSON/NAME"/>
</NAME>
<xsl:for-each select="ADDRESS"> <<<<INSERT
<ADDRESS>
<NUMBER>
<xsl:value-of select="PERSON/ADDRESS/NUMBER"/>
</NUMBER>
<STREET>
<xsl:value-of select="PERSON/ADDRESS/STREET"/>
</STREET>
</ADDRESS>
</xsl:for-each> <<<<INSERT
</PERSON>
</XML_OUTPUT>
</asx:values>
</asx:abap>
</xsl:template>
</xsl:transform>
After that I adjusted the ABAP code as follows:
REPORT zwa01_trans.
* Data Definitions --------------------------------------------------
TYPES: BEGIN OF ty_address,
number TYPE char10,
street TYPE char40,
END OF ty_address.
TYPES: BEGIN OF ty_person,
name TYPE char20,
address TYPE ty_address occurs 0, <<< ADDED OCCURS 0
END OF ty_person.
This will add a nested table. The XML remained unchanged (It will just create one entry in the nested table). When executing the report now you can see that the table has been nested in the original structure, but is just empty. The curious thing is if I add ADDRESS more than once (e.g.) five times I get five empty lines in the nested table but they are all empty - so it does recognize the repeating tags, but just not transferring the data.
I also tried
address like standard table of gt_address
where gt_address is a data structure of number and street. I even created the structure in the dictionary, but that didnu2019t make a difference either.
I am out of ideas - anybody stumbled accros this and found a solution? I tested it in SAP 4.7 and ECC 6.0 with the same result.
Thank you,
Michael
10-14-2008 8:15 PM
Hi,
Did you test your XSLT program offline in Altova XML spy or any other similar tool ?
regards,
Advait
10-14-2008 8:15 PM
Hi,
Did you test your XSLT program offline in Altova XML spy or any other similar tool ?
regards,
Advait
10-15-2008 2:46 PM
Hello Advait,
No I didn't test my XSLT in any offline tool, however I tested the XSLT in SE80 and it didn't return the data there either once I introduced xsl:for-each.
Michael
11-19-2008 3:59 PM
MIchael,
i think you have to use for-each with the XPATH preceding-sibling function.
Check the docu for "preceding-sibling" or "following-sibling".
Regards,
Gordon
11-24-2008 3:43 PM
Hello Gordon,
I tried that as well in different flavors but unfortunately it didn't work either. In the meantime since we had to get this working we are using the SAP DOM object and some custom code to interpret the DOM object and create the appropriate internal table.
Thanks anyway,
Michael
11-24-2008 9:57 PM
Hello Michael
The following sample reports shows the transformation complex itab -> XML and XML -> complex itab.
*&---------------------------------------------------------------------*
*& Report ZUS_SDN_XSLT_NESTED_ITAB
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*
REPORT zus_sdn_xslt_nested_itab.
TYPE-POOLS: abap, trwbo.
DATA: gt_requests_pbo TYPE trwbo_requests, " complex itab
gt_requests_pai TYPE trwbo_requests.
DATA: gd_xml TYPE string,
gt_data TYPE TABLE OF string.
DATA: go_xml_doc TYPE REF TO cl_xml_document,
gd_rc TYPE i.
PARAMETERS:
p_trkorr TYPE trkorr DEFAULT 'EE2K902337'.
START-OF-SELECTION.
CALL FUNCTION 'TR_READ_REQUEST_WITH_TASKS'
EXPORTING
iv_trkorr = p_trkorr
IMPORTING
* ET_REQUEST_HEADERS =
et_requests = gt_requests_pbo
EXCEPTIONS
invalid_input = 1
OTHERS = 2.
IF sy-subrc <> 0.
* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
* WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
ENDIF.
BREAK-POINT.
CALL TRANSFORMATION indent
SOURCE itab = gt_requests_pbo
RESULT XML gd_xml.
CREATE OBJECT go_xml_doc.
CALL METHOD go_xml_doc->parse_string
EXPORTING
stream = gd_xml
RECEIVING
retcode = gd_rc.
go_xml_doc->display( ).
CALL METHOD go_xml_doc->export_to_file
EXPORTING
filename = 'C:\temp\ZZ_nested_itab.xml'
RECEIVING
retcode = gd_rc.
CLEAR: go_xml_doc.
CLEAR: gd_xml.
CREATE OBJECT go_xml_doc.
CALL METHOD go_xml_doc->import_from_file
EXPORTING
filename = 'C:\temp\ZZ_nested_itab.xml'
RECEIVING
retcode = gd_rc.
CALL METHOD go_xml_doc->render_2_string
EXPORTING
pretty_print = 'X'
IMPORTING
retcode = gd_rc
stream = gd_xml
* size =
.
CALL TRANSFORMATION indent
SOURCE XML gd_xml
RESULT itab = gt_requests_pai.
IF ( gt_requests_pai = gt_requests_pbo ).
MESSAGE 'Itabs are equal' TYPE 'S'.
ELSE.
MESSAGE 'Itabs are NOT equal' TYPE 'S'.
ENDIF.
IF sy-subrc <> 0.
* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
* WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
ENDIF.
END-OF-SELECTION.
The XML for a sample transport request looks like this:
<?xml version="1.0" encoding="utf-8"?>
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
<asx:values>
<ITAB>
<item>
<H>
<TRKORR>BEPK900061</TRKORR>
<TRFUNCTION>T</TRFUNCTION>
<TRSTATUS>R</TRSTATUS>
<TARSYSTEM>BEP</TARSYSTEM>
<KORRDEV>SYST</KORRDEV>
<AS4USER>MH</AS4USER>
<AS4DATE>2008-10-09</AS4DATE>
<AS4TIME>15:03:20</AS4TIME>
<STRKORR/>
<AS4TEXT>test huwi</AS4TEXT>
<AS4TEXT_FILLED>X</AS4TEXT_FILLED>
<CLIENT>000</CLIENT>
<TARCLIENT/>
<CLIENTS_FILLED>X</CLIENTS_FILLED>
<TARDEVCL/>
<DEVCLASS/>
<TARLAYER/>
<E070M_FILLED/>
</H>
<OBJECTS>
<item>
<TRKORR>BEPK900061</TRKORR>
<AS4POS>000001</AS4POS>
<PGMID>R3TR</PGMID>
<OBJECT>PMKS</OBJECT>
<OBJ_NAME>P0001</OBJ_NAME>
<OBJFUNC/>
<LOCKFLAG/>
<GENNUM/>
<LANG/>
<ACTIVITY/>
</item>
<item>
<TRKORR>BEPK900061</TRKORR>
<AS4POS>000002</AS4POS>
<PGMID>R3TR</PGMID>
<OBJECT>PMKS</OBJECT>
<OBJ_NAME>P0002</OBJ_NAME>
<OBJFUNC/>
<LOCKFLAG/>
<GENNUM/>
<LANG/>
<ACTIVITY/>
</item>
<item>
<TRKORR>BEPK900061</TRKORR>
<AS4POS>000003</AS4POS>
<PGMID>R3TR</PGMID>
<OBJECT>PMKS</OBJECT>
<OBJ_NAME>P0006</OBJ_NAME>
<OBJFUNC/>
<LOCKFLAG/>
<GENNUM/>
<LANG/>
<ACTIVITY/>
</item>
</OBJECTS>
<KEYS/>
<OBJECTS_FILLED>X</OBJECTS_FILLED>
<ATTRIBUTES/>
<ATTRIBUTES_FILLED/>
</item>
</ITAB>
</asx:values>
</asx:abap>
<ITAB> is the entire complex itab whereas <OBJECTS> is the nested itab containing the object keys.
Your XSLT transformation has the create the same structure as above and then it should work.
Regards
Uwe
11-24-2008 10:42 PM
Hello Michael
I have solved this enigma: You need to replace XML_OUTPUT with ITAB in your XSLT transformation !!!!
Do not ask me why.
Below you see my sample report ZUS_SDN_XSLT_COMPLEX_ITAB followed by the XML file and the XLST transformation.
*&---------------------------------------------------------------------*
*& Report ZWA01_TRANS
*&
*&---------------------------------------------------------------------*
*& Thread: Problem with nested tables in XSLT transformation (XML to ABAP)
*& <a class="jive_macro jive_macro_thread" href="" __jive_macro_name="thread" modifiedtitle="true" __default_attr="1086843"></a>
*&---------------------------------------------------------------------*
REPORT zus_sdn_xslt_complex_itab.
* Data Definitions --------------------------------------------------
TYPES: BEGIN OF ty_address,
number TYPE char10,
street TYPE char40,
END OF ty_address.
TYPES: BEGIN OF ty_person,
name TYPE char20,
address TYPE STANDARD TABLE OF ty_address
WITH DEFAULT KEY,
END OF ty_person.
TYPES: BEGIN OF ty_xml_line,
data(256) TYPE c,
END OF ty_xml_line.
DATA: go_xml_doc TYPE REF TO cl_xml_document,
gd_rc TYPE i.
DATA: gt_in TYPE TABLE OF ty_xml_line,
gs_out TYPE ty_person,
gs_address TYPE ty_address,
gt_out TYPE TABLE OF ty_person
WITH DEFAULT KEY.
DATA: gt_xml TYPE STANDARD TABLE OF string.
DATA: gd_xml TYPE string,
gd_xml_nested TYPE string.
START-OF-SELECTION.
** <?xml version="1.0" encoding="utf-8"?>
** <HEADER>
** <PERSON>
** <NAME>Some Name</NAME>
** <ADDRESS>
** <NUMBER>111</NUMBER>
** <STREET>Test Street</STREET>
** </ADDRESS>
** </PERSON>
** </HEADER>
** CALL FUNCTION 'GUI_UPLOAD'
** EXPORTING
** filename = 'C: empsdn_XSLTaddress_sample.xml'
** TABLES
** data_tab = gt_in.
BREAK-POINT.
CREATE OBJECT go_xml_doc.
CALL METHOD go_xml_doc->import_from_file
EXPORTING
filename = 'C: empsdn_XSLTaddress_sample.xml'
RECEIVING
retcode = gd_rc.
CALL METHOD go_xml_doc->render_2_string
EXPORTING
pretty_print = 'X'
IMPORTING
retcode = gd_rc
stream = gd_xml
* size =
.
go_xml_doc->display( ).
TRY.
CALL TRANSFORMATION zus_sdn_complex_itab
SOURCE XML gd_xml
RESULT XML gd_xml_nested.
CALL METHOD go_xml_doc->parse_string
EXPORTING
stream = gd_xml_nested
RECEIVING
retcode = gd_rc.
go_xml_doc->display( ).
** <?xml version="1.0" encoding="UTF-8"?>
** <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
** <asx:values>
** <ITAB>
** <item>
** <NAME>Some Name</NAME>
** <ADDRESS>
** <item>
** <NUMBER>111</NUMBER>
** <STREET>Test Street</STREET>
** </item>
** </ADDRESS>
** </item>
** </ITAB>
** </asx:values>
** </asx:abap>
call TRANSFORMATION id
SOURCE XML gd_xml_nested
result itab = gt_out.
CATCH cx_xslt_exception.
EXIT.
ENDTRY.
XML sample file:
<?xml version="1.0" encoding="utf-8"?>
<HEADER>
<PERSON>
<NAME>Some Name</NAME>
<ADDRESS>
<NUMBER>111</NUMBER>
<STREET>Test Street</STREET>
</ADDRESS>
</PERSON>
</HEADER>
XSL transformation:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="HEADER">
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
<asx:values>
<ITAB>
<xsl:for-each select="PERSON">
<item>
<NAME>
<xsl:value-of select="NAME"/>
</NAME>
<ADDRESS>
<xsl:for-each select="ADDRESS">
<item>
<NUMBER>
<xsl:value-of select="NUMBER"/>
</NUMBER>
<STREET>
<xsl:value-of select="STREET"/>
</STREET>
</item>
</xsl:for-each>
</ADDRESS>
</item>
</xsl:for-each>
</ITAB>
</asx:values>
</asx:abap>
</xsl:template>
</xsl:transform>
Regards
Uwe
11-24-2008 10:49 PM
Hi,
I think you will have to give the entire path of the ADDRESS.
i.e PERSON/ADDRESS
<xsl:for-each select="PERSON/ADDRESS">
Check this [Example program|http://www.w3schools.com/Xsl/xsl_for_each.asp] for further details on how to use for-each.
regards,
Advait
06-18-2012 6:51 PM
Hi, I had a similar problem. The main problem is how abap translate the internal table to xml:
"For each table a "item" object is created for each line in the table"
Example
Table def:
TYPES:
BEGIN OF t_w_detalle,
grossamount TYPE string,
END OF t_w_detalle,
BEGIN OF t_w_items,
posicionpedido TYPE string,
detalle TYPE t_w_detalle OCCURS 0,
END OF t_w_items,
END OF t_w_invoices,
begin of t_w_xml,
items TYPE t_w_items OCCURS 0,
end of t_w_xml.
data: w_xml type t_w_xml.
Converted ABAP TABLE to XML:
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
<asx:values>
<FACTURAEV32>
<INVOICES>
<ITEMS>
<item>
<POSICIONPEDIDO>1112577</POSICIONPEDIDO>
<DETALLE>
<item>
<GROSSAMOUNT>3950.000000</GROSSAMOUNT>
</item>
<item>
<GROSSAMOUNT>345.000000</GROSSAMOUNT>
</item>
</DETALLE>
</item>
</ITEMS>
</INVOICES>
</FACTURAEV32>
</asx:values>
</asx:abap>
In the xslt you can call it :
<-- CODE -->
<xsl:apply-templates select="INVOICES/ITEMS"/>
<-- CODE -->
<xsl:template match="ITEMS">
<xsl:for-each select="item">
<-- CODE -->
<xsl:apply-templates select="DETALLE"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="DETALLE">
<xsl:for-each select="item">
<-- CODE -->
</xsl:for-each>
</xsl:template>
U can also debug the xslt to have a detailed view of ur xml and how it's processed
Good luck !
pd: sorry for my english