jsonapache-nifijolt

Flat the Complicated Nested JSON with multiple combinations of Input JSON


I'm trying to write a spec to do the below transformation using jolt transformation. I need to convert the nestedd JSON to flat JSON

I am having some trouble with converting the convert the nested JSON to Flat JSON I have looked at examples and didn't get any closer as to what is mentioned above. I need to transform a JSON structure by using a JOLT spec. I use https://jolt-demo.appspot.com to test the following below.

Input 1 Case where Sales Area is a List there is no nested Partner Function

{
  "CustomerMaster": {
    "Rootnode": {
      "KUNNR": "0006503338",
      "NAME1": "Schwarz Unternehmens Treuhand KG",
      "LAND1": "DK",
      "SalesArea": [
        {
          "VKORG": "1301",
          "VTWEG": "10",
          "SPART": "00"
        },
        {
          "VKORG": "1501",
          "VTWEG": "10",
          "SPART": "00"
        }
      ],
      "Indicator": "",
      "TimeStamp": "2024-12-18T14:04:40Z"
    }
  }
}

Expected Output 1:

I want the flattened JSON


[  {
  "KUNNR" : "0006503338",
  "NAME1" : "Schwarz Unternehmens Treuhand KG",
  "LAND1" : "DK",
  "Indicator" : "",
  "TimeStamp" : "2024-12-18T14:04:40Z",
  "VKORG" : "1301",
  "VTWEG" : "10",
  "SPART" : "00"
}, {
  "KUNNR" : "0006503338",
  "NAME1" : "Schwarz Unternehmens Treuhand KG",
  "LAND1" : "DK",
  "Indicator" : "",
  "TimeStamp" : "2024-12-18T14:04:40Z",
  "VKORG" : "1501",
  "VTWEG" : "10",
  "SPART" : "00",
}
} ]

Input 2 Case where Sales Area is not a List and one element which is can contain a list of Partner Function

{
  "CustomerMaster": {
    "Rootnode": {
      "KUNNR": "123456789",
      "NAME1": "Milchwerke \"Mittelelbe\" GmbH",
      "NAME2": "Milchwerke \"Mittelelbe\" GmbH",
      "NAME3": "Milchwerke \"Mittelelbe\" GmbH",
      "NAME4": "Milchwerke \"Mittelelbe\" GmbH",
      "LAND1": "DE",
      "SalesArea": {
        "VKORG": "1301",
        "VTWEG": "00",
        "SPART": "00",
        "PartnerFunction": [
          {
            "PARVW": "WE",
            "PARZA": "000",
            "KUNN2": "0000045959"
          },
          {
            "PARVW": "AG",
            "PARZA": "000",
            "KUNN2": "0000047480"
          }
        ]
      }
    }
  }
}

Expected Output 2 :

[ {
  "KUNNR" : "0006503338",
  "NAME1" : "Schwarz Unternehmens Treuhand KG",
  "LAND1" : "DK",
  "Indicator" : "",
  "TimeStamp" : "2024-12-18T14:04:40Z",
  "VKORG" : "1301",
  "VTWEG" : "10",
  "SPART" : "00",
  "PARVW" : "WE",
  "PARZA" : "000",
  "KUNN2" : "0000045959"
}, {
  "KUNNR" : "0006503338",
  "NAME1" : "Schwarz Unternehmens Treuhand KG",
  "LAND1" : "DK",
  "Indicator" : "",
  "TimeStamp" : "2024-12-18T14:04:40Z",
  "VKORG" : "1301",
  "VTWEG" : "10",
  "SPART" : "00",
  "PARVW" : "AG",
  "PARZA" : "000",
  "KUNN2" : "0000047480"
} ]

Input 3

Case where Sales Area is a List and each element can contain a list of Partner Function

{
  "CustomerMaster": {
    "Rootnode": {
      "KUNNR": "0006503338",
      "NAME1": "Schwarz Unternehmens Treuhand KG",
      "LAND1": "DK",
      "SalesArea": [
        {
          "VKORG": "1301",
          "VTWEG": "10",
          "SPART": "00",
          "PartnerFunction": [
          {
            "PARVW": "WE",
            "PARZA": "000",
            "KUNN2": "0000045959"
          },
          {
            "PARVW": "AG",
            "PARZA": "000",
            "KUNN2": "0000047480"
          }
        ]
      },
        {
          "VKORG": "1501",
          "VTWEG": "10",
          "SPART": "00",
          "PartnerFunction": [
          {
            "PARVW": "WE",
            "PARZA": "000",
            "KUNN2": "0000045959"
          },
          {
            "PARVW": "AG",
            "PARZA": "000",
            "KUNN2": "0000047480"
          }
        ]
      }
      ],
      "Indicator": "",
      "TimeStamp": "2024-12-18T14:04:40Z"
    }
  }
}

