Page 1 of 3

Converting XML plists to the old NeXTSTEP (ASCII) format

Posted: Wed Jun 08, 2011 1:37 pm
by Cdr. Jettison
I was looking for a tool that makes said conversion, but found nothing and ended up writing this quick&dirty python script:

Code: Select all

#!/usr/bin/python
# Convert plist from XML to NextStep format
# 'with' and 'str.format()' require python 2.6

from plistlib import readPlist

filename = 'shipyard.plist'
newsuffix = '-ns'

def writeDict(fs, dct, lvl):
	fs.write('{\n')
	for k, v in sorted(dct.iteritems()):
		fs.write((lvl+1)*'\t'+'"{key}" = '.format(key=k))
		if isinstance(v, list):
			writeList(fs, v, lvl+1)
		elif isinstance(v, dict):
			writeDict(fs, v, lvl+1)
		else:
			q = '"' if isinstance(v, str) else ''
			fs.write('{quote}{val}{quote}'.format(quote=q,
				val=v if not isinstance(v, bool) else 'yes' if v else 'no'))
		fs.write(';\n')
	fs.write(lvl*'\t'+'}')

def writeList(fs, lst, lvl):
	fs.write('(\n')
	for i in lst:
		fs.write((lvl+1)*'\t')
		if isinstance(i, list):
			writeList(fs, i, lvl+1)
		elif isinstance(i, dict):
			writeDict(fs, i, lvl+1)
		else:
			q = '"' if isinstance(i, str) else ''
			fs.write('{quote}{item}{quote}'.format(quote=q, item=i))
		fs.write(',\n')
	fs.write(lvl*'\t'+')')

pl = readPlist(filename)
level = 0
with open(filename + newsuffix, 'w') as f:
	if isinstance(pl, list):
		writeList(f, pl, level)
	elif isinstance(pl, dict):
		writeDict(f, pl, level)
	else:
		f.write('// Parsing error!')
	f.write('\n')

Re: Converting XML plists to the old NeXTSTEP (ASCII) format

Posted: Fri Jun 10, 2011 2:55 pm
by Eric Walch
Cdr. Jettison wrote:
I was looking for a tool that makes said conversion, but found nothing and ended up writing this quick&dirty python script:
Nice code. But you better provide it as a downloadable file and not one you copy from the browser. By copying you pick up all kind of hidden characters that break the code. After removing some hidden characters I got the next result:

Code: Select all

iMac-DDR2:~ ericwalch$ cd '/Users/ericwalch/Desktop/' && '/usr/bin/pythonw'  '/Users/ericwalch/Desktop/XML->openstep.py'  && echo Exit status: $? && exit 1
  File "/Users/ericwalch/Desktop/XML->openstep.py", line 14
    if isinstance(v, list):
                          ^
IndentationError: unindent does not match any outer indentation level
iMac-DDR2:Desktop ericwalch$ 
After removing the leading spaces and adding it back with a new indentation, I finally got a converted shipyard.plist:

Code: Select all

{
	"clix-player" = 	"cobraclipper-player" = {
		"chance" = 		"optional_equipment" = 		"price" = 		"standard_equipment" = 		"techlevel" = 		"weapon_facings" = 1;
	};
}
A fully crippled file. But this still could be caused by some hidden characters that copied with it and not because of errors in the code, as I am sure it is working for you. (I used python 2.6.1)

Re: Converting XML plists to the old NeXTSTEP (ASCII) format

Posted: Fri Jun 10, 2011 6:41 pm
by Commander McLane
When copying code from the BB it's always better to hit the "quote" button and copy from the typing window. Just don't press the "Submit" button after you're finished. :wink:

Re: Converting XML plists to the old NeXTSTEP (ASCII) format

Posted: Fri Jun 10, 2011 6:58 pm
by Eric Walch
Commander McLane wrote:
When copying code from the BB it's always better to hit the "quote" button and copy from the typing window. Just don't press the "Submit" button after you're finished. :wink:
I should have figure this out myself because I had this problem regularly with plist files presented this way. :?

Anyhow, copying it with your method and pasting it in a textfile ending on '.py' fixed it. Just opening that file resulted in running the python script and now converting the shipyard.plist correctly. I know that I have easier tools on the mac, but just wanted to check this script. For window/linux users without good plist editors it is a very useful script. Good work Cdr. Jettison. This should probably get added to the wiki somewhere.

One remark. This converted script is readable for all systems except old mac systems that are still running on OSX10.4 (tiger).
Tiger threats the comma as terminator as an error:

Code: Select all

			"extras" = (
				"EQ_ADVANCED_COMPASS",
				"EQ_ECM",
				"EQ_ENERGY_UNIT",
				"EQ_FUEL_SCOOPS",
				"EQ_HEAT_SHIELD",  // <------
			);
