Converting XML plists to the old NeXTSTEP (ASCII) format

General discussion for players of Oolite.

Moderators: another_commander, winston

Cdr. Jettison
Average
Average
Posts: 15
Joined: Wed Jun 01, 2011 2:26 pm

Converting XML plists to the old NeXTSTEP (ASCII) format

Post 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')
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

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

Post 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)
User avatar
Commander McLane
---- E L I T E ----
---- E L I T E ----
Posts: 9520
Joined: Thu Dec 14, 2006 9:08 am
Location: a Hacker Outpost in a moderately remote area
Contact:

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

Post 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:
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

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

Post 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.
Cdr. Jettison
Average
Average
Posts: 15
Joined: Wed Jun 01, 2011 2:26 pm

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

Post 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.
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

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

Post 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.
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

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

Post 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.
Last edited by Eric Walch on Mon Aug 15, 2011 7:42 am, edited 4 times in total.
User avatar
Capt. Murphy
Commodore
Commodore
Posts: 1127
Joined: Fri Feb 25, 2011 8:46 am
Location: UK South Coast.

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

Post 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.
[EliteWiki] Capt. Murphy's OXPs
External JavaScript resources - W3Schools & Mozilla Developer Network
Win 7 64bit, Intel Core i5 with HD3000 (driver rev. 8.15.10.2696 - March 2012), Oolite 1.76.1
User avatar
Diziet Sma
---- E L I T E ----
---- E L I T E ----
Posts: 6310
Joined: Mon Apr 06, 2009 12:20 pm
Location: Aboard the Pitviper S.E. "Blackwidow"

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

Post 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.
Most games have some sort of paddling-pool-and-water-wings beginning to ease you in: Oolite takes the rather more Darwinian approach of heaving you straight into the ocean, often with a brick or two in your pockets for luck. ~ Disembodied
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

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

Post 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?
User avatar
Diziet Sma
---- E L I T E ----
---- E L I T E ----
Posts: 6310
Joined: Mon Apr 06, 2009 12:20 pm
Location: Aboard the Pitviper S.E. "Blackwidow"

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

Post 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:
Most games have some sort of paddling-pool-and-water-wings beginning to ease you in: Oolite takes the rather more Darwinian approach of heaving you straight into the ocean, often with a brick or two in your pockets for luck. ~ Disembodied
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

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

Post 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. :?
User avatar
Svengali
Commander
Commander
Posts: 2370
Joined: Sat Oct 20, 2007 2:52 pm

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

Post 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.
User avatar
Diziet Sma
---- E L I T E ----
---- E L I T E ----
Posts: 6310
Joined: Mon Apr 06, 2009 12:20 pm
Location: Aboard the Pitviper S.E. "Blackwidow"

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

Post 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!
Most games have some sort of paddling-pool-and-water-wings beginning to ease you in: Oolite takes the rather more Darwinian approach of heaving you straight into the ocean, often with a brick or two in your pockets for luck. ~ Disembodied
User avatar
Svengali
Commander
Commander
Posts: 2370
Joined: Sat Oct 20, 2007 2:52 pm

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

Post 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:]))
Post Reply