Converting saving method to Lion

In my document based application I saved and reloaded the data in OS 10.6 via the method “NSPropertyListSerialization”. It worked well.
It does not in OS 10.7 (Lion). So I changed the method to “NSKeyedArchiver” and “NSKeyedUnarchiver” (as Shane teached me). It works well too.

But now I have the problem to read in the Data saved in OS 10.6 to OS 10.7.

ErrMess.:
Appname(912,0xace542c0) malloc: reference count underflow for 0x1aeb910, break on auto_refcount_underflow_error to debug.
2011-08-01 18:46:59.956 Appname[912:f0b] *** -[NSKeyedUnarchiver initForReadingWithData:]: incomprehensible archive version (-1)

Does anyone know a way to convert the data in a readable form?

Heiner

Can you show us the NSPropertyListSerialization code that is not working in Lion/ Because it should still work fine…

Hi Shane,

here are my script parts. Some of the words are in german language, but I think you can detect the structure.


on dataOfType_error_(typeName, outError)
		set theFormat to 100 -- kCFPropertyListXMLFormat_v1_0
		set theOptions to 0 -- default
		set theError to missing value
		set outError to missing value
		
               -- First collecting the properties:

		set dieSchuelerDaten1 to kDataSource -- 1. data source

		-- die Spaltentitel --- headerCells in table view
		set theTableColumns to kTableView's tableColumns as list
		set theTitleList to {}
		repeat with aColumn in theTableColumns
			set theTitle to (aColumn's headerCell's stringValue)
			set theTitleList to (theTitleList & theTitle)
		end repeat
		set dieSpaltenTitel to theTitleList
		---------------------------
                -- some others:
		set dieKopfzeile to kopfzeile
		set dieAnzahlSpalten to kTableView's numberOfColumns()
		set dieNamenEinfuegung to namenEingefuegt
		set dieStufenart to stufenart
		set dieVerteilungsBestimmung to verteilungBestimmt
		set dieArtDerVerteilung to verteilungFeld's stringValue()
		set dieMaxPunkte to maxPunkteFeld's intValue()
		set dieSicherungGrenze5 to grenze5Feld's intValue()
		set dieSicherungGrenze6 to grenze6Feld's intValue()
		
                -- pushbuttons:
		set dieSicherungBonusMalus to pbBewertungAls's indexOfSelectedItem as integer
		set dieSicherungRelAbsG5 to pbRelAbs5's indexOfSelectedItem as integer
		set dieSicherungRelAbsG6 to pbRelAbs6's indexOfSelectedItem as integer
		set dieSicherungVerteilungNach to pbVerteilungNach's indexOfSelectedItem as integer
		set dieSicherungSchwerpunkt to pbSchwerpunkt's indexOfSelectedItem as integer
		set dieSicherungVerteilung to pbberechneVerteilung's indexOfSelectedItem as integer
		set dieSicherungFormat to pbFormat's indexOfSelectedItem as integer
		
                -- 2. table view, depending of users choice:
                if (stufenart as string) is "oberstufe" then
			set dieSchuelerDaten2 to noDataSource
		else if (stufenart as string) is "u_m_stufe" then
			set dieSchuelerDaten2 to nuDataSource
		else if (stufenart as string) is "" then
			set dieSchuelerDaten2 to {}
		end if

               -- and now putting all together:
		set myData to {ergebnisse1:dieSchuelerDaten1, spalten:anzahlSpalten, spaltenTitel:dieSpaltenTitel, kopfzeile:dieKopfzeile, NamenEinfuegung:dieNamenEinfuegung, maximalePunkte:dieMaxPunkte, VerteilungsBestimmung:dieVerteilungsBestimmung, SicherungGrenze5:dieSicherungGrenze5, SicherungGrenze6:dieSicherungGrenze6, SicherungRelAbsG5:dieSicherungRelAbsG5, SicherungRelAbsG6:dieSicherungRelAbsG6, SicherungBonusMalus:dieSicherungBonusMalus, SicherungVerteilungNach:dieSicherungVerteilungNach, SicherungSchwerpunkt:dieSicherungSchwerpunkt, SicherungVerteilung:dieSicherungVerteilung, SicherungFormat:dieSicherungFormat, ergebnisse2:dieSchuelerDaten2, dieStufenart:stufenart, dieArtDerPunkte:ArtDerPunkte, SicherungArtDerVerteilung:dieArtDerVerteilung}
	
              -- build the property list:	
set theData to current application's class "NSPropertyListSerialization"'s dataWithPropertyList_format_options_error_(myData, theFormat, theOptions, theError)
		if outError is not missing value then
			set contents of outError to my NSError's errorWithDomain_code_userInfo_(my NSOSStatusErrorDomain, my unimpErr, missing value)
		end if
		return theData
	end dataOfType_error_
	
	-- 2. part.
	on readFromData_ofType_error_(theData, typeName, outError)
		set theFormat to missing value --? otherwise error message!
		set theOptions to 0 -- default
		set theError to missing value --outError
		set outError to missing value
		
		set propertyList to current application's class "NSPropertyListSerialization"'s propertyListWithData_options_format_error_(theData, theOptions, theFormat, theError)

                -- read back:
		set anzahlSpalten to propertyList's spalten
		set spaltenTitel to propertyList's spaltenTitel -- in 
		set kopfzeile to propertyList's kopfzeile -- in 
		set ArtDerVerteilung to propertyList's SicherungArtDerVerteilung
		set stufenart to propertyList's dieStufenart
		set verteilungBestimmt to propertyList's VerteilungsBestimmung
		set namenEingefuegt to propertyList's NamenEinfuegung
		set maxPunkte to propertyList's maximalePunkte
		set wertGrenze5 to propertyList's SicherungGrenze5
		set wertGrenze6 to propertyList's SicherungGrenze6
		
		set myBonusMalus to (propertyList's SicherungBonusMalus)
		set ArtDerPunkte to propertyList's dieArtDerPunkte
		set relabsG5 to (propertyList's SicherungRelAbsG5) as string
		set relabsG6 to (propertyList's SicherungRelAbsG6) as string
		set dieverteilungnach to (propertyList's SicherungVerteilungNach) as string
		set derschwerpunkt to (propertyList's SicherungSchwerpunkt) as string
		set berechneDieVerteilung to (propertyList's SicherungVerteilung) as string
		set dasformat to (propertyList's SicherungFormat) as string
		
		set mykDataSource to propertyList's ergebnisse1 -- data source 1
		set myTheDataSource to propertyList's ergebnisse2 -- data source 2

 
		set dataloaded to true 
                -- if true then rebuilding in 'windowDidBecomeMain_(aNotification)'
		
		if outError is not missing value then
			set contents of outError to my NSError's errorWithDomain_code_userInfo_(my NSOSStatusErrorDomain, my unimpErr, missing value)
		end if
		return true
	end readFromData_ofType_error_


errmess.:
2011-08-02 11:30:35.832 Schulnoten 2.2[489:f0b] *** -[MyDocument readFromData:ofType:error:]: unable to set argument 5 - the type o^@ is not supported. (error -10000)

I hope it helps.

Heiner

Try using:

    set propertyList to current application's class "NSPropertyListSerialization"'s propertyListWithData_options_format_error_(theData, 0, theFormat, missing value)

Setting a variable to missing value and then using the variable probably won’t work.

Als, the two “if outError …” blocks might as well be deleted; they don’t work. The last argument passed in to the handlers is a pointer to a pointer (NSError **), and AppleScriptObjC can’t do anything with them.

I did as you wrote, but the same errmess.

I wonder if there’s a problem with myData. What happens if you just set myData to something simple like {a:“a”}? Do you still get the error? Can you put in log statements to confirm where the error is happening?

My scripts are looking now


on dataOfType_error_(typeName, outError)
		set theFormat to 100 -- kCFPropertyListXMLFormat_v1_0
		
		set myData to {a:"a"}
		
		set theData to current application's class "NSPropertyListSerialization"'s dataWithPropertyList_format_options_error_(myData, theFormat, 0, missing value)
		
		return theData
	end dataOfType_error_
	
	
	on readFromData_ofType_error_(theData, typeName, outError)
		
		set propertyList to current application's class "NSPropertyListSerialization"'s propertyListWithData_options_format_error_(theData, 0, missing value, missing value)
		--log propertyList
		
		return true
	end readFromData_ofType_error_

I can’t save it.
errmess:
2011-08-02 15:08:24.073 Schulnoten 2.2[1885:f0b] *** -[MyDocument dataOfType:error:]: unable to set argument 5 - the type o^@ is not supported. (error -10000)

Heiner,

At this stage all I can do is confirm the problem. As a work around, you could try using something like this instead:

set myData to current application's NSDictionary's dictionaryWithDictionary_(myData)
myData's writeToFile_atomically_("/Users/shane/Desktop/Test.plist", true)
set myData to current application's NSData's dataWithContentsOfFile_("/Users/shane/Desktop/Test.plist")

It’s dreadful double-handling, though. It might be better to implement it in writeToURL:ofType:error:, instead of using dataOfType:error:.

I’ll see if I can find out any more…

That shows me, this is not only my special problem.

Shane, I hope you can find out a solution.

Thanks

Heiner

PS: To clarification:
I am working with Mac OS 10.7 and Xcode 3.2.6

I’m baffled, really. It works fine in Objective-C. Try the workaround I suggested.

(And you’re braver than me using Xcode 3 under 10.7 I wish I were…).

So the best solution will probably to add an Objective-C class to your project. Call it something like PLSHelper. The PLSHelper.h file should be like this:

#import <Foundation/Foundation.h>

@interface PLSHelper : NSObject

+(NSData *)dataWithPropertyListSpecial:(id)stuff;

@end

The PLSHelper.m file should be like this:

#import "PLSHelper.h"

@implementation PLSHelper

+(NSData *)dataWithPropertyListSpecial:(id)stuff {
   return ([NSPropertyListSerialization dataWithPropertyList:stuff format:NSPropertyListXMLFormat_v1_0 options:0 error:NULL]);
}

@end

And your script should now use:

      set theData to current application's PLSHelper's dataWithPropertyListSpecial_(myData)

I am sorry Shane,
all what I get is a list in hexadecimals.

But I found out that the file is to open with property list editor.
So I made a work around with an additional menu in the menu bar that calls an open window to get the path.

set theDict to current application's NSDictionary's dictionaryWithContentsOfFile_(filePath)

brings me all the datas in a readable way.

I think that’s an acceptable compromise.

Thanks again

Heiner

Right, it’s raw data – you then return that in your dataOfType_error_ handler.

As log as you have something working.

Oh Yes!!

I was busy, but I turned in a circle.
All what I got was a list in hexas. And how do I set it into the dataOfType_error_ handler?

I tried that:

on readFromData_ofType_error_(theData, typeName, outError)
		try
			set theDecoder to current application's NSKeyedUnarchiver's alloc()'s initForReadingWithData_(theData)

....
stuff for decoding a newer file
...
                     return true
		     on error errmess

--if the user will open an older file then

                        set myHelpersData to current application's PLSHelper's dataWithPropertyListSpecial_(theData)
                        log myHelpersData

-- and now?

                      -- return false
                end try
end readFromData_ofType_error_

on dataOfType_error_(typeName, outError)
       
       set myData to {a:"a"} -- whatever
       
       set theData to current application's PLSHelper's dataWithPropertyListSpecial_(myData)
       
       return theData
   end dataOfType_error_

My problem was to read datas in (not to write).

So I solved it by adding a new menu that calls the following handler (in short):


on openOldFile()
		try
			-- open panel and file path:
			set myOpenPanel to NSOpenPanel's openPanel
			myOpenPanel's setCanChooseFiles_(true)
			myOpenPanel's setCanChooseDirectories_(true)
			myOpenPanel's setAllowsMultipleSelection_(false)
			myOpenPanel's runModal
			set thePath to myOpenPanel's URLs
			set thePathComponents to thePath's pathComponents
			set myPathComponents to item 1 of thePathComponents
			set theCount to myPathComponents's |count|()
			set thePath to "/"
			repeat with i from 2 to (theCount - 1)
				set thePath to thePath & (item i of myPathComponents as string) & "/"
			end repeat
			set filePath to POSIX path of (pfad & item theCount of myPathComponents)
			
			-- read in a dictionary:
			set theDict to current application's NSDictionary's dictionaryWithContentsOfFile_(filePath)
			
			-- set the title of the new window to file name (last part of the path):
			mainWindow's setTitle_(item theCount of myPathComponents as string) -- mainWindow id bound as a property to the main window
			
			-- to get the keys of theDict: set allKeys to theDict's allKeys()
			-- for example (FE) the key for the table's data source is 'tableDatas'
			set myDataSource to theDict's objectForKey_("tableDatas")
			myTableView's reloadData()
			
			-- setting fields:
			maxPointsField's setIntValue_(theDict's objectForKey_("maxPoints"))
			
			-- setting popupButtons (FE):
			pbMyChoice's selectItemAtIndex_(theDict's objectForKey_("myChoice") as integer)
			
		on error errmess
			display alert errmess
			return false
		end try
	end openOldFile


After reading in, the table view and others of the window can now saved normally.

Shane, thanks for your untiring help.

Heiner

Hallo Heiner,

just FYI, file name and parent directory can be easier specified with Cocoa methods


set myOpenPanel to NSOpenPanel's openPanel
myOpenPanel's setCanChooseFiles_(true)
myOpenPanel's setCanChooseDirectories_(true)
myOpenPanel's setAllowsMultipleSelection_(false)
myOpenPanel's runModal -- returns a list of file URL's
		
set myURL to item 1 of myOpenPanel's URLs -- file URL with format file://localhost/Users/.
set thePath to myURL's |path| -- coercion to string path with format /Users/.
set fileName to thePath's lastPathComponent
set parentDirectory to thePath's stringByDeletingLastPathComponent

Hallo Stefan,

Thank you for the hint.
Now the code looks much more elegantly.

A slight mod of your suggested code works nicely for getting propertyListWithData working in an ASOC project.
There seems to be a genuine bug in the Lion/Applescript implementation of NSPropertyListSerialization functions.

-Thanks for the nudge in the right direction.