inserting a string into a dict item into an array in a Plist file.

Hello.

I’m trying to create a property list structure, that at this point only shall contain an array LibraryPaths with dictionaries containing names and data of the paths. like in a structure like this: I am using system events for this, since it as of SL, can do that officially, I know that I can make this code work with Tiger.

I need help on accessing the previously created items.

(plistFile
/LibraryPaths
/Libpath1
/name
/thepath
/property1
/property2
/Libpath2
etc.
)

There is something I’m doing wrong. Can you tell me what it is?

I also wonder why one sometimes address the property list item 1 directly. I also see this scheme used in UI-Scripting, I wonder why that is and hopes somebody can explain it to me.:wink:


tell application "System Events"
	-- create an empty property list dictionary item
	set the parent_dictionary to make new property list item with properties {kind:record}
	-- create new property list file using the empty dictionary list item as contents
	set the plistfile_path to "~/Desktop/example.plist"
	set this_plistfile to ¬
		make new property list file with properties {contents:parent_dictionary, name:plistfile_path}
	
	make new property list item at end of property list items of contents of this_plistfile ¬
		with properties {kind:list, name:"LibraryPaths"}
	--		Makes an array of Library pathts, the idea is to hold a set of records/dictionaries.
	
	tell property list item "LibraryPaths" of contents of this_plistfile
		make new property list item at end of property list items of it with properties {kind:record, name:"Libpath1"}
		-- creating this dictionary works well.
	end tell
	try
		-- putting this string into the dictionary doesn't work
		tell property list item "Libpath1" of property list item "LibraryPaths" of contents of this_plistfile
			make new property list item at end of property list items of it with properties {kind:string, name:"DisplayedName", value:"firstUserPath"}
			-->  (*System Events got an error: Can't get property list item "Libpath1" of property list item "LibraryPaths" of property list file "/Users/McUsr/Desktop/example.plist".-1728*)
		end tell
	on error e number n
		log e & n
	end try
	--	return
	-- the construct right below this line does neither work.
	try
		set libpaths to a reference to property list item "Libpath1" of property list item "LibraryPaths" of contents of this_plistfile
		tell libpaths
			make new property list item at end of property list items of it with properties {kind:string, name:"DisplayedName", value:"firstUserPath"}
			-->  (*System Events got an error: Can't get property list item "Libpath1" of property list item "LibraryPaths" of property list file "/Users/McUsr/Desktop/example.plist".-1728*)
			
		end tell
	on error e number n
		log e & n
	end try
	
end tell

It doesn’t error if you change the kind of “LibraryPaths” to a record, but I don’t know if it’s what you want.

The ‘make’ command returns the “made” item as a result. It might be easier if you use this rather than writing out the reference.

Hello Nigel.

Thank you for that one. There is still a long way to go with this, I just have to get on the right tracks!

First of all: I have noticed that if I create and array, then it isn’t that simple, as you told in the post above by hinting that it was better to use a dict/record (which has a name (key), to which one get get to it’s values.

Is it always so that you get access to property list elements of an array like this:
(This is not what I’m ending up with, it is just something to make me manage to insert an element into an array
-It will be records, where it is a string here.)


set the plistfile_path to "~/Desktop/example.plist"
tell application "System Events"
	tell property list file plistfile_path
		set aprob to property list item "LibraryPaths" of its contents
		” array
		set bprob to property list item 1 of aprob
		tell bprob
			make new property list item at end of property list items of it with properties {kind:string, name:"DisplayedName", value:"firstUserPath"}
		end tell
		
	end tell
end tell

Put another way: do you always access the values of an array by getting them out of property list item 1 ? If it is so, then I have the answer to why one uses the same construct in GUI-Scripting as well.

Just fooling around with it briefly (I’ve not tried creating a property list file before) it seems that lists (notated as “arrays” in the file) can only hold non-structural values.


tell application "System Events"
	set the plistfile_path to "~/Desktop/example.plist"
	set this_plistfile to ¬
		make new property list file with properties {name:plistfile_path}
	
	set libPathList to (make new property list item at end of property list items of contents of this_plistfile ¬
		with properties {kind:list, name:"LibraryPaths", value:{"LibPath1", "LibPath2"}}) -- Just these string values.
end tell

If you want to put in more property list items, the property list item containing them has to be of the ‘record’ kind:


tell application "System Events"
	set the plistfile_path to "~/Desktop/example.plist"
	set this_plistfile to ¬
		make new property list file with properties {name:plistfile_path}
	
	set libPathList to (make new property list item at end of property list items of contents of this_plistfile ¬
		with properties {kind:record, name:"LibraryPaths"})
	make new property list item at end of property list items of libPathList with properties {kind:record, name:"LibPath1", value:"path:to:LibFile1"}
	make new property list item at end of property list items of libPathList with properties {kind:record, name:"LibPath2", value:"path:to:LibFile2"}
	
	
	value of this_plistfile --> {LibraryPaths:{LibPath2:"path:to:LibFile2", LibPath1:"path:to:LibFile1"}}
	(* NB. This isn't the expected order, probably because records aren't ordered. *)
	
	-- But:
	value of property list item 2 of property list item 1 of contents of this_plistfile --> "path:to:LibFile1"
end tell

It’s also appears to be possible to set the “array” value more directly:


tell application "System Events"
	set the plistfile_path to "~/Desktop/example.plist"
	set this_plistfile to ¬
		make new property list file with properties {name:plistfile_path}
	
	set libPathList to (make new property list item at end of property list items of contents of this_plistfile ¬
		with properties {kind:record, name:"LibraryPaths", value:{LibPath1:"path:to:LibFile1", LibPath2:"path:to:LibFile2"}})
	
	value of this_plistfile --> {LibraryPaths:{LibPath2:"path:to:LibFile2", LibPath1:"path:to:LibFile1"}}
	(* NB. This still isn't the expected order. *)
end tell

Hello Nigel.


tell application "Quicksilver" to show large type "Thank you very much for sorting it out"

I can live easily with a record of records structure - and use lists for unstructured items.

And to get values out in proper order without doing any soting of all the items I have to address them directly.

I figure I have to live with the lack of sort order. But that is ok, I knew I couldn’t expect any order of the items.

But I’m still wondering about the property list item 1, and what is special about that one, and is intended usage. Well, I know about a marvelous piece of code I can inspect. :slight_smile:

Thank you so much, this was really helpful. :slight_smile:

Hello.

I perused some documentation for Smile, and realized that it indeed could store dictionaries in arrays, which really
is a construct often used when working with XML And I also believe it to be easer to parse an array of dicts, than a dictionary of dictionaries. Well, there may be no practical difference, if the item 1 of works with a dictionary as it would with a record, but I haven’t checked it.

Of course I have to try it, and this was trickier than it was supposed to, after a long dive into the AppleScript Mailing Users list did I find the liberating piece. The trickiness goes for adding items to a new property list file. I believe! that it is much easier to parse a property list file with Satimages XMLLib.osax, than with system events. It feels much better, because it is much more closer to the usual way of parsing xml, which I am accustomed to from Java.


set the plistfile_path to (path to desktop folder as text) & "SatimageEx.plist"
-- That sort of easy posix paths from system events are now history.
log plistfile_path
set the_plist to PlistNew

PlistSet the_plist key "LibraryPaths" to {}
-- Line above creates an array it shouldn't¬
--  according to at least how I read the XMLLib's dictionary. 
set a to PlistChild the_plist key "LibraryPaths"
-- *not* PlistGetChild which returns the values.
PlistNewChild {libname:"Library1", libpath:"somepath", appleMods:false} at a
PlistNewChild {libname:"Library2", libpath:"somepath", appleMods:false} at a
PlistSave the_plist in file plistfile_path without binary
PlistClose the_plist

The result: The items of the dict is in wrong order, but the dictionary items are correct within the array. :slight_smile:

Hello.

I thought I should post the rest of the basis operations with XMLLib, how they are used and so on.
I’m not deleting anything, but I’m changing plist files by adding records ahem dictionaries, and changing values.

For those who might find interests in property list files. The thing about parsing property list files, is that their structure is known up front, and almost every property list file, has their own layout. As for my objective for the evening, checking out what there was in the market for property list parsing, I feel that I should be able to produce a solution faster with XMLLib than with System Events. Maybe the code won’t be as elegant as with system events. But here it feels more like a snap to do it. Which I am sure those like Nigel master it from the start, or has invested some time in working with property lists from System Events. I haven’t.

System Events can’t create arrays of dictionaries, but I wonder if that is a downside at all, with regards to the possibility of adding duplicate items into an array. I really haven’t tested that with a dictionary.

Dictionaries has by the way the property that their keys are hashed, so that the values of the keys will be retrieved in the equal amount of time. whether it be element 32767 ( or greater) or item no 1.


set the plistfile_path to (path to desktop folder as text) & "SatimageEx.plist"
set the_plist to PlistOpen plistfile_path as alias
-- Now have the contents to the plist fila via a CRef reference.
set libPathsArray to PlistGet the_plist key "LibraryPaths"
-- This is how to get the contents of the array "LibraryPaths" for usage with
-- user interaction through some dialog or something. you get the contents in a 
-- format that reflects what it was array - list, dict - record and so on.

set refToLibPathsArray to PlistChild the_plist key "LibraryPaths"
-- this instruction PlistChild must be used for getting at the elements
-- via the property list file.


set theFirstItem to PlistGet refToLibPathsArray index 1
-- It doesn't work well to use keys at all for extracting array elements.
set secondFirstItem to item 1 of libPathsArray
-- Why not if the items are extracted any way.

-- * creating a new item to insert into the array *
set aNewItem to {libraryItem:"Library3", libpath:"somepath", appleMods:false}
-- and that was our new item
set theNew to PlistNewChild at refToLibPathsArray
-- must create a new child node. in the right array.
PlistSet theNew to aNewItem
-- here I *Set* then new item to its new contents. 

set anotherNewItem to {libraryItem:"Library4", libpath:"sometotallydifferentpath", appleMods:false}
PlistSet (PlistNewChild at refToLibPathsArray) to anotherNewItem
-- this works on a single line as well

-- So far we have created two new dictionary items. - it is time for a change.
-- the final task would be to change some value in a key value pair of some dictionary of the array
-- and update the plist file with the changes.
set refToSecondDict to PlistChild refToLibPathsArray index 2
-- obtains a reference to the second dictionary in the plist file 

set theLibPath to PlistChild refToSecondDict key "libpath"
-- obtains a reference to the key/value pair I'm going to change
PlistSet theLibPath to "ABrandNewpath"
-- updates the value directly, with the reference to the key.

-- I guess this can be done in one line as well as above,
-- BUT IT DOESN'T WORK FOR A BOOLEAN ( ends up "with to" after "check syntax" )

PlistSet (PlistChild refToSecondDict key "llibraryItem") to "library21"

PlistSave the_plist in file plistfile_path without binary
-- I have to save in order to save the new changes.
-- I have added two new dictionaries to the array, and changed two properties in all in all 4 different ways.
PlistClose the_plist

Hello.

I just wanted to add that there may be some reasons for System Events to support the operations it does on properties lists. It’s operations are closely parallelled by the defaults command, which neither supports the usage of arrays.

This is something to think through before one creates a properties file, as one can leverage on updating the preferences in a “side process” by using the defaults program from a do shell script command and sending the command to the background ignoring application responsens.