Saturday, October 31, 2020

#1 2018-06-28 12:47:18 am

ProGrammer
Member
Registered: 2016-12-16
Posts: 24

Store, Retrieve, Update Property list file without overwriting it

I am working on a script to group file names and store them in a property list (.plist) file.
The following function takes in a collective name for the group of files (ie. "Group1") and a list of files belonging to it (ie. {"File1.png", "File2.png", "File3.png"}). It is supposed to write the information to the plist in the following format:

<plist version="1.0">
<dict>
    <key>lastUpdate</key>
    <date>2018-06-28T05:07:43Z</date>
    <key>groups</key>
    <dict>
        <key>Group1</key>
        <array>
            <string>File 1.png</string>
            <string>File 2.png</string>
            <string>File 3.png</string>
        </array>
        <key>Group2</key>
        <array>
            <string>File 4.png</string>
            <string>File 5.png</string>
            <string>File 6.png</string>
        </array>
    </dict>
</dict>
</plist>

Applescript:


on saveToPlist(groupName, groupFiles)
   
   tell application "System Events"
       set theParentDictionary to make new property list item with properties {kind:record}
       set thePropertyListFilePath to "~/Desktop/Groups.plist"
       
       -- Get / Create Property List File
       if not (exists file thePropertyListFilePath) then
           set thePropertyListFile to make new property list file with properties {contents:theParentDictionary, name:thePropertyListFilePath}
           log "New plist created"
       else
           set thePropertyListFile to property list file thePropertyListFilePath
           log "Plist exists"
       end if        
       
       -- Add to Property List File
       tell property list items of thePropertyListFile
           make new property list item at end with properties {kind:date, name:"lastUpdate", value:current date}
           
           if not (exists property list item "groups") then
               set groupsRec to make new property list item at end with properties {kind:record, name:"groups"}
               log "groups record created"
           else
               set groupsRec to property list item "groups"
               log "groups record already exists"
           end if
           
           make new property list item at end of groupsRec with properties {kind:list, name:groupName, value:groupFiles}
       end tell

   end tell
end saveToPlist

1. There is a problem with the last section (adding to the plist file). The script doesn't seem to find the property list item "groups". I would also like to like the script to append to the "groups" record instead of replacing the content completely.

2. How would I access the values in each group?

3. How would I add/remove individual values in each group's array once they are already in the file?

Any help is appreciated. Please let me know if you need clarification on anything. Cheers.

Offline

 

#2 2018-06-28 01:33:39 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6473

Re: Store, Retrieve, Update Property list file without overwriting it

Dealing with anything but the simplest property list file in System Events is slow and painful. It's much simpler and faster to use AppleScriptObjC. That way you can just copy-and-paste the handlers to read and write the file, and manipulate the information as a normal AppleScript record.

Here's an example:

Applescript:

use AppleScript version "2.5" -- 10.11 or later
use framework "Foundation"
use scripting additions

set thePath to POSIX path of ((path to desktop as string) & "Groups.plist")
-- define your info as a record or array
set theInfo to {lastUpdate:(current date), groups:{Group1:{"File1.png", "File2.png", "File3.png"}, Group2:{"File4.png", "File5.png", "File6.png"}}}
-- store it in a file
my writeToPlist:theInfo inFile:thePath
-- read from file
set newInfo to my readPlistFrom:thePath
-- modify info
set groups of newInfo to groups of newInfo & {Group3:{"File7.png", "File8.png", "File9.png"}}
set lastUpdate of newInfo to (current date)
--write newInfo to property list
my writeToPlist:newInfo inFile:thePath

