jsonabapabap-st

Ignore order and missing fields for Self-Written Transformations for JSON


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>

Solution

  • 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="?">.