Json_foreach always executes once, even if the json array is empty

Hello,
I'm using Mp3tag v3.27a under windows 10 and recently observed during testing of a script for the discogs WS api, that the body of a json_foreach loop was executed, although the json array, it was acting on, was empty or even did not exist. AFAIK in other programming languages, for or foreach loops never iterate, if the stop condition is already met from the start on. So this probably is a bug in the WSS language.
An example for such a foreach loop you can find in the following code snippet:

outputto "Companies"
	json_foreach "companies"
		json_select "entity_type_name"
		sayrest
		say ": "
		json_select "name"
		sayrest
		say "; "
	json_foreach_end

If you insert this code in the file "Discogs Release ID.src" e.g. behind line 50 and use as test case https://www.discogs.com/de/release/4567531-Albert-Collins-Robert-Cray-Johnny-Copeland-Showdown you will get a tag COMPANIES, that contains the string ": ; ". This is because the above WS does not contain a section about Companies, but the foreach loop iterates once.
The corresponding debug output it looks like that:

Script-Line    : 54
Command        : json_foreach
Parameter 1    : >companies<

JSON objects   : ><
JSON loops     : >companies: 1/0<

Output         : ><

Line and position:
0
^

This shows that iteration starts at element 1 of an array with size 0.
I know that there is a workaround by surrounding the foreach loop with

json_select_many_count "companies" "name" 1
ifgreater 0
	outputto "Companies"
	json_foreach "companies"
		.......
	json_foreach_end
endif

It would be nice, if this could be fixed, because it would save some lines of code and some run time.

1 Like

I came across the same behavior before (but hadn't reported it yet). From my research on this bug I'm theorizing that, for loops with 0 entries, the WSS parser doesn't know how to proceed, and simply ignores the json_foreach "S" and json_foreach_end lines and runs the rest.

Good job with the bug catching and workaround. :+1:

Thank you for reporting! It's now fixed in Mp3tag v3.36-beta.3.

I hope that not too many Tag Sources relied on the behavior that's now fixed.

1 Like

Thank you for fixing it.

One tag source, I know of, that relied on this "feature" was the [WS] Bandcamp (search by URL) up to version 0.4.5, if used for single track URLs. In the newer versions from 0.4.6 on this issue had already been handled.

Sorry to report that its broken the TMDB script, as it relies on this behaviour in the .inc file to be able to process movies (single track) or TV shows (multiple as an album) with the same code by continuing through this single loop even if there's nothing present, i.e.:

	    # Still "season/#" object, get "episodes"  #
	    # but if not found, stays in response body #
	    json_foreach "episodes"
	        LENGTH, ITUNESMEDIATYPE, TVEPISODE etc...
	    json_foreach_end

If there's an episodes object from a TV show to loop through, great, otherwise it would still process what it could find for a movie. It's been convenient in my case not having to constantly include extra code for a switch between an expected loop to a forced single-run loop of the same code.

I'll still look into a fix but it's worth a try asking if it's possible to have a parameter to decide whether json_foreach proceeds with the single loop (empty or 0) or bypass as has been requested (1)? Hopefully that might save issues with other scripts that have sneaked this behaviour in.

Update: not a big deal, very easy and cheap to fix actually, just contain each end of a loop with an If with an appropriate trigger when needed:

	IfOutput "TVSeason"
	    json_foreach "episodes"
	    TVEPISODE, TVEPISODEID etc...
	EndIf

	LENGTH, ITUNESMEDIATYPE etc...

	IfOutput "TVSeason"
	    json_foreach_end
	EndIf

Rather than leave json_foreachend exposed and swap around custom loop amounts at the start. :person_shrugging:

Just a side-note for anyone else needing to update their scripts that a recent upgrade to json_foreach without a parameter outside of an array will also stop becoming a quick single loop and will iterate through objects currently loaded. I got a fright when debugs showing JSON loops : >loops: 1/0< started to show >loops: 1/31< but that's all it is. Just If the loops in and out of your script. :sweat_smile:

1 Like

That's great! Glad you found a solution that quickly :+1:

I also prefer the fixed behavior and correctly implement normal foreach semantics, so that empty or missing targets execute zero times. In the long term, this is the better solution than making the unexpected behavior the default and hiding the corrected behavior behind an optional parameter.

1 Like