Expected Output 3:

[
 {
  "KUNNR" : "0006503338",
  "NAME1" : "Schwarz Unternehmens Treuhand KG",
  "LAND1" : "DK",
  "Indicator" : "",
  "TimeStamp" : "2024-12-18T14:04:40Z",
  "VKORG" : "1301",
  "VTWEG" : "10",
  "SPART" : "00",
  "PARVW" : "WE",
  "PARZA" : "000",
  "KUNN2" : "0000045959"
}, {
  "KUNNR" : "0006503338",
  "NAME1" : "Schwarz Unternehmens Treuhand KG",
  "LAND1" : "DK",
  "Indicator" : "",
  "TimeStamp" : "2024-12-18T14:04:40Z",
  "VKORG" : "1301",
  "VTWEG" : "10",
  "SPART" : "00",
  "PARVW" : "AG",
  "PARZA" : "000",
  "KUNN2" : "0000047480"
}, {
  "KUNNR" : "0006503338",
  "NAME1" : "Schwarz Unternehmens Treuhand KG",
  "LAND1" : "DK",
  "Indicator" : "",
  "TimeStamp" : "2024-12-18T14:04:40Z",
  "VKORG" : "1501",
  "VTWEG" : "10",
  "SPART" : "00",
  "PARVW" : "WE1",
  "PARZA" : "000",
  "KUNN2" : "0000045959"
}, {
  "KUNNR" : "0006503338",
  "NAME1" : "Schwarz Unternehmens Treuhand KG",
  "LAND1" : "DK",
  "Indicator" : "",
  "TimeStamp" : "2024-12-18T14:04:40Z",
  "VKORG" : "1501",
  "VTWEG" : "10",
  "SPART" : "00",
  "PARVW" : "AG1",
  "PARZA" : "000",
  "KUNN2" : "0000047480"
} ]

Solution

  • This spec will be able to handle all the formats

    [
      {
        "operation": "cardinality",
        "spec": {
          "CustomerMaster": {
            "Rootnode": {
              "SalesArea": "MANY"//make SalesArea always as an array
            }
          }
        }
      },
      {
        "operation": "cardinality",
        "spec": {
          "CustomerMaster": {
            "Rootnode": {
              "SalesArea": {
                "*": {
                  "PartnerFunction": "MANY"//make PartnerFunction always as an array
                }
              }
            }
          }
        }
      },
      {
        "operation": "default",
        "spec": {
          "CustomerMaster": {
            "Rootnode": {
              "SalesArea[]": {
                "*": {
                  "PartnerFunction": [], //initialize a default PartnerFunction array if it's missing
                  "PartnerFunction[]": {
                    "0": { //initialize a default PartnerFunction object at index 0 using a dummy field named initializer
                      "initializer": "placeholder"
                    }
                  }
                }
              }
            }
          }
        }
      },
      {
        "operation": "shift",
        "spec": {
          "CustomerMaster": {
            "Rootnode": {
              "SalesArea": {
                "*": {
                  "PartnerFunction": {
                    "*": {// Map the fields starting from PartnerFunction array
                      "PARVW": "[&3].[&1].&",
                      "PARZA": "[&3].[&1].&",
                      "KUNN2": "[&3].[&1].&",
                      "@(2,VKORG)": "[&3].[&1].VKORG", // Go 2 level up to map SalesArea fields                  
                      "@(2,VTWEG)": "[&3].[&1].VTWEG",
                      "@(2,SPART)": "[&3].[&1].SPART",
                      "@(4,KUNNR)": "[&3].[&1].KUNNR", // Go 4 level up to map Rootnode fields                  
                      "@(4,NAME1)": "[&3].[&1].NAME1",
                      "@(4,LAND1)": "[&3].[&1].LAND1"
                    }
                  }
                }
              }
            }
          }
        }
      },
      {
        "operation": "shift",
        "spec": {
          "*": {
            "*": "" // Unwrap the grouped objects
          }
        }
      }
    ]
    

    The key here is to iterate over PartnerFunction and then map the outer fields to it. For that, first the SalesArea and PartnerFunction objects need to normalized as an Array using Cardinality operation.

    Below are the functions of each operation in this spec

    1. Cardinality to make SalesArea always as an array
    2. Cardinality to make PartnerFunction always as an array
    3. Default operation to inialize a PartnerFunction object within the array so that it can be used as an anchor for mapping
    4. Shift for mapping all the fields using PartnerFunction as the anchor
    5. Shift for unwrapping the SalesArea grouped array