Search code examples
jsontransformationjolt

Jolt Spec transformation with nested Array structure


I have a json like

{
  "Product": [
    {
      "id": 123,
      "name": "foo",
      "ProductCharge": [
        {
          "productCode": "PIIB",
          "configLevel": "PROD",
          "feeRate": [
            {
              "rate": 123,
              "range": "123"
            },
            {
              "rate": 321,
              "range": "321"
            }
          ]
        }
      ]
    }
  ]
}

which I want transformed to this

{
  "Product" : [ {
    "id": 123,
      "name": "foo",
   // more fields
    "ProductCharge" : [ {
      "productCode" : "PIIB",
      "configLevel" : "PROD",
      "rate" : 123,
      "range" : "123"
    }, {
      "productCode" : "PIIB",
      "configLevel" : "PROD",
      "rate" : 321,
      "range" : "321"
    } ]
  } ]
}

I have this spec,

[
  {
    "operation": "shift",
    "spec": {
      "Product": {
        "*": {
          // Preserve the id, name, and any other top-level fields
          "ProductCharge": {
            "*": {
              "feeRate": {
                "*": {
                  "@2": { "*": "Product[#6].ProductCharge.[&1].&" },
                  "*": "Product[#6].ProductCharge.[&1].&"
                }
              }
            }
          }
        }
      }
    }
  },
  {
    "operation": "remove",
    "spec": {
      "Product": {
        "*": {
          "ProductCharge": {
            "*": {
              "feeRate": ""
            }
          }
        }
      }
    }
  }
]

My current spec is missing the "id" and "name" and the other fields under "Product". If I try to add this line to get the remaining fields under "Product", it distorts the whole structure

"*": "Product[#2].&"

Can someone point out how to get around this.

Edit:

Barbaros's your solution worked, but I realized when I have more than one ProductCharge like below

{
  "Product": [
    {
      "id": 123,
      "name": "foo",
      "ProductCharge": [
        {
          "aboveAmount": 0,
          "chargeType": "ABC",
          "chargeValueType": "PERCENT",
          "frequency": 1,
          "feeRate": [
            {
              "bandNumber": 1,
              "rate": 0.6
            },
            {
              "bandNumber": 2,
              "rate": 1.7
            }
          ]
        },
        {
          "aboveAmount": 0,
          "chargeType": "DEF",
          "chargeValueType": "PERCENT",
          "frequency": 1,
          "feeRate": [
            {
              "bandNumber": 1,
              "rate": 0.75
            },
            {
              "bandNumber": 2,
              "rate": -0.55
            }
          ]
        }
      ]
    }
  ]
}

rather than create it as 4 separate sections

{
  "Product" : [ {
    "id" : 123,
    "name" : "foo",
    "ProductCharge" : [ {
      "aboveAmount" : 0,
      "chargeType" : "ABC",
      "chargeValueType" : "PERCENT",
      "frequency" : 1,
      "bandNumber" : 1,
      "rate" : 0.6
    }, {
      "aboveAmount" : 0,
      "chargeType" : "ABC",
      "chargeValueType" : "PERCENT",
      "frequency" : 1,
      "bandNumber" : 2,
      "rate" : 1.7
    }, {
      "aboveAmount" : 0,
      "chargeType" : "DEF",
      "chargeValueType" : "PERCENT",
      "frequency" : 1,
      "bandNumber" : 1,
      "rate" : 0.75
    }, {
      "aboveAmount" : 0,
      "chargeType" : "DEF",
      "chargeValueType" : "PERCENT",
      "frequency" : 1,
      "bandNumber" : 2,
      "rate" : -0.55
    } ]
  } ]
}

it creates like this;

{
  "Product" : [ {
    "id" : 123,
    "name" : "foo",
    "ProductCharge" : [ {
      "aboveAmount" : [ 0, 0 ],
      "chargeType" : [ "ABC", "DEF" ],
      "chargeValueType" : [ "PERCENT", "PERCENT" ],
      "frequency" : [ 1, 1 ],
      "bandNumber" : [ 1, 1 ],
      "rate" : [ 0.6, 0.75 ]
    }, {
      "aboveAmount" : [ 0, 0 ],
      "chargeType" : [ "ABC", "DEF" ],
      "chargeValueType" : [ "PERCENT", "PERCENT" ],
      "frequency" : [ 1, 1 ],
      "bandNumber" : [ 2, 2 ],
      "rate" : [ 1.7, -0.55 ]
    } ]
  } ]
}

Solution

  • You can handle the issue better by accumulating the inner stuff( those within the "feeRate" array ) in a new object, namely "Others" while using placeholder ampersand symbols for the keys such as "Product", "ProductCharge" and "feeRate" such that

    [
      {
        "operation": "shift",
        "spec": {
          "Product": {
            "*": {
              "*": "&2[&1].&",
              "ProductCharge": {
                "*": {
                  "*": "&4[&3].&2[&1].Others.&",
                  "feeRate": "&4[&3].&2[&1].&"
                }
              }
            }
          }
        }
      },
      {
        "operation": "shift",
        "spec": {
          "Product": {
            "*": {
              "*": "&2[&1].&",
              "ProductCharge": {
                "*": {
                  "feeRate": {
                    "*": {
                      "@2,Others": { "*": "&7[&6].&5[&1].&" },
                      "*": "&6[&5].&4[&1].&"
                    }
                  }
                }
              }
            }
          }
        }
      }
    ]
    

    the demo on the site Jolt Transform Demo Using v0.1.1 is :

    enter image description here

    Edit : You'll need to add one more partition for this new case and the expression &3_&1 will do the trick such as

    [
      {
        "operation": "shift",
        "spec": {
          "Product": {
            "*": {
              "*": "&2.&",
              "ProductCharge": {
                "0": { //in order to bring only once among duplicated values
                  "*": "&4.&2.Others.&",
                  "feeRate": {
                    "*": {
                      "*": "&6.&4.&2.&3_&1.&"
                    }
                  }
                },
                "*": {
                  "feeRate": {
                    "*": {
                      "*": "&6.&4.&2.&3_&1.&"
                    }
                  }
                }
              }
            }
          }
        }
      },
      {
        "operation": "shift",
        "spec": {
          "Product": {
            "*": "&1[0].&",
            "ProductCharge": {
              "feeRate": {
                "*": {
                  "@": "&4[0].&3.[#2]",
                  "@2,Others": { "*": "&5[0].&4.[#3].&" }
                }
              }
            }
          }
        }
      }
    ]
    

    This last spec will handle the previous case as well