2
votes

Personnaliser JSON créé par CL_SXML_STRING_WRITER

Je crée JSON comme ça pour extraire n'importe quelle table (nom "aléatoirement" décidé à l'exécution, son nom est dans la variable iv_table_name):

sub_json = /ui2/cl_json=>serialize( data = <lt_result> pretty_name = /ui2/cl_json=>pretty_mode-low_case ).

La variable résultat sub_json ressemble à ceci:

 [
   {"MANDT":"220","AUFNR":"0000012", ...},
   {"MANDT":"220","AUFNR":"0000013", ...},
   ...
  ]

Y a-t-il un moyen d'éviter le dictionnaire environnant et d'obtenir le résultat comme ça?

{"RESULT":
 [
   {"MANDT":"220","AUFNR":"0000012", ...},
   {"MANDT":"220","AUFNR":"0000013", ...},
   ...
  ]
}

Contexte:

J'ai utilisé ceci:

FIELD-SYMBOLS <itab> TYPE STANDARD TABLE.
DATA ref_itab TYPE REF TO data.

DATA(iv_table_name) = 'SCARR'.
CREATE DATA ref_itab TYPE STANDARD TABLE OF (iv_table_name).
ASSIGN ref_itab->* TO <itab>.

SELECT *
  INTO TABLE <itab>
  FROM (iv_table_name).

DATA results_json TYPE TABLE OF string.
DATA sub_json TYPE string.

DATA(lo_json_writer) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).

CALL TRANSFORMATION id
        SOURCE result = <itab>
        RESULT XML lo_json_writer.

cl_abap_conv_in_ce=>create( )->convert( 
        EXPORTING
          input = lo_json_writer->get_output( )
        IMPORTING
          data = sub_json ).

Mais les performances de / ui2 / cl_json => sérialiser () n'est pas bon.


2 commentaires

Probablement juste avec une autre transformation. Je publierai ma proposition quand j'aurai plus de temps.


Vous pouvez le faire de la manière KISS en supprimant simplement les parties de début et de fin


4 Réponses :


1
votes

Je n'ai pas de réponse s'il est possible d'omettre la balise initiale "RESULT" en sXML complet, mais mon avis est NON.

Maintenant, il y a la solution avec le Principe de KISS :

SHIFT sub_json LEFT BY 10 PLACES CIRCULAR.
REPLACE SECTION OFFSET strlen( sub_json ) - 11 OF sub_json WITH ``.

Il y a aussi cette autre écriture (légèrement plus lente): p>

sub_json = replace( val = sub_json regex = '^\{"RESULT":|\}$' with = `` occ = 0 ).

ADDENDUM sur les performances:

J'ai mesuré cela pour une chaîne de 880K caractères, le code suivant avec le nombre exact de positions à supprimer (10 premiers caractères et 1 caractère de fin) est 6 fois plus rapide que regex (peut varier en fonction de la version du noyau ABAP), mais peut-être que cela ne sera pas perceptible par rapport au reste du programme:

REPLACE ALL OCCURRENCES OF REGEX '^\{"RESULT":|\}$' IN sub_json WITH ``.


0 commentaires

2
votes

Si vous voulez vraiment l'utiliser simplement comme un outil pour extraire des enregistrements de table, vous pouvez écrire votre propre transformation ID dans STRANS . Cela pourrait ressembler à ça, appelons-le Z_JSON_TABLE_CONTENTS (créez-le avec le type XSLT):

REPORT ZZZ.

FIELD-SYMBOLS <itab> TYPE STANDARD TABLE.
DATA ref_itab TYPE REF TO data.

DATA(iv_table_name) = 'SCARR'.
CREATE DATA ref_itab TYPE STANDARD TABLE OF (iv_table_name).
ASSIGN ref_itab->* TO <itab>.

SELECT *
  INTO TABLE <itab>
  FROM (iv_table_name).

DATA results_json TYPE TABLE OF string.
DATA sub_json TYPE string.

DATA g_string TYPE string.
DATA(g_document) = cl_ixml=>create( )->create_document( ).
DATA(g_ref_stream_factory) = cl_ixml=>create( )->create_stream_factory( ).
DATA(g_ostream) = g_ref_stream_factory->create_ostream_cstring( g_string ).

CALL TRANSFORMATION Z_JSON_TABLE_CONTENTS
        SOURCE result = <itab>
        RESULT XML g_ostream.

DATA(g_json_parser) = new /ui5/cl_json_parser( ).
g_json_parser->parse( g_string ).

Ensuite, vous pouvez l'utiliser comme ça.

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

<xsl:output method="text" encoding="UTF-8" />

<xsl:strip-space elements="*"/>

<xsl:template match="RESULT">
  [
    <xsl:for-each select="*">
      {
      <xsl:for-each select="*">
        &quot;<xsl:value-of select="local-name()" />&quot;: &quot;<xsl:value-of select="text()" />&quot;<xsl:if test="position() != last()">,</xsl:if>
      </xsl:for-each>
      }<xsl:if test="position() != last()">,</xsl:if>
    </xsl:for-each>
  ]
</xsl:template>

</xsl:transform>


0 commentaires

0
votes

Si vous utilisez la transformation d'appel ID , quel que soit le nœud que vous donnez à la transformation, ce nœud sera ajouté par défaut. Nous ne pouvons pas ignorer cela, mais vous pouvez supprimer la méthode suivante.

Remplacer : utilisation de l'expression régulière ou du mot direct avec l'instruction Remplacer la première occurrence et la dernière accolade de fermeture suivante } . Comme vous l'avez fait.

TROUVER : vous pouvez simplement utiliser cette déclaration ci-dessous TROUVEZ REGEX '(\ [. * \])' dans sub_json SUBMATCHES sub_json.


1 commentaires

De plus, votre instruction FIND équivaut à cette affectation avec la fonction match : sub_json = match (val = sub_json regex = '\ [. * \]').



1
votes

Juste un peu de travail manuel et voilà!

DATA(writer) = CAST if_sxml_writer( cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ) ).

DATA(components) =
CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_name( iv_table_name ) )->components.

writer->open_element( name = 'object' ).
LOOP AT <itab> ASSIGNING FIELD-SYMBOL(<line>).
 LOOP AT components ASSIGNING FIELD-SYMBOL(<fs_comp>).
  ASSIGN COMPONENT <fs_comp>-name OF STRUCTURE <line> TO FIELD-SYMBOL(<fs_val>).
  writer->open_element( name = 'str' ).
  writer->write_attribute( name = 'name' value = CONV string( <fs_comp>-name ) ).
  writer->write_value( CONV string( <fs_val> ) ).
  writer->close_element( ).
 ENDLOOP.
ENDLOOP.
writer->close_element( ).

DATA(xml_json) = CAST cl_sxml_string_writer( writer )->get_output(  ).  
sub_json = cl_abap_codepage=>convert_from( source = xml_json codepage = `UTF-8` ).

Pas de liste environnante et pas de dictionnaire. Si vous voulez chaque ligne dans un dictionnaire séparé, elle est facilement ajustable.


0 commentaires