schibsted / jslt

JSON query and transformation language
Apache License 2.0
638 stars 120 forks source link

jslt for loop capability #328

Closed Al64v closed 9 months ago

Al64v commented 9 months ago

Hello, can you please tell me how to correctly transform json data using a jslt script?

Input JSON: "ran" : { "ranNum" : 248, "ranName" : "PM123", "ranva" : { "ranvarec" : [ { "recNum" : 4, "ranvades" : { "Text" : [ { "Language" : "EN", "des" : "spgal" }, { "Language" : "RU", "des" : "Мяг" } ] } }, { "recNum" : 5, "ranvades" : { "Text" : [ { "Language" : "EN", "des" : "Ks48" }, { "Language" : "RU", "des" : "Сталь" } ] } } ] } }

Output JSON: "ran_head": [{ "ran_num": "248", "ran_name": "PM123" }], "ran_va_des": [ { "ran_num": "248", "rec_num": "4", "language": "EN", "des": "spgal" }, { "ran_num": "248", "rec_num": "4", "language": "RU", "des": "Мяг" }, { "ran_num": "248", "rec_num": "5", "language": "EN", "des": "Ks48" }, { "ran_num": "248", "rec_num": "5", "language": "RU", "des": "Сталь" } ]

I tried to do it this way, but the result does not match the expected((

JSLT: { "ran_head": { "ran_num": .ranNum, "ran_name": .ranName }, "ran_va_des": [for (.ran.ranva.ranvarec){ "ran_num": .ranNum, "language": .ranvades.Text.Language, "des": .ranvades.Text.des } ] }

I also looked at the documentation and don’t quite understand how this script can be made dynamic.

It’s not entirely clear how you can iterate over attributes "ranvades.text.language"

catull commented 9 months ago

Your input is, aside from invalid JSON, badly formatted.

This should have been you input:

{
  "ran": {
    "ranNum": 248,
    "ranName": "PM123",
    "ranva": {
      "ranvarec": [
        {
          "recNum": 4,
          "ranvades": {
            "Text": [
              {
                "Language": "EN",
                "des": "spgal"
              },
              {
                "Language": "RU",
                "des": "Мяг"
              }
            ]
          }
        },
        {
          "recNum": 5,
          "ranvades": {
            "Text": [
              {
                "Language": "EN",
                "des": "Ks48"
              },
              {
                "Language": "RU",
                "des": "Сталь"
              }
            ]
          }
        }
      ]
    }
  }
}

And this is a sample, half-way working JSTL instruction:

{
  "ran_head": {
    "ran_num":  .ran.ranNum,
    "ran_name": .ran.ranName
  },
  "ran_va_des": [for (.ran.ranva.ranvarec) {
      "ran_num": .recNum,
      "texts": [for (.ranvades.Text) {
        "language": .Language,
        "des": .des
      }]
  }],
}
Al64v commented 9 months ago

@catull Thank you!

catull commented 9 months ago

Dear @Al64v It is not quite what you want, will have to adapt it.

catull commented 9 months ago

This is the best I can come up with, good luck!

def combine(n, texts)
  [ for ($texts) {
      "ran_num": $n,
      "language" : .Language,
      "des" : .des
    }
  ]

{
  "ran_head": {
    "ran_num":  .ran.ranNum,
    "ran_name": .ran.ranName
  },
  "ran_va_des": flatten (
      [ for (.ran.ranva.ranvarec) [
        combine (.recNum, .ranvades.Text)
      ]]
  )
}
catull commented 9 months ago

Doing it all in one "recipe" does not work, because the .recNum from the outer loop cannot be passed as a constant to the inner loop, at least, I have not found a way.

This is what I tried, without success:

{
  "ran_head": {
    "ran_num":  .ran.ranNum,
    "ran_name": .ran.ranName
  },
  "ran_va_des": flatten (
      [ for (.ran.ranva.ranvarec)
         [ for (.ranvades.Text) {
            "ran_num": .recNum,
            "language": .Language,
            "des": .des
         }
         ]
      ]
   )
}

@larsga Is there a clever way to pass values between for-loops ? Is there a way for the inner loop to pick up a value from its parent ?

Such as ../.recNum from the level of .ran.ranva.ranvare.ranvades.Text ? That would bind to .ran.ranva.ranvare.ranvades.recNum.

I tried by inserting a let n = .recNum and then using "ran_num": $n, ....., but that gets rejected.

Al64v commented 9 months ago

@catull Thank you!)