Получить имена и значения элементов из раздела объекта iXML - PullRequest
0 голосов
/ 19 марта 2019

Я пытаюсь получить тег Totales и его дочерние элементы из этого XML:

<Body>
  <Receptor>
    <RUTRecep>9655</RUTRecep>
  </Receptor>
  <Totales>
    <MntNeto>63934</MntNeto>
    <TasaIVA>19</TasaIVA>
    <MntTotal>76081</MntTotal>
  </Totales>
</Body>

Мой код возвращает ТОЛЬКО значения тегов, а не имена тегов:

Итого

639341976081

Мне нужны как имена, так и значения внутри Totales:

MntNeto

63934

TasaIVA

19

MntTotal

76081

Это мой код:

DATA(lo_ixml) = cl_ixml=>create( ).
DATA(lo_stream_factory) = lo_ixml->create_stream_factory( ).
DATA(lo_doc) = lo_ixml->create_document( ).

IF lo_ixml->create_parser(
          document       = lo_doc
          stream_factory = lo_stream_factory
          istream        = lo_stream_factory->create_istream_string( string =
                `<Body>                                      ` &&
                `  <Receptor>                                ` &&
                `    <RUTRecep>9655</RUTRecep>               ` &&
                `  </Receptor>                               ` &&
                `  <Totales>                                 ` &&
                `    <MntNeto>63934</MntNeto>                ` &&
                `    <TasaIVA>19</TasaIVA>                   ` &&
                `    <MntTotal>76081</MntTotal>              ` &&
                `  </Totales>                                ` &&
                `</Body>                                     ` )
      )->parse( ) <> 0.
  RETURN.
ENDIF.

DATA(lo_node_col) = lo_doc->get_elements_by_tag_name( name = 'Totales' ).
DATA(lo_iterator) = lo_node_col->create_iterator( ).
DATA(lo_node) = lo_iterator->get_next( ).

WHILE NOT lo_node IS INITIAL.
  DATA(lf_name)  = lo_node->get_name( ).
  DATA(lf_value) = lo_node->get_value( ).

  "do something for text
  WRITE /: lf_name , lf_value.

  lo_node = lo_iterator->get_next( ).
ENDWHILE.

Ответы [ 2 ]

2 голосов
/ 19 марта 2019

Причина в том, что вы используете коллекцию узлов, которые соответствуют всем элементам XML с именем Totales, поэтому ваша коллекция содержит только один узел, и итератор будет выполнять итерацию только одного узла.Метод get_value объединяет все тексты узла и его дочерних узлов на всех уровнях глубины.

Вместо этого не используйте коллекцию, получите элемент с именем Totales, создайте итератор наэтот узел, который будет перебирать этот узел и его дочерние узлы.

Более того, узлы могут быть как элементами, так и текстами (и, возможно, другими типами, такими как атрибуты и т. д.). Для <name>value</name> существует два узла, одинэлемента типа (имя) и элемента типа текст (значение).Это полезно для обработки потоков XML, например, <a>v1<b>v2</b>v3</a>.Таким образом, для обработки только форм, таких как <name>value</name>, вам нужно выбрать узлы, содержащие ровно один дочерний элемент, являющийся текстовым узлом.

DATA(lo_elem) = CAST if_ixml_node( lo_doc->find_from_path( path = '/Body/Totales' ) ).
IF lo_elem IS BOUND.
  DATA(lo_iterator) = lo_elem->create_iterator( ).

  DATA(lo_node) = lo_iterator->get_next( ). " get /Body/Totales node

  WHILE NOT lo_node IS INITIAL.
    " Only nodes of the form `<name>value</name>`
    IF lo_node->get_type( ) = lo_node->co_node_element
          AND lo_node->get_children( )->get_length( ) = 1
          AND lo_node->get_first_child( )->get_type( ) = lo_node->co_node_text.
      DATA(lf_name)  = lo_node->get_name( ).
      DATA(lf_value) = lo_node->get_value( ).

      "do something for text
      WRITE /: lf_name , lf_value.
    ENDIF.

    lo_node = lo_iterator->get_next( ).
  ENDWHILE.
ENDIF.

Результат:

MntNeto
63934
TasaIVA
19
MntTotal
76081
1 голос
/ 19 марта 2019

То, что вы делаете в своем коде, является преобразованием. Мой совет: не пишите ненужные коды для того, что уже решено с помощью XSLT.

Вот как вы можете сделать это в SAP. Перейдите к транзакции STRANS и создайте там следующее XSL-преобразование. Давайте назовем это ZTEST.

<xsl:transform version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="text" encoding="UTF-8" />
<xsl:strip-space elements="*"/>

    <xsl:template match="Totales">
        <xsl:for-each select="child::*">
<xsl:value-of select="local-name()" /><xsl:text>
</xsl:text><xsl:value-of select="text()" /><xsl:text>
</xsl:text>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="text() | comment()" />
</xsl:transform>

Тогда просто используйте CALL TRANSFORMATION, чтобы достичь того, что вы хотите.

REPORT ZZZ.

START-OF-SELECTION.
  DATA g_string TYPE string.
  DATA(g_ref_stream_factory) = cl_ixml=>create( )->create_stream_factory( ).
  DATA(g_ostream) = g_ref_stream_factory->create_ostream_cstring( g_string ).
  DATA(g_istream) =
    g_ref_stream_factory->create_istream_string(
      string =
                `<Body>                                      ` &&
                `  <Receptor>                                ` &&
                `    <RUTRecep>9655</RUTRecep>               ` &&
                `  </Receptor>                               ` &&
                `  <Totales>                                 ` &&
                `    <MntNeto>63934</MntNeto>                ` &&
                `    <TasaIVA>19</TasaIVA>                   ` &&
                `    <MntTotal>76081</MntTotal>              ` &&
                `  </Totales>                                ` &&
                `</Body>                                     ` ).

  CALL TRANSFORMATION ZTEST
    SOURCE XML g_istream
    RESULT XML g_ostream.

  SPLIT g_string AT cl_abap_char_utilities=>cr_lf INTO TABLE DATA(g_tab_string).
  LOOP AT g_tab_string ASSIGNING FIELD-SYMBOL(<string_line>).
    WRITE / <string_line>.
  ENDLOOP.
...