0

I receive XML messages from a subsystem that sends changing namespaces. The system seems to increase a counter on a namespace depending on if its needed. I don't know how to handle this in SAP.

For example it might send me this:

<?xml version="1.0"?>
<topTag xmlns:ns1="xsd/namespacesForBoxes" 
        xmlns:ns2="xsd/namespacesForCartons" 
        xmlns:ns3="xsd/namespacesForPallets">

  <ns1:container>
    <ns1content/>
  </ns1:container>

  <ns2:container>
    <ns2:content/>
  </ns2:container>

  <ns3:container>
    <ns3:content/>
  </ns3:container>

</topTag>

which would be fine so far, but if the second element is not needed it would set the counter for pallets to 2, like so:

<?xml version="1.0"?>
<topTag xmlns:ns1="xsd/namespacesForBoxes" 
        xmlns:ns2="xsd/namespacesForPallets">

  <ns1:container>
    <ns1content/>
  </ns1:container>

  <ns2:container>
    <ns2:content/>
  </ns2:container>

</topTag>

Of course the <content> tag would hold different values depending on the namespace on my side, so the transformation will fail as it "hardcodes" the expected namespace.

Is there a way to implement this in simple transformations? The structure is quite big, so dynamic transformation via code is not feasible. Or is there a way to do this with other transformation technices?

Followup question: Is this normal? To me this seems like a very wild thing to implement, if namespaces are not needed they should not switch names. This just makes it horribly difficult to trace data back to the string.

Sandra Rossi
  • 11,934
  • 5
  • 22
  • 48
  • 3
    Prefixes for namespaces are trivial. They don’t mean anything and should be able to change without your code being changed. The actual namespace is the uri. Can you share your problem in the code, so you get a answer to your problem ? – Siebe Jongebloed Aug 11 '23 at 05:05

2 Answers2

2

As Siebe Jongebloed already pointed out, consumers of XML documents should never work with the namespace prefixes (like ns1) but work instead with the namespace URIs (like xsd/namespacesForBoxes).

Simple transformations follow this rule and compare the namespace URI, not the prefix, as demonstrated by the following example:

<?sap.transform simple?>
<tt:transform xmlns:tt="http://www.sap.com/transformation-templates"
 xmlns:ddic="http://www.sap.com/abapxml/types/dictionary"
 xmlns:ns1="namespace_uri">
 <tt:root name="ROOT" type="ddic:SCARR"/>
 <tt:template>
  <ns1:Airline>
   <tt:value ref=".ROOT.CARRID"/>
  </ns1:Airline>
 </tt:template>
</tt:transform>

When you use this simple transformation to transform ABAP into XML, you get the namespace prefix ns1. But you can exchange the namespace prefix in the XML document and still use the same simple transformation to transform the XML back into ABAP:

data: scarr     type scarr,
      xmlstring type string.
scarr-carrid = 'XX'.
call transformation ...
 source root = scarr
 result xml xmlstring.
write / xmlstring.
replace all occurrences of 'ns1' in xmlstring with 'ns2'.
call transformation ...
 source xml xmlstring
 result root = scarr.
write / scarr-carrid.
Heiko Theißen
  • 12,807
  • 2
  • 7
  • 31
0

The previous answer from Heiko is correct, simple transformations can handle namespaces, what my solution was lacking (in addition to lacking namespace handling) was a condition check.

If you have three namespaces and they might change the numbers because not all of them are needed in every transformation you need to make them "optional" in the transformation, like so:

    <tt:template>

    <top_element
        xmlns:ns1="xsd/namespacesForBoxes"
        xmlns:ns2="xsd/namespacesForCartons"
        xmlns:ns3="xsd/namespacesForPallets">
      <tt:namespace name="ns1"/>
      <tt:namespace name="ns2"/>
      <tt:namespace name="ns3"/>

        <tt:cond s-check="not-initial(ref('.telegram.BOX_ID'))">
          <ns1:container>
            <tt:attribute name="box-id" value-ref=".telegram.BOX_ID"/>
          </ns1:container>
        </tt:cond>

        <tt:cond s-check="not-initial(ref('.telegram.CARTON_ID'))">
          <ns2:container>
            <tt:attribute name="carton-id" value-ref=".telegram.CARTON_ID"/>
          </ns2:container>
        </tt:cond>

        <tt:cond s-check="not-initial(ref('.telegram.PALLET_ID'))">
          <ns3:container>
            <tt:attribute name="pallet-id" value-ref=".telegram.PALLET_ID"/>
          </ns3:container>
        </tt:cond>


    </top_element>

</tt:template>

This works with my test-report:

TYPES: BEGIN OF ts_test,
         box_id    TYPE char10,
         carton_ID TYPE char10,
         pallet_ID TYPE char10,
       END OF ts_test.

DATA(lv_res1) = VALUE ts_test( ).
DATA(lv_res2) = VALUE ts_test( ).

CONCATENATE
'<top_element '
'xmlns:ns1="xsd/namespacesForBoxes" '
'xmlns:ns2="xsd/namespacesForCartons" '
'xmlns:ns3="xsd/namespacesForPallets">'
'<ns1:container box-id="BOX"/>'
'<ns2:container carton-id="CARTON"/>'
'<ns3:container pallet-id="PALLET"/>'
'</top_element>'
INTO DATA(lv_trans1)
RESPECTING BLANKS.

CONCATENATE
'<top_element '
'xmlns:ns1="xsd/namespacesForBoxes" '
'xmlns:ns2="xsd/namespacesForPallets">'
'<ns1:container box-id="BOX"/>'
'<ns2:container pallet-id="PALLET"/>'
'</top_element>'
INTO DATA(lv_trans2)
RESPECTING BLANKS.

CALL TRANSFORMATION z_namespace_test
  SOURCE XML lv_trans1
  RESULT telegram = lv_res1.

CALL TRANSFORMATION z_namespace_test
  SOURCE XML lv_trans2
  RESULT telegram = lv_res2.

Thank you for the input!