I am trying to create an AppleScript that will process only JSON files. As part of the selection-filtering, I used the following snippet:
set this_selection to the selection
if this_selection is {} then error "Please select some content."
repeat with this_item in this_selection
set current_name to the name of this_item
set docPath to path of item 1 of this_item
-- Using the 'txt' from 'type2' enumeration to select the JSON file
-- This is not ideal, but gets me at least part of the way
-- Additional filtering (such as document 'kind') can be done
if type of this_item is txt then
-- …
-- Do the processing here
-- …
else
display alert "DEVONThink Pro" message "No JSON selected." as warning giving up after 3
end if
end repeat
The issue is that the type is not being set to txt, even though the DevonThink3 AppleScript dictionary indicates that this should be the value for all text files (as part of the type2 enumeration).
Using Script Debugger, I can see that the type value for the selected JSON file is indeed a text file.
I have been able to work around this by using this if condition instead:
…
if kind of this_item is "JSON File" then
…
but wanted to check if the earlier type check for txt types is incorrect, or a bug? The corresponding DT3 dictionary for type2 enumeration is:
As this is certainly feasible, I think it might be a lot (a lot, lot, lot) easier doing with a JavaScript file. You could wrap JSON.parse() into a try - catch block so that you don’t need to check for JSON first. Like so
let app=Application("DEVONthink 3");
app.includeStandardEditions = true;
let sel = app.selection();
sel.forEach(file => {
/* no need for these two to process JSON */
let name = el.name();
let path = el.path();
try {
let result = JSON.parse(file.plainText());
} catch(e) {
// process exception
}
})
As a side note: I’m not sure, why you use path of item 1 of this_item in your AppleScript to get at the path of the current item – the item is not a list, I think, so you could just refer to it directly, as you did with name in the line directly before.
AFAIK from many trial and errors, this is necessary sometimes. In scripting DT, you don’t always get a list when it is an array of reference objects. Sometimes the array becomes a list of list with each item being a list containing an item. Each’s item 1 is to coerce the item into an item instead of a list. I can’t tell exactly the condition of its happening but it happens. Perhaps the DT experts will be able to explain it better.
However, if we use repeat with n from 1 to count of this_selection, the AppleScript will always coerce item n into an item. That’s why I always use repeat with n from … whenever I’m manipulating arrays with reference objects.
One last time: We’re in a loop here, and this_item is the current element. It is either a list or an object. It can’t change what it is inside the same loop. Unless AppleScript is even weirder than I think.
Oh, and the loop is repeat with this_item in this_selection anyway, so your first case.
I am not trying to prove myself correct. This is a lesson learnt from many trial and errors that might be beneficial to others. In fact, a few years ago a forum member also mentioned in this forum about this after many rather frustrating puzzles in using repeat with each to manipulating array with objects.
Sometimes a loop won’t work when using each, but using each’s item 1 will. But as a non-pro user, I find using repeat with n from … is the safest way to go for.
After having had a closer look, I understand that you’re comparing apples to oranges here
You have one loop that iterates over the list (repeat with item in list), whereas the other one does not. It just iterates over the numbers from 1 to count list. That is fundamentally different. I’m using “item” and “list” here to make the concepts clearer (hopefully).
repeat with item from list
iterates over the list, returning each of its elements in turn. So item is always exactly this: an item.
In
repeat with n from i to count a
you do not access the list at all. You just get at the number of its elements (count). Then, inside the loop, you have to access the list with a's item n. Which of course gives you an item.
In conclusion: Just because both loops use repeat, they do not work the same way. One of AppleScript weirdnesses. There’s more on repeat here. Apparently, Apple thought keywords were expensive, when it conceived of its scripting language. Whereas one has “while”, “repeat”, “for”, “foreach” and so on in other languages, AppleScript only knows “repeat”. Which obviously leads to confusion.
Compare with the same thing in JavaScript:
for (i = 0; i < list.length; i++) {
console.log(list[i]); /* item i of list */
}
vs.
list.forEach(item => { console.log(item))}; /* forEach returns each item in turn */
Thanks for pointing out the redundant path of item 1 of this_item. It was probably a hold-over from another script that I used as the foundation.
I changed it to be:
set docPath to the path of this_item
and it is working fine.
Using JavaScript is certainly a great recommendation. However, for my use-case, I am just bulk-processing the JSON files (at the file-level), and not parsing them in the script. Hence AppleScript does the job equally well.