0

I'm trying to use Self-Written Transformations for JSON instead of the Identity Transformation ID to deserialize JSON from an external server.

However, this only works as long as all fields are in the specified order (which isn't guaranteed by JSON specs). Also missing fields are an issue, throwing an exception.

Is there any way to copy the ID behavior (which is fine with any order and missing fields), yet let me define the field names myself?

Example JSON

{
    "d": {
        "__abc": "111",
        "results": [
            {
                "__metadata": {
                    "id": "SOME_ID",
                    "uri": "SOME_URI",
                    "type": "SOME_TYPE"
                },
                "FieldA": "X",
                "FieldB": "X"
            },
            {
                "__metadata": {
                    "id": "SOME_ID2",
                    "uri": "SOME_URI2",
                    "type": "SOME_TYPE2"
                },
                "FieldA": "Y",
                "FieldB": "QQ"
            }
        ]
    }
}

The transformation that works thanks to Sandra:

<?sap.transform simple?>
<tt:transform xmlns:tt="http://www.sap.com/transformation-templates" version="0.1">
  <tt:root name="ROOT" type="?"/>
  <tt:template>
    <object>
      <object name="d">
        <str name="__abc">
          <tt:value ref=".ROOT.d.__abc"/>
        </str>

        <array name="results">
          <tt:loop name="S_RESULT" ref=".ROOT.d.results">
            <object>
              <object name="__metadata">
                <tt:skip/>
                <!--<str name="id">
                  <tt:value ref="$S_RESULT.__metadata.id"/>
                </str>
                <str name="uri">
                  <tt:value ref="$S_RESULT.__metadata.uri"/>
                </str>
                <str name="type">
                  <tt:value ref="$S_RESULT.__metadata.type"/>
                </str>-->
              </object>
              <tt:group>
                <tt:cond>
                  <str name="FieldA">
                    <tt:value ref="$S_RESULT.FIELDA"/>
                  </str>
                </tt:cond>
                <tt:cond>
                  <str name="FieldB">
                    <tt:value ref="$S_RESULT.FIELDB"/>
                  </str>
                </tt:cond>
              </tt:group>
            </object>
          </tt:loop>
        </array>
      </object>
    </object>
  </tt:template>
</tt:transform>
Regyn
  • 585
  • 3
  • 17
  • Use [`tt:group` with nested `tt:cond` (and alike)](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenst_tt_group.htm). Please provide a minimal reproducible example if you need further explanations. – Sandra Rossi Nov 16 '21 at 19:28
  • @SandraRossi Thanks! I've posted what works now. Can you post this as an answer? Sadly I cannot access __metadata though, as there is a REF crash. But the question itself is answered, the other thing seems to be another problem. – Regyn Nov 17 '21 at 14:59
  • In fact I couldn't post it as an answer because much too short, but your own solution is worth an answer, just add the links I have provided, and that will be fine. – Sandra Rossi Nov 17 '21 at 16:03
  • @SandraRossi Really want you to get the credit, tt:group and tt:cond combination is a bit hidden. I think it's already a win if you can post your comment and the explanation of Deserialization behavior from your linked help page. It works perfectly now, just forgot to add the structure for the second part. – Regyn Nov 17 '21 at 16:08
  • OK, done. You may also move your solution out of your question, to a distinct answer. Thanks. – Sandra Rossi Nov 17 '21 at 19:02
  • @SandraRossi thanks! The only weird thing is sap‘s documentation saying the deserialization will stop after all frequency=1 fields are filled. In my tests it’s filling all 1 and ? Fields and then stops the loop. Also expressing that it must be present, but can be null, is not possible as it requires two optional cond with frequency „?“. But that’s fine since null is basically optional. Just the loop stop worries me. Who wouldn’t want the optional field being filled just because it comes after the required fields in the json string :/ – Regyn Nov 17 '21 at 20:16
  • Maybe it's worth another question, if needed. I'm not sure to understand your exact case. Stopping the loop after all elements have been consumed seems normal. For the value which can be `null` or not null (string or whatever), I guess it's possible to define a more complex condition, but I'm not familiar with it. – Sandra Rossi Nov 17 '21 at 20:40

1 Answers1

1

To permit a group of elements to be in any position in the group, you may wrap them into tt:group with nested tt:cond (and alike).

Example to have <str name="FieldA">...</str> and <str name="FieldB">...</str> in any order:

              <tt:group>
                <tt:cond>
                  <str name="FieldA">
                    <tt:value ref="$S_RESULT.FIELDA"/>
                  </str>
                </tt:cond>
                <tt:cond>
                  <str name="FieldB">
                    <tt:value ref="$S_RESULT.FIELDB"/>
                  </str>
                </tt:cond>
              </tt:group>

If you want to have one of them being optional (or both), you may use <tt:cond frq="?">.

Sandra Rossi
  • 11,934
  • 5
  • 22
  • 48