JacquesD
JacquesD

Reputation: 23

Java Jolt: can't find proper spec for my transformation

I'm struggling with that Jolt transformation: Here is my input JSON

{
  "bloc1-1": {
    "bloc1-2": [
      {
        "key": "key1",
        "value": "value1-1"
      },
      {
        "key": "key2",
        "value": "value1-2"
      }
    ]
  },
  "bloc2-1": {
    "bloc2-2": [
      {
        "key": "key1",
        "value": "value2-1"
      },
      {
        "key": "key2",
        "value": "value2-2"
      },
      {
        "key": "key3",
        "value": "value2-3"
      }
    ]
  }
}

Here is what I'm expecting

{
  "bloc1-key1" : "value1-1",
  "bloc1-key2" : "value1-2",
  "bloc2-key1" : "value2-1",
  "bloc2-key2" : "value2-2",
  "bloc2-key3" : "value2-3"
}

I have tried the following spec, but I cannot figure out how to prefix the key in the RHS (the @ should be the first character)

[
  {
    "operation": "shift",
    "spec": {
      "*": {
        "*": {
          "*": {
            "value": "@(1,key)"
          }
        }
      }
    }
  }
]

and got that

{
  "key1" : [ "value1-1", "value2-1" ],
  "key2" : [ "value1-2", "value2-2" ],
  "key3" : "value2-3"
}

Any help would be appreciated

Upvotes: 2

Views: 179

Answers (2)

Raymond Choi
Raymond Choi

Reputation: 1271

You may consider another library Josson.

https://github.com/octomix/josson

Josson josson = Josson.fromJsonString(inputJSON);

JsonNode node = josson.getNode(
    "entries()" +        // Field "key" generated by entries() is the same as the inner one.
    ".field(k:key)" +    // So, rename it to "k".
    ".unwind(value.*)" + // Unwind the "value" generated by entries(), not the inner "value".
    ".map(concat(k.keepBefore('-'),'-',key)::value)" +
    ".mergeObjects()");
System.out.println(node.toPrettyString());

Output

{
  "bloc1-key1" : "value1-1",
  "bloc1-key2" : "value1-2",
  "bloc2-key1" : "value2-1",
  "bloc2-key2" : "value2-2",
  "bloc2-key3" : "value2-3"
}

Upvotes: 0

kasptom
kasptom

Reputation: 2458

The spec below should do the trick:

[
  {
    "operation": "shift",
    "spec": {
      "*": {
        "*": {
          "*": {
            "$2": "keyParts1",
            "key": "keyParts2",
            "value": "values"
          }
        }
      }
    }
  },
  {
    "operation": "shift",
    "spec": {
      "keyParts1": {
        "*": "[#1].part1"
      },
      "keyParts2": {
        "*": "[#1].part2"
      },
      "values": {
        "*": "[#1].value"
      }
    }
  },
  {
    "operation": "modify-default-beta",
    "spec": {
      "*": {
        "newKey": "=split('-', @(1,part1))",
        "newKeyFirst": "@(1,newKey[0])",
        "newKeyComplete": "=concat(@(1,newKeyFirst),'-',@(1,part2))"
      }
    }
  },
  {
    "operation": "shift",
    "spec": {
      "*": {
        "value": "@(1,newKeyComplete)"
      }
    }
  }
]

Try to apply each of the operations separately (for example in the demo app).

Check out the links to the jolt docs and the examples from this answer


Step by step explanation:

First "shift" transformation places the components of the final result in the separate arrays:

{
  "keyParts1" : [ "bloc1-1", "bloc1-1", "bloc2-1", "bloc2-1", "bloc2-1" ],
  "keyParts2" : [ "key1", "key2", "key1", "key2", "key3" ],
  "values" : [ "value1-1", "value1-2", "value2-1", "value2-2", "value2-3" ]
}

The above operation let us have each of the components of the end result positioned in the right index of the desired result. Note that each array has the same size. In the second operation we group each of the array elements by its index using the # operator. Looking at the docs [#1] can be translated to:

"Go up one level and ask that node how many matches it has had before finding the current element".

The node above is an array, so for the 3rd array element it will be the array index 2.

[
  ...
  {
    "part1" : "bloc2-1",
    "part2" : "key1",
    "value" : "value2-1"
  }
  ...
]

In the 3rd operation I have assumed that the key of the desired result is the concatenation of the "blocX" substring of the "blocX-Y" key concatenated with the "key" value. That's why I've used the "modify-default-beta". Have a look at the example from the demo app Each of five elements of the array is transformed similarly to the 3rd array element:

[
  ...
  {
    "part1" : "bloc2-1",
    "part2" : "key1",
    "value" : "value2-1",
    "newKey" : [ "bloc2", "1" ],
    "newKeyFirst" : "bloc2",
    "newKeyComplete" : "bloc2-key1"
  }
  ...
]

The last operation produces the desired result from the 3rd operation's output.

Upvotes: 2

Related Questions