it wants it only as seperator. Meaning the last should be skipped.

Code: Select all

			"extras" = (
				"EQ_ADVANCED_COMPASS",
				"EQ_ECM",
				"EQ_ENERGY_UNIT",
				"EQ_FUEL_SCOOPS",
				"EQ_HEAT_SHIELD"  // <------
			);
All other systems accept the second solution without comma at the end.

Re: Converting XML plists to the old NeXTSTEP (ASCII) format

Posted: Sat Jun 11, 2011 7:54 pm
by Cdr. Jettison
Well, Eric, in your own words
Eric Walch wrote:
I know that I have easier tools on the mac
So, Mac users probably don't need this script at all. Anyway, I changed the script so that it produces tiger-friendly results and uploaded it to box.net. Also added UTF-8 encoding. There are plists that make use of some non-ASCII characters like quotation marks, em dash or Cruseiro sign.

Re: Converting XML plists to the old NeXTSTEP (ASCII) format

Posted: Mon Jun 13, 2011 12:09 pm
by Eric Walch
Just tested your revised version. Works great and the resulting plist will now also work for the few old Mac-tiger users. And now lines with special characters like "<string>My ship & co</string>" is correctly converted to "My ship & co"

Script should definitely go somewhere on the wiki.

Re: Converting XML plists to the old NeXTSTEP (ASCII) format

Posted: Thu Aug 04, 2011 12:57 pm
by Eric Walch
I almost forgot this. The script provided by Cdr. Jettison works now great on mac systems also. I have not yet uploaded it, but will add it below:

Code: Select all

#!/usr/bin/python
'''Convert plist from XML to OpenStep format'''
# Uses features available starting with python 2.6:
#	* 'with' statement (introduced in 2.5)
#	* String formatting with 'str.format()' method
#	* plistlib module from Standard Library
#		(this module was MacOS-specific before 2.6)
#       * by Cdr. Jettison

import sys
from plistlib import readPlist

# Change this as needed.
# plist file name can also be passed as parameter on command line
filename = 'shipyard.plist' # name of plist file to be converted
newsuffix = '-ns' # added to 'filename' to produce output file name

def writeDict(fs, dct, lvl):
	fs.write('{\n')
	for k, v in sorted(dct.iteritems()):
		fs.write((lvl+1)*'\t'+'"{key}" = '.format(key=k))
		if isinstance(v, list):
			writeList(fs, v, lvl+1)
		elif isinstance(v, dict):
			writeDict(fs, v, lvl+1)
		else:
			q = '"' if isinstance(v, basestring) else ''
			fs.write(u'{quote}{val}{quote}'.format(
				quote=q,
				val=v if not isinstance(v, bool) else 'yes' if v else 'no'
				).encode('utf_8')
			)
		fs.write(';\n')
	fs.write(lvl*'\t'+'}')

def writeList(fs, lst, lvl):
	fs.write('(\n')
	for i, e in enumerate(lst):
		fs.write((lvl+1)*'\t')
		if isinstance(e, list):
			writeList(fs, e, lvl+1)
		elif isinstance(e, dict):
			writeDict(fs, e, lvl+1)
		else:
			q = '"' if isinstance(e, basestring) else ''
			fs.write(u'{quote}{elem}{quote}'.format(
				quote=q,
				elem=e
				).encode('utf_8')
			)
		fs.write(',\n') if i != len(lst)-1 else fs.write('\n')
	fs.write(lvl*'\t'+')')

def main(args):
	global filename

	if len(args) != 0:
		filename=args[0] # Get plist file name from command line
	
	pl = readPlist(filename)
	level = 0
	with open(filename + newsuffix, 'w') as f:
		if isinstance(pl, list):
			writeList(f, pl, level)
		elif isinstance(pl, dict):
			writeDict(f, pl, level)
		else:
			f.write('// Parsing error!')
		f.write('\n')

if __name__ == '__main__':
	sys.exit(main(sys.argv[1:]))
Edit: I now added a download link on the wiki [wiki]Property_list[/wiki] page. It is the same link provided by Cdr Jettison, two messages back.

Re: Converting XML plists to the old NeXTSTEP (ASCII) format

Posted: Mon Aug 15, 2011 5:42 am
by Capt. Murphy
Thanks to Cmdr. Jettison and Eric for a great conversion tool. XML makes by eyes and brain hurt and this was just what the doctor ordered.

Re: Converting XML plists to the old NeXTSTEP (ASCII) format

Posted: Fri Jan 25, 2013 8:55 pm
by Diziet Sma
I guess the downloadable copy of the script on the Wiki hasn't been getting a lot of use by Linux users, because it turns out it's full of CR (carriage return) characters, which cause bad interpreter: No such file or directory errors when you try to run it on Linux. I've uploaded a version with the CRs stripped out, and posted a link to it on the Wiki [EliteWiki] Property list page.