on writeToPlist:someASThing inFile:posixPath
   -- convert to property list data
   set {theData, theError} to current application's NSPropertyListSerialization's dataWithPropertyList:someASThing |format|:(current application's NSPropertyListXMLFormat_v1_0) options:0 |error|:(reference)
   if theData is missing value then error (theError's localizedDescription() as text)
   -- write data to file
   set theResult to theData's writeToFile:posixPath atomically:true
   return theResult as boolean -- returns false if save failed
end writeToPlist:inFile:

on readPlistFrom:posixPath
   -- read file as data
   set theData to current application's NSData's dataWithContentsOfFile:posixPath
   -- convert to Cocoa object
   set {theThing, theError} to current application's NSPropertyListSerialization's propertyListWithData:theData options:0 |format|:(missing value) |error|:(reference)
   if theThing is missing value then error (theError's localizedDescription() as text)
   -- we don't know the class of theThing for coercion, so...
   set listOfThing to current application's NSArray's arrayWithObject:theThing
   return item 1 of (listOfThing as list)
end readPlistFrom:


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#3 2018-06-28 02:22:00 am

ProGrammer
Member
Registered: 2016-12-16
Posts: 24

Re: Store, Retrieve, Update Property list file without overwriting it

Thank you for the hint with AppleScriptObjC. I see what you are doing with theInfo at the very top, in my quest to make it as dynamic as possible, I have also included the following code to assign a name to the group (instead of calling it Group1..3 etc), and pass in a list.

Applescript:


-- A list
set theList to {"File1", "File2", "File3"}
-- Assign Name to Group
set groupName to the text returned of (display dialog "Name of this group:" default answer "")

I then update using:

Applescript:


...
set newInfo to my readPlistFrom:thePath
set groups of newInfo to groups of newInfo & {groupName:theList} --> Issue described below
my writeToPlist:newInfo inFile:thePath
...

How do I set the name of the group to my input dynamically (ie. the string in groupName - returned from the dialog)? Right now, it saves it as "groupName" instead of the string contained in the variable. (theList saves just fine)

Offline

 

#4 2018-06-28 06:00:26 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6473

Re: Store, Retrieve, Update Property list file without overwriting it

ProGrammer wrote:

How do I set the name of the group to my input dynamically (ie. the string in groupName - returned from the dialog)?



ASObjC can handle that simply too:

Applescript:

set theRecord to (current application's NSDictionary's dictionaryWithObject:"File10.png" forKey:groupName) as record


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#5 2018-06-28 09:00:00 pm

ProGrammer
Member
Registered: 2016-12-16
Posts: 24

Re: Store, Retrieve, Update Property list file without overwriting it

Shane, it now successfully saved the record using the dialog result as key.

When I read back the file using the readPlistFrom routine, I am unable to enumerate the results in a manner that allows me to tell the routine the key and get back a list of values for that key, rather than a dump of everything.

Applescript:

on getValuesForKey:theKey
   --> return values at theKey as list
end getValuesForKey:

I would also like to be able to delete a key (and thus any values associated with it) by specifying which key to remove.

Applescript:

on deleteRecord:theKey
   --> delete record (key-value(s) pair) at theKey
end deleteRecord:

Lastly but not least, I would like to manipulate a specific value under a specific key, in order to update the record for that key only.

Applescript:

on manipulate:theKey atValue:theValue newValue:newValue
   --> update/replace theValue under key theKey with newValue
end manipulate:atValue:newValue:

From a few hours of research, I know about removeObjectForKey but can't seem to implement it correctly. Does the file need to be read in, key-values removed and saved or is there a simpler way that does it all in one sweep.

Appreciate the help with the 3 routines above.

Offline

 

#6 2018-06-28 11:04:54 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6473

Re: Store, Retrieve, Update Property list file without overwriting it

ProGrammer wrote:

When I read back the file using the readPlistFrom routine, I am unable to enumerate the results in a manner that allows me to tell the routine the key and get back a list of values for that key, rather than a dump of everything.



This is also probably better done in ASObjC. If you're comfortable with it, you could modify the second handler so it just returns theThing, which in this case with be an NSDictionary. You can also change the options parameter to (current application's NSPropertyListMutableContainersAndLeaves) so you can modify it in place.

Anyhow, once you have the groups dictionary, you can do something like this rough outline:

Applescript:

set theKeys to groupsDict's allKeys()
repeat with oneKey in theKeys
-- retrieve value
set itsValue to groupDict's objectForKey:oneKey
-- or to change it
if oneKey as text = "Group1" then
groupDict's setObject:newListOfEntries forKey:oneKey
-- or to delete it
if oneKey as text = "GroupUnwanted" then
groupDict's setObject:(missing value) forKey:oneKey

That should get you started.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)