JSONata only return existing items

I have an object and an array of urls.

example array:

['http://airbnb.design/feed/','http://nautil.us/rss/all']

example object:

{
  "attr": { "version": "1.1" },
  "head": [{ "title": ["some opml"] }],
  "body": [
    {
      "outline": [
        {
          "attr": { "text": "Design", "title": "Design" },
          "outline": [
            { "attr": { "text": "Airbnb Design", "title": "Airbnb Design", "description": "", "type": "rss", "version": "RSS", "htmlUrl": "https://airbnb.design/", "xmlUrl": "http://airbnb.design/feed/" } }, 
            { "attr": { "text": "web.dev: Blog", "title": "web.dev: Blog", "description": "", "type": "rss", "version": "RSS", "htmlUrl": "https://web.dev/blog/", "xmlUrl": "https://web.dev/feed.xml" } }]
        },
        {
          "attr": { "text": "Science", "title": "Science" },
          "outline": [
            { "attr": { "text": "Nature", "title": "Nature", "description": "", "type": "rss", "version": "RSS", "htmlUrl": "http://feeds.nature.com/nature/rss/current", "xmlUrl": "http://www.nature.com/nature/current_issue/rss" } }, 
            { "attr": { "text": "Nautilus", "title": "Nautilus", "description": "", "type": "rss", "version": "RSS", "htmlUrl": "", "xmlUrl": "http://nautil.us/rss/all" } }]
        }
      ]
    }
  ]
}

I want to remove the containing object when the xmlUrl matches (ie. the urls don't work, so exclude/remove them)

I see I can do something like **[xmlUrl!='http://nautil.us/rss/all'] but that only returns an array excluding the object containing the url.

Using a javascript function became somewhat convoluted because of the nested properties and was hoping that jsonata could make quick work of this...if I only would understand it :')

With the help of qwen2.5-coder 14B locally on my mac, I was able to generate a javascript function that works.

function removeOutlineByXmlUrl(inputObject, xmlUrlToRemove) {

    function processOutline(outlineArray) {
        return outlineArray.filter(item => {
            if (item.outline && item.outline.length > 0) {
                item.outline = processOutline(item.outline);
            }
            return item.attr.xmlUrl !== xmlUrlToRemove;
        });
    }

    const resultObject = inputObject

    if (resultObject.body && resultObject.body.length > 0) {
        resultObject.body.forEach(bodyItem => {
            if (bodyItem.outline && bodyItem.outline.length > 0) {
                bodyItem.outline = processOutline(bodyItem.outline);
            }
        });
    }

    return resultObject;
}

I dont think I have ever used recursion like this myself, nifty, you learn every day.

If you only want to remove the object in the msg.payload.body[0].outline would this not do it. Or does the body have more than 1 array element

[{"id":"5659df32a49fb39c","type":"inject","z":"667cec54c048503c","name":"","props":[{"p":"payload"},{"p":"urls","v":"[\"http://airbnb.design/feed/\",\"http://nautil.us/rss/all\"]","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{   \"attr\": { \"version\": \"1.1\" },   \"head\": [{ \"title\": [\"some opml\"] }],   \"body\": [     {       \"outline\": [         {           \"attr\": { \"text\": \"Design\", \"title\": \"Design\" },           \"outline\": [             { \"attr\": { \"text\": \"Airbnb Design\", \"title\": \"Airbnb Design\", \"description\": \"\", \"type\": \"rss\", \"version\": \"RSS\", \"htmlUrl\": \"https://airbnb.design/\", \"xmlUrl\": \"http://airbnb.design/feed/\" } },              { \"attr\": { \"text\": \"web.dev: Blog\", \"title\": \"web.dev: Blog\", \"description\": \"\", \"type\": \"rss\", \"version\": \"RSS\", \"htmlUrl\": \"https://web.dev/blog/\", \"xmlUrl\": \"https://web.dev/feed.xml\" } }]         },         {           \"attr\": { \"text\": \"Science\", \"title\": \"Science\" },           \"outline\": [             { \"attr\": { \"text\": \"Nature\", \"title\": \"Nature\", \"description\": \"\", \"type\": \"rss\", \"version\": \"RSS\", \"htmlUrl\": \"http://feeds.nature.com/nature/rss/current\", \"xmlUrl\": \"http://www.nature.com/nature/current_issue/rss\" } },              { \"attr\": { \"text\": \"Nautilus\", \"title\": \"Nautilus\", \"description\": \"\", \"type\": \"rss\", \"version\": \"RSS\", \"htmlUrl\": \"\", \"xmlUrl\": \"http://nautil.us/rss/all\" } }]         }       ]     }   ] }","payloadType":"json","x":510,"y":1120,"wires":[["fc362e0308a48ac0"]]},{"id":"fc362e0308a48ac0","type":"change","z":"667cec54c048503c","name":"","rules":[{"t":"set","p":"payload.body[0].outline","pt":"msg","to":"[$filter($$.payload.body[0].outline, function($v){\t    $count($v.**.xmlUrl[$ in $$.urls]) = 0\t})]","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":750,"y":1120,"wires":[["984cf197480bc4af"]]},{"id":"984cf197480bc4af","type":"debug","z":"667cec54c048503c","name":"debug 2579","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1010,"y":1120,"wires":[]}]

This deleted all items in the body ?

No, it removes all objects from the msg.payloadbody[].outline array, that has a xmlURL in the msg.url array.
Output

{
    "attr": {
        "version": "1.1"
    },
    "head": [
        {
            "title": [
                "some opml"
            ]
        }
    ],
    "body": [
        {
            "outline": []
        }
    ]
}

If I am mistaken with what you require the output to be, please supply what the output should look like.

With this as input:

['http://airbnb.design/feed/','http://nautil.us/rss/all']

should produce this as output (ie. remove the object where the url appears under xmlUrl.

{
  "attr": { "version": "1.1" },
  "head": [{ "title": ["some opml"] }],
  "body": [
    {
      "outline": [
        {
          "attr": { "text": "Design", "title": "Design" },
          "outline": [
            
            { "attr": { "text": "web.dev: Blog", "title": "web.dev: Blog", "description": "", "type": "rss", "version": "RSS", "htmlUrl": "https://web.dev/blog/", "xmlUrl": "https://web.dev/feed.xml" } }]
        },
        {
          "attr": { "text": "Science", "title": "Science" },
          "outline": [
            { "attr": { "text": "Nature", "title": "Nature", "description": "", "type": "rss", "version": "RSS", "htmlUrl": "http://feeds.nature.com/nature/rss/current", "xmlUrl": "http://www.nature.com/nature/current_issue/rss" } }
          ]
        }
      ]
    }
  ]
}

Try

[$map($$.payload.body[0].outline, function($v){
    $v ~> |$|{
        "outline": [$filter($.outline, function($vf){
            $not($vf.attr.xmlUrl in $$.urls)
        })]
    }|
})]

Or more concise

[$$.payload.body[0].outline ~> |$|{
    "outline": [$filter($.outline, function($vf){
        $not($vf.*.xmlUrl in $$.urls)
    })]
}|]

[edit] Wondering if node-red-contrib-fastxml (node) - Node-RED may return a more workable object. (guessing that you object is parsed from an xml)

1 Like