Re: Converting XML plists to the old NeXTSTEP (ASCII) format

Posted: Fri Jan 25, 2013 9:31 pm
by Eric Walch
Thanks for making it 'multiple platform'. The script is very short and actually does not do the conversion itself. The conversion is just a call to a library inside python. That makes me wonder if there are not more user friendly programs written for python, doing the conversion?

Re: Converting XML plists to the old NeXTSTEP (ASCII) format

Posted: Fri Jan 25, 2013 9:46 pm
by Diziet Sma
Eric Walch wrote:
That makes me wonder if there are not more user friendly programs written for python, doing the conversion?
What's not user-friendly about entering ./xml2ns.py <filename> into a console? :lol:

Re: Converting XML plists to the old NeXTSTEP (ASCII) format

Posted: Fri Jan 25, 2013 10:20 pm
by Eric Walch
It is clear that I never really used it. Because the name "shipyard.plist" was written out as filename, I thought it only works for that file and you had to change the script for other plists. Apparently it is only the default name. :?

But more user friendly would be to just drop the plist on the script. It works that way for the "DropObj2DatTex" converters on the mac. No need there to run python to do the conversion. :?

Re: Converting XML plists to the old NeXTSTEP (ASCII) format

Posted: Sat Jan 26, 2013 10:50 am
by Svengali
Dropping a file works under Windows and places the new file in the same folder as the original one.

btw: I'm using a tweaked version which doesn't use quotes for the inner keys (except if a minus is used like in escort-ship). Together with a custom syntax highlighting in NPP for specific plists you'll get a nice overview.

Re: Converting XML plists to the old NeXTSTEP (ASCII) format

Posted: Sat Jan 26, 2013 11:08 am
by Diziet Sma
Svengali wrote:
btw: I'm using a tweaked version which doesn't use quotes for the inner keys (except if a minus is used like in escort-ship). Together with a custom syntax highlighting in NPP for specific plists you'll get a nice overview.
So.. share them, please!

Re: Converting XML plists to the old NeXTSTEP (ASCII) format

Posted: Sat Jan 26, 2013 12:52 pm
by Svengali

Code: Select all

#!/usr/bin/python
'''Convert plist from XML to OpenStep format'''
# Uses features available starting with python 2.6:
#	* 'with' statement (introduced in 2.5)
#	* String formatting with 'str.format()' method
#	* plistlib module from Standard Library
#		(this module was MacOS-specific before 2.6)

import sys
import re
from plistlib import readPlist

# Change this as needed.
# plist file name can also be passed as parameter on command line
filename = 'shipyard.plist' # name of plist file to be converted
newsuffix = '-ns' # added to 'filename' to produce output file name

def writeDict(fs, dct, lvl):
	fs.write('{\n')
	for k, v in sorted(dct.iteritems()):
		check = re.compile(r"[-+\.]")
		pattern = check.search(k)
		if lvl > 0:
			if pattern:
				fs.write((lvl+1)*'\t'+'"{key}" = '.format(key=k))
			else:
				fs.write((lvl+1)*'\t'+'{key} = '.format(key=k))
		else:
			fs.write((lvl+1)*'\t'+'"{key}" = '.format(key=k))
		if isinstance(v, list):
			writeList(fs, v, lvl+1)
		elif isinstance(v, dict):
			writeDict(fs, v, lvl+1)
		else:
			q = '"' if isinstance(v, basestring) else ''
			fs.write(u'{quote}{val}{quote}'.format(
				quote=q,
				val=v if not isinstance(v, bool) else 'yes' if v else 'no'
				).encode('utf_8')
			)
		fs.write(';\n')
	fs.write(lvl*'\t'+'}')

def writeList(fs, lst, lvl):
	fs.write('(\n')
	for i, e in enumerate(lst):
		fs.write((lvl+1)*'\t')
		if isinstance(e, list):
			writeList(fs, e, lvl+1)
		elif isinstance(e, dict):
			writeDict(fs, e, lvl+1)
		else:
			q = '"' if isinstance(e, basestring) else ''
			fs.write(u'{quote}{elem}{quote}'.format(
				quote=q,
				elem=e
				).encode('utf_8')
			)
		fs.write(',\n') if i != len(lst)-1 else fs.write('\n')
	fs.write(lvl*'\t'+')')

def main(args):

# Get plist file name from 
	if len(args) != 0:
		filename=args[0]
	
	pl = readPlist(filename)
	level = 0
	with open(filename + newsuffix, 'w') as f:
		if isinstance(pl, list):
			writeList(f, pl, level)
		elif isinstance(pl, dict):
			writeDict(f, pl, level)
		else:
			f.write('// Parsing error!')
		f.write('\n')

if __name__ == '__main__':
	sys.exit(main(sys.argv[1:]))