Concat dynamic number of JSON expressions

  • Platform information:
    • Hardware: Raspberry Pi 4
    • OS: Raspberry Pi OS / x64 / 11 (bullseye)
    • Java Runtime Environment: 11.0.15 (Temurin-11.0.15+10) (running in Docker)
    • openHAB version: 3.4.4 (running in Docker)
    • ConBee 2 USB stick

Dear community,

I want to access a RESTful API with openHAB’s HTTP binding. The backend returns JSON data as follows:

[
  {
    "day": "2023-08-13T00:00:00",
    "mealType": {
      "name": "Breakfast",
      "mealTypeId": 1,
      "order": 1
    },
    "recipe": {
      "name": "Toast",
      "numberOfDays": 2,
      "numberOfPersons": 2,
      "ingredients": [],
      "recipeId": 1
    },
    "mealId": 1,
    "hasBeenShopped": false,
    "numberOfPersons": 1
  },
  {
    "day": "2023-08-13T00:00:00",
    "mealType": {
      "name": "Lunch",
      "mealTypeId": 2,
      "order": 2
    },
    "recipe": {
      "name": "Pasta",
      "numberOfDays": 2,
      "numberOfPersons": 2,
      "ingredients": [],
      "recipeId": 2
    },
    "mealId": 2,
    "hasBeenShopped": false,
    "numberOfPersons": 1
  }
]

On a dashboard, I want to display Breakfast: Toast | Lunch: Pasta.

Now my question is which transformation I have to use within the HTTP binding for that? Using JSONPATH, this would be the necessary single transformation steps:

  • JSONPATH:$.mealType.name
  • JSONPATH:$.recipe.name

But how do I concat them? And how can the transformation handle a dynamic number of array elements (e. g. Breakfast, Lunch and Dinner)?

For the sake of completeness: I’m controlling the API, so in theory I could do the string dance on the server-side - but I’d appreciate it to not change the API if it can be accomplished in openHAB.

Thank you :wave:t2:

Well, afaik you’ll have to build JSONPATH expressions for every value, like that:

JSONPATH:$[?(@.mealType.name=='Lunch')].recipe.name
JSONPATH:$[?(@.mealType.name=='Breakfast')].recipe.name

as openHAB JSONPATH doesn’t allow to get multiple values from one JSONPATH call.

I’m pretty sure it would be much better to rework the API, though, as it would be even more complex to get a specific meal for a specific date.

Even though the input is JSON, if you really want one item with formatted values you should consider REGEX transform instead.

But, I personally would also just populate 3 different items and achieve the desired formatting that you want by placing the the items into one custom widget.

Thank you guys for your suggestions :muscle:t2:

But, I personally would also just populate 3 different items and achieve the desired formatting that you want by placing the the items into one custom widget.

Sorry, but I don’t understand how this would solve the problem challenge of having not always three but a different number of meals

Use JS or RB (Ruby).

Since I use JRuby / Ruby, here’s an example in jruby:

Transformation: RB:|require "json"; JSON.parse(input).map { |meal| "#{meal["mealType"]["name"]}: #{meal["recipe"]["name"]}" }.join(" | ")

You could put that into a transformation file if you prefer it instead of inline - makes it easier if you need this transformation in multiple items / things.

1 Like

Oops, you’re on openhab 3.4. In which case, the script transformation needs to be saved as conf/transform/meals.script

Transformation: SCRIPT:rb:meals.script
and the meals.script:

input ||= nil
require "json"
JSON.parse(input).map { |meal| "#{meal["mealType"]["name"]}: #{meal["recipe"]["name"]}" }.join(" | ")
1 Like

Your widget would be where this filtering takes place. The way the JSONpath transform works, if it doesn’t find a result it just returns the whole JSON string. So, if you have all three meals in the original object each meal item would have an appropriate string value. If one of the meals was missing from the original data then the missing meal, instead of a single value would be the whole JSON string. This would be easy to test for in a widget (e.g., presence of "mealTypeID" in the Item state) to determine whether to show the information about that meal or not.

Widgets themselves can also work with JSON strings. If you wanted to, you could just skip the JSONpath or REGEX or Script transform and store the whole returned JSON string in a single string item. Then you could do all the parsing in the widget expressions.

@JimT you are my freaking hero of the day! :partying_face: works like a charm, thank you so much