Dear mates,

Here is the next part of my blog related to OS functionality. You can find the beginning here:

Persistent classes: revival of a spirit. Query by range.

I presented the way how we can use local variables as more transparent way to create a query request.

As a result of IF_OS_CA_PERSISTENCY~GET_PERSISTENT_BY_QUERY method we have a table of OSREFTAB type.

So let’s first consider how SAP proposes to process such references in DEMO_QUERY_SERVICE example:


LOOP AT connections ASSIGNING FIELD-SYMBOL(<connection>).
          connection = CAST #( <connection> ).
          result-carrid = connection->get_carrid( ).
          result-connid = connection->get_connid( ).
          APPEND result TO results.
        ENDLOOP.

That’s probably OK – but let’s assume we have really pretty big number of attributes. The idea of numerous get_() method calls each time i want to read the full object structure didn’t make me happy. I’m too lazy for it.

In my background there were several tasks when I used asXML serialization-deserialization and i decided to try it here:

The logic should be very simple:

  1. we serialize our object to asXML for object instance

When I tried to serialize CL_SPFLI_PERSISTENT instance i got nothing interesting:


<asx:abap version="1.0" xmlns:asx="http://www.sap.com/abapxml">
<asx:values>
  <STRUC href="#o52"/>
</asx:values>
 <asx:heap xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:abap="http://www.sap.com/abapxml/types/built-in" xmlns:cls="http://www.sap.com/abapxml/classe//www.sap.com/abapxml/types/dictionary">
  <cls:CL_SPFLI_PERSISTENT id="o52"/>
</asx:heap>
</asx:abap>

The reason of such a short file is that object must have IF_SERIALIZABLE_OBJECT interface to be serialized.

As I was going to use it for my own object I just created new Z class for SPFLI table but with this interface defined.

This time the result looked rather better:


<asx:abap version="1.0" xmlns:asx="http://www.sap.com/abapxml">
<asx:values>
  <STRUC href="#o52"/>
</asx:values>
 <asx:heap xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:abap="http://www.sap.com/abapxml/types/built-in" xmlns:cls="http://www.sap.com/abapxml/classes/global" xmlns:dic="http://www.sap.com/abapxml/types/dictionary">
  <cls:ZCL_SPFLI_PERSISTENT id="o52">
   <ZCL_SPFLI_PERSISTENT>
    <CARRID>UA</CARRID>
    <CONNID>3517</CONNID>
    <COUNTRYFR>DE</COUNTRYFR>
    <CITYFROM>FRANKFURT</CITYFROM>
    <AIRPFROM>FRA</AIRPFROM>
    <COUNTRYTO>US</COUNTRYTO>
    <CITYTO>NEW YORK</CITYTO>
    <AIRPTO>JFK</AIRPTO>
    <FLTIME>495</FLTIME>
    <DEPTIME>10:40:00</DEPTIME>
    <ARRTIME>12:55:00</ARRTIME>
    <DISTANCE>6162.0</DISTANCE>
    <DISTID>KM</DISTID>
    <FLTYPE/>
    <PERIOD>0</PERIOD>
   </ZCL_SPFLI_PERSISTENT>
  </cls:ZCL_SPFLI_PERSISTENT>
</asx:heap>
</asx:abap>

2. Well, now we need to think how we can fill SPFLI structure from XML. To see which file should I have I serialized SPFLI structure first:


<asx:abap version="1.0" xmlns:asx="http://www.sap.com/abapxml">
<asx:values>
  <STRUC>
   <MANDT/>
   <CARRID>UA</CARRID>
   <CONNID>3517</CONNID>
   <COUNTRYFR>DE</COUNTRYFR>
   <CITYFROM>FRANKFURT</CITYFROM>
   <AIRPFROM>FRA</AIRPFROM>
   <COUNTRYTO>US</COUNTRYTO>
   <CITYTO>NEW YORK</CITYTO>
   <AIRPTO>JFK</AIRPTO>
   <FLTIME>495</FLTIME>
   <DEPTIME>10:40:00</DEPTIME>
   <ARRTIME>12:55:00</ARRTIME>
   <DISTANCE>6162.0</DISTANCE>
   <DISTID>KM</DISTID>
   <FLTYPE/>
   <PERIOD>0</PERIOD>
  </STRUC>
</asx:values>
</asx:abap>

lns:dic

3. Now we have asXML with object instance from one side, and we have asXML that we need to create .

There are several ways how to create it:

  • Parsing first one and renderring new one manually
  • Do the same over XSLT transormation

As I’m lazy i chose the last one. I’m not a big expert in XSLT so this is what actually I managed to create:


<xsl:transform version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:sap="http://www.sap.com/sapxsl"
>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<asx:abap
  version="1.0"
  xmlns:asx="http://www.sap.com/abapxml">
  <asx:values>
    <STRUCT>
       <xsl:copy-of select="./*/*/*/*/*"/>
    </STRUCT>
  </asx:values>
  </asx:abap>
</xsl:template>
</xsl:transform>

To my big surprise it worked:

So what I did I provided a new method for this operation:


class-methods TO_DATA
    importing
      !IO_OBJECT type ref to IF_SERIALIZABLE_OBJECT
    changing
      !CS_DATA type ANY .
  method TO_DATA.
     try.
        " Object to asXml:serializable object
        call transformation id
          source struc = io_object
          result xml data(lv_xml).
        " asXml:serializable object to asXml:data
        call transformation zcw_obj2struc
          source xml lv_xml
          result xml data(lv_xml_struc).
        " asXml:data to data
        call transformation id
          source xml lv_xml_struc
          result struct = cs_data.
      catch cx_transformation_error into data(lo_cx).
        if 1 eq 2.
          " here you can go from the debugger
          data(lo_output) = cl_demo_output=>new( ). " <-place the cursor here and press Shift+F12
          lo_output->begin_section( 'Object to asXml:serializable object' ).
          lo_output->write_xml( xml = lv_xml  ).
          lo_output->begin_section( 'asXml:serializable object to asXml:data' ).
          lo_output->write_xml( xml = lv_xml_struc  ).
          lo_output->write_text( lo_cx->get_text( ) ).
          lo_output->display( ).
        endif.
    endtry.
  endmethod.

and finally this SAP code:


agent = ca_spfli_persistent=>agent.
    TRY.
        query_manager = cl_os_system=>get_query_manager( ).
        query = query_manager->create_query(
                  i_filter  = `AIRPFROM = PAR1 AND AIRPTO = PAR2` ).
        connections =
          agent->if_os_ca_persistency~get_persistent_by_query(
                   i_query   = query
                   i_par1    = airpfrom
                   i_par2    = airpto ).
        LOOP AT connections ASSIGNING FIELD-SYMBOL(<connection>).
          connection = CAST #( <connection> ).
          result-carrid = connection->get_carrid( ).
          result-connid = connection->get_connid( ).
          APPEND result TO results.
        ENDLOOP.
        cl_demo_output=>display( results ).
      CATCH cx_root INTO exc.
        cl_demo_output=>display( exc->get_text( ) ).
    ENDTRY.

became more elegant to me:


types: begin of query_ts,
    airpfrom type spfli-airpfrom,
    airpto type spfli-airpto,
  end of query_ts.
  data: lt_results type table of spfli.
  zcl_os_api=>select_by_query(
      exporting
        io_agent     =  zca_spfli_persistent=>agent   " Class-Specific Persistence Interface
        is_selection = value query_ts(
          airpfrom = p_from
          airpto = p_to
        )
      importing
        et_data = lt_results
      ).

You probably think that such a technique will decrease overall performance sufficiently but that was a big surpsrise to me that trasnformation works really fast.

Also I think that XSLT transformation can be optimized somehow but here we need experts in this area to recieve an advise.

Will be glad to see your comments here. Critics is appreciated.

Petr.

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

  1. Valentin Huber

    Hello Petr,

    Thanks for sharing this! I’m a fan of the Object Services myself. The example above, however, looks a bit contrieved: Your use the Object Services to load data from the DB into an internal table…i.e. the starting point is a DB table and the end result is an internal table.

    Anyway, thanks again for sharing the helper methods!

    Kind regards,

    Valentin

    (0) 
    1. Petr Plenkov Post author

      Thanks for the feedback, Valentin.

      Actually I redesigned this solution already and now it returns a table of persistent object instances.

      The main idea here was using structure of any type as a selection criteria for query service.

      THis is something that helped me to build pretty powerful generic abstract query servie with caching results inside.

      (0) 

Leave a Reply