0

Fair warning: I know absolutely no Objective-C. I am using the Foundation module for PyObjC.

I have a dictionary which I use to create an NSDictionary. An example:

from Foundation import NSDictionary
d = {'a': 1, 'b': 2, 'c': 3}
nsd = NSDictionary(d)

I know that I can write the contents of nsd to a file with nsd.writeToFile_atomically_(), but I can't figure out how to just get a string which contains all the plist-y XML. You might reckon that I could use a StringIO object, but:

>>> import StringIO
>>> s = StringIO.StringIO()
>>> nsd.writeToFile_atomically_(s,True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: NSInvalidArgumentException - Class OC_PythonObject: no such selector: getFileSystemRepresentation:maxLength:

I can't make heads or tails of the help pages, and I've tried searching SO and the web at large and people seem more interested in doing the converse of what I'm trying to do.

What I would like is to be able to do something like this:

plst = nsd.asPlistyString_()

Or maybe I have to use an NSSString, I wouldn't know:

plst = NSString.makePlistyStringFromDictionary_(nsd)

Either way, plst would be something like "<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC" yada yada. Can someone point me in the right direction?

Edit: I am aware of plistlib but I'm trying to do this directly with Foundation to help my my code future-compatible. (plistlib is pretty nice, though.)

2rs2ts
  • 10,662
  • 10
  • 51
  • 95

2 Answers2

3

While this doesn't directly answer your question, you could do this much more easily with the standard library module plistlib:

import plistlib
d = {'a': 1, 'b': 2, 'c': 3}
plist_string = plistlib.writePlistToString(d)

Result:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>a</key>
    <integer>1</integer>
    <key>b</key>
    <integer>2</integer>
    <key>c</key>
    <integer>3</integer>
</dict>
</plist>
omz
  • 53,243
  • 5
  • 129
  • 141
  • I should have mentioned that I looked at this too, but I'd prefer using the Foundation libraries for future-compatibility (they update along with the OS) – 2rs2ts Jul 13 '13 at 05:28
  • 1
    The xml plist format hasn't changed since the first version of OS X (and it's very unlikely that it'll ever change in an incompatible way). Besides, the Python standard library is also updated along with the OS... – omz Jul 13 '13 at 14:21
  • I'd also go with plistlib when you only use PyObjC for creating the plist. – Ronald Oussoren Jul 15 '13 at 05:49
  • @omz When I was trying to open plists with `plistlib` earlier I ran into errors around formatting. It's ok, I've got it working anyway. – 2rs2ts Jul 15 '13 at 17:37
  • Opening plist files is a different matter, `plistlib` doesn't support ASCII or binary plists iirc. – omz Jul 16 '13 at 00:45
1

Something like this should work for you. You basically need to use NSPropertyListSerialization to serialize the dictionary as xml.

from Foundation import NSDictionary, NSString, NSPropertyListSerialization
from Foundation import NSUTF8StringEncoding, NSPropertyListXMLFormat_v1_0

d = {'a': 1, 'b': 2, 'c': 3}
nsd = NSDictionary(d)

# serialize the dictionary as XML into an NSData object
xml_plist_data, error = NSPropertyListSerialization.dataWithPropertyList_format_options_error_(nsd, NSPropertyListXMLFormat_v1_0, 0, None)

if xml_plist_data:
    # convert that data to a string
    xml_plist = NSString.alloc().initWithData_encoding_(xml_plist_data, NSUTF8StringEncoding)
else:
    # look at the error
    pass
Firoze Lafeer
  • 17,133
  • 4
  • 54
  • 48
  • Works fine until `NSString.alloc().initWithData_encoding_`. I get a warning about leaking an uninitialized `NSPlaceholderString` and then an `AttributeError: 'NSPlaceholderString' object has no attribute 'initWithData_encoding'`. I lack the understanding to interpret the meaning of this. I know next to nothing about Foundation objects. Thanks for the help so far though! – 2rs2ts Jul 13 '13 at 05:42
  • Leak is the secondary issue, caused by the AttributeError. So did you include the last underscore after 'encoding'? I just ran the above code myself and it works fine. Also, what version of pyobjc and mac sdk are you running? – Firoze Lafeer Jul 13 '13 at 17:51
  • Strange. I had copied the code before and it didn't work, but it does today. – 2rs2ts Jul 14 '13 at 21:21
  • `objc.__version__` was 2.3.2a0. I don't know how to find what version of Mac SDK I'm running - can you help me find that? – 2rs2ts Jul 14 '13 at 21:22
  • Unless you did something unusual, it should just be whatever version of OS X you are using. But from your earlier comment, it seems this code now works for you? – Firoze Lafeer Jul 14 '13 at 22:09
  • 1
    The call to NSString.initWithData_encoding_ isn't really necessary. "xml_plist_data" is an NSData object that can be converted to a bytes object (or str in python 2) using "bytes(xml_plist_data)", and if you then need a unicode string you can use the usual Python APIs for that. – Ronald Oussoren Jul 15 '13 at 05:51