How to read property list (plist) items ?

Hi,

I tried many times many solutions and nobody seems to know or simply never shared how read a property list at many levels. I tried some tutorials but nobody talk about how deep we can look and read a plist. Here is an example of my plist I want to read. This is very simple. I want to be able to get all the strings of “ShortURL” key.

What I tried so far that surely work is this:

tell application "System Events"
	tell property list file thePListPath
		tell contents
			{value of property list item "FiletoDownload"}
		end tell
	end tell
end tell

How can I look at the right place ? I hope somebody know the answer. :slight_smile: I hope I will learn something new. The concept behind how to read is more complex (in my point of view) than write a simple plist, go figure why !?

Thank you very much in advance !

Hello!

I know for certain there are at least one thread in here, dealing with propertylists in detail.

Maybe Satimage.osax was used in place of system events but still, the thread dealt with the details of reading arrays or dicts from property list files, like you have in yours.

All there is to it, is really getting the parent element, seeking down one level further, by key,until you have what you need.

Please have a look at that thread, and see if it can help you. Nigel Garvey and McUsr participated in that thread, NIgel taking care of the intrinsics! :slight_smile:

Edit

The link to the post. I think Satimage’s xmllib was used, since neither SystemEvents, nor the defaults commmand supports the Dict structure.

It seems to me though, that the solution adayzdone below has come up with it, is preferable! :smiley:

Download & install this first.

http://www.macosxautomation.com/applescript/apps/runner_vanilla.html

tell application "ASObjC Runner"
	set xxx to read plist at alias "path:to:your:Info.plist"
	set yyy to value for label "ShortURL" in records xxx
end tell

Sadly, I don’t think it’s that simple. The value for label command can’t flatten a nested AS record.

Oops, I tested that on a “flat” plist.

I suspect there’s a better way if you know XPath queries, but this is a start:

tell application id "au.com.myriad-com.ASObjC-Runner" -- ASObjC Runner.app
	extract from XML "Macintosh HD:Users:shane:Desktop:Test.plist" matching "//string[3]"
end tell

Hello! :slight_smile:

This is kind of brute force line oriented approach, but it should work, at least it did for me, if the url’s are just to be gotten as an unordered collection! The snippet below, filters out the urls in the SoftURL keys with a line of shell script,
and collects them into a list of urls.


set theFn to "/Users/me/Junk/the.plist"

set theresult to do shell script "for a in `grep -n SoftURL " & theFn & "| tr -d \"a-zA-Z:<>/\"` ; do  a=$((a+1))  ; sed -n ${a}p " & theFn & " | tr -d ' ' ; done "
set tids to AppleScript's text item delimiters
set AppleScript's text item delimiters to "</string>"
set better to every text item of theresult
set AppleScript's text item delimiters to ""
set better to better as text
set AppleScript's text item delimiters to "<string>"
set theresult to every text item of better
set AppleScript's text item delimiters to ""
set theresult to theresult as text
set tc to (get count paragraphs of theresult)
set urllist to {}
repeat with i from 1 to tc
	set end of urllist to paragraph i of theresult
end repeat
set AppleScript's text item delimiters to tids

(There is a spelling error in the plist file one key is ended with “/string>” and not “”)

You can also try:

set xxx to read file ((path to desktop as text) & "example.plist")
set yyy to do shell script "echo " & quoted form of xxx & " | grep -A1 SoftURL | grep string | sed -E 's/<?\\/?string>//g'"

As McUsr points out, (There is a spelling error in the plist file one key is ended with “/string>” and not “”).
If the error did not exist, the command would be:

 set yyy to do shell script "echo " & quoted form of xxx & " | grep -A1 SoftURL | grep string | sed -E 's/<\\/?string>//g'"

Having had some recent exercise with System Events & plists I tried to store the XML in post # 1 in a plist file.
And found another typo…
It needs an additional at the end.

Of course that’s maybe not so relevant when ‘brute-force’ parsing plain text.
But the actual data do state it’s a plist - if the ‘brute-force’ depends on that it’ll fail, obviously.

The OP says he experimented with SE: that does not exclude the possibility that the errors pre-existed.
I did not try to read the malformed plist with SE. I doubleclicked, to open it in Plist Editor, and it refused.

Hi !

I’m surprised, thank you for your contributions. I was sure MacScripter is the place to ask this kind of question. So everything seems interesting. I just saw I forgot to set the level of difficulty of my script. I cannot use any plugins or apps externally of AppleScript. I thought about use grep, shell scripts of any kind but I’m dependent on this way of doing that I don’t have any kind of experience. I was looking on how I can use the AppleScript syntax, not shell, to read a plist. Maybe this is impossible with a multilevel plist. :frowning: If you have any other solutions please share. At first I will check the solutions you shared and see if I can well abapt it to my project.

Yes I wrote part of my example of my plist by hand, from a copy/paste of the original one where it’s 50 bigger. This is totally possible to see errors. My goal is really to find explanation of how, in general, I can read any kind of plist/xml file with many multi levels. :wink:

OK, I fixed and paste again in my first post, my plist 100 % working, that comply, no errors. Sorry for this mistake. Hope this can help you to help me. :smiley:

Hello Frédéric.

maybe this plain vanilla code will help you.


--[SCRIPT extract URLs]

(path to desktop as text) & "Frédéric.plist"

read file result

set liste to items 2 thru -1 of my decoupe(result, "<key>SoftURL</key>")

set lesAdresses to {}
repeat with blahblah in liste
	contents of blahblah
	item 2 of my decoupe(result, "<string>")
	item 1 of my decoupe(result, "</string>")
	set end of lesAdresses to result
end repeat
lesAdresses

--=====

on decoupe(t, d)
	local oTIDs, l
	set oTIDs to AppleScript's text item delimiters
	set AppleScript's text item delimiters to d
	set l to text items of t
	set AppleScript's text item delimiters to oTIDs
	return l
end decoupe

--=====
--[/SCRIPT]

I just saved your datas after adding the missing character “<”.

Yvan KOENIG (VALLAURIS, France) vendredi 20 juillet 2012 18:48:24

Merci Yvan vraiment apprécié ! :slight_smile:

This more close to what I was looking for. But so far this is only show me that AppleScript team failed to do good syntax to explain how, from System Events, use the Property List Suite dictionary for reading. Like I wrote before, no problem to create one. I thought about “AppleScript’s text item delimiters” a month ago, but I told me that I should ask on this forum if someone know how to perfectly use the Property List Suite dictionary for reading multi level plist and avoid your actual solution. Your solution is exactly what I was looking for without what I wrote before. Maybe this is will challenge a few other scripters here, to find the way to use well what is in place from Apple.

Thanks again ! :cool:

System Events’s Property List Suite is good if you know (and can rely on!) the structure of the property lists. Assuming that by “ShortURL” key you mean “SoftURL” key, the strings can be extracted from plists like the one in your original post like this:

set filePath to ((path to desktop as text) & "Test.plist") -- Or wherever your file is. Must be a path.

tell application "System Events"
	set SoftURLs to value of property list item "SoftURL" of ¬
		property list item "NetRestore" of ¬
		every property list item of ¬
		property list item "ProductTest" of ¬
		property list item "FiletoDownloadVersions" of ¬
		property list item "2" of ¬
		property list item "FiletoDownload" of ¬
		contents of property list file filePath
end tell
--> {"http://website.com/test1.soft.zip", "http://website.com/test2.soft.zip"}

I find that sometimes it’s just easier to work with the plist in applescript. Then you can use all of your applescript skills to find the information rather than using property list syntax. For example you can convert your example plist into an applescript object like this and then work with it however you need.

set f to (path to desktop as text) & "test.plist"

tell application "System Events"
	set theContents to contents of property list file f
	set theValue to value of theContents
end tell

return {class of theValue, theValue}

Now that you can see it, it’s not too hard to create this to get your answer…

set f to (path to desktop as text) & "test.plist"
tell application "System Events"
	set theContents to contents of property list file f
	set theValue to value of theContents
end tell

set softURL1 to SoftURL of NetRestore of |1a001| of ProductTest of FiletoDownloadVersions of |2| of FiletoDownload of theValue
set softURL2 to SoftURL of NetRestore of |1b001| of ProductTest of FiletoDownloadVersions of |2| of FiletoDownload of theValue
return {softURL1, softURL2}
--{"http://website.com/test1.soft.zip", "http://website.com/test2.soft.zip"}

The point for me is to see the plist information in applescript so I have an understanding of the structure. As Nigel mentions, you have to know about the structure if you want to do something with it via applescript property list file syntax.

The problem, though, is that you have parsed the sample – I suspect the full XML includes a lot more than |1a001| and |1b001|. That’s why I think something based on an XPath query would be more the go.

Advantage of property list items is that the file will be serialized as well while XML only parses the file, the other advantage is that I (personally) prefer to use as less 3rd party software as possible. So if the list gets bigger you can simply use something like:

set f to (path to desktop as text) & "test.plist"
tell application "System Events"
	tell (value of (contents of property list file f)) as record
		set softURLs to {}
		repeat with p in productTest of FiletoDownloadVersions of |2| of FiletoDownload of it as list
			set end of softURLs to softURL of NetRestore of p
		end repeat
	end tell
end tell

return softURLs

Yes. “Vanilla rules!” That was more or less the point of my earlier post.

  • use the plist as such, it’s a native format
  • use the software provided to read it: System Events
  • use the native data type it returns: record

(I tend to simplify to the bare bone) :stuck_out_tongue:

Hello!

My few cents is that System Events is single threaded, and a major component for scripters, and should be avoid being bogged down at all costs, as it can lead to unstability, at least slow repsonses. Because what happens when the propertylist file is malformed for example?

So, when it comes to parsing of propertylist files, I’d prefer xmllib of Satimage, or XPath queries.

That’s just me.