XML pickle

castironpi at gmail.com castironpi at gmail.com
Sun Feb 17 17:01:56 EST 2008


> > Which xmlns:ns1 gets "redefined" because I just didn't figure out how
> > get xmlns:ns0 definition into the Workbook tag.  But too bad for me.
>
> What about actually *reading* the links I post?
>
> http://codespeak.net/lxml/tutorial.html#the-e-factory
>
> Hint: look out for the "nsmap" keyword argument.

That solved the problem.  nsmap doesn't set the root xmlns, but el.set
does.  So that cleared up the [hugevariable] gripe.

Revision separates code from data.

def Cell( p, index= None, styleid= None ):
	el= etree.SubElement( p, 'Cell' )
	if index is not None: el.set( SS+ 'Index', index )
	if styleid is not None: el.set( SS+ 'StyleID', styleid )
	return el

becomes

class Cell( XMLable ):
	ctor= XMLable.CTor( ( 'index', SS+ 'Index' ), ( 'style', SS+
'StyleID' ) )
	ftor= XMLable.FTor( { 'style': lambda x: x.styleid } )

29 lines, loosely packed, define base class; 29 define the subclasses
Workbook, Worksheet, &c., also loose.

class Data( XMLable ):
	ctor= XMLable.CTor( ( 'type', SS+ 'Type' ), ( 'data',
XMLable.Text ) )

XMLable.Text pseudo-flag indicates to call node.data= X where
node.set( tag, X ) is called.  Tag present in XMLable.ftor indicates
to call node.set( tag, ftor[tag]( X ) ).

class Font( XMLable ):
	#jtor= JTor( 'family', X+ 'Family', req='Swiss' ), JTor( 'bold', SS+
'Bold', lambda x: str( int( x ) ) )
	ctor= XMLable.CTor( ( 'family', X+ 'Family' ), ( 'bold', SS+
'Bold' ) )
	ftor= XMLable.FTor( { 'bold': lambda x: str( int( x ) ) } )

JTor combines CTor and FTor allowing for extension, separating data
into data structure, but is developing.  One could even put that spec
in XML!

Yes: in the example, the base class + derivatives comprise more code,
29 + 29 = 58 lines over the earlier 46.  Actual construction underwent
a slight adjustment, still 9 lines.  Hearing arguments on payoff and
extensibility.

Full implementation present; remove in replies.

Aside, in the C++ equivalent, each XMLable derivative has a class-
static list of JTor derivatives, "static JTor* specs[];", the
population of which is declared and initialized globally, and a
"virtual JTor* GetSpec( int i ) { return specs[ i ]; }"
implementation.  CMIIW, correct me if I'm wrong; +1 on Python.

The for-statements in XMLable.__init__ could conflate; parameter
extraction, which parameters may be specified by place or keyword, is
unclear.  Is "if ca[1] is XMLable.Text:" a special case, and if so, is
it handled correctly?  Ought JTor to contain a visit method to the
end, which calls node.set in the base class?  It leads to redundancy,
but code is code.  Thence comes the moral, 'No functions in
constructors', and "if ca[1] is XMLable.Text:" is not a callback.
Aside, what is?



from lxml import etree

class XMLable:
	cname= ''
	Text= object()
	class CTor:
		def __init__( self, *ar ):
			self.ar, self.kwar= ar, dict( ar )
	ctor= CTor()
	FTor= dict
	ftor= {}
	def __init__( self, par= None, *ar, **kwar ):
		nsmap= kwar.pop( 'nsmap', None )
		if par is None:
			self.node= etree.Element( self.cname or self.__class__.__name__,
nsmap= nsmap )
		else:
			self.node= etree.SubElement( par.node, self.cname or
self.__class__.__name__, nsmap= nsmap )
		for a, ca in zip( ar, self.ctor.ar ):
			if ca[0] in self.ftor:
				a= self.ftor[ ca[0] ]( a )
			if ca[1] is XMLable.Text:
				self.node.text= a
			else:
				self.node.set( ca[1], a )
		for k, v in kwar.items():
			if k in self.ftor:
				v= self.ftor[ k ]( v )
			if self.ctor.kwar[ k ] is XMLable.Text:
				self.node.text= v
			else:
				self.node.set( self.ctor.kwar[ k ], str( v ) )

SS= '{urn:schemas-microsoft-com:office:spreadsheet}'
X= '{urn:schemas-microsoft-com:office:excel}'

class Workbook( XMLable ):
	#jtor= JTor( 'xmlns', req= 'urn:schemas-microsoft-
com:office:spreadsheet' )
	def __init__( self ):
		nns= { 'x': 'urn:schemas-microsoft-com:office:excel',
			'ss': 'urn:schemas-microsoft-com:office:spreadsheet' }
		XMLable.__init__( self, nsmap= nns )
		self.node.set( 'xmlns', 'urn:schemas-microsoft-
com:office:spreadsheet' )
		self.styles= Styles( self )
class Worksheet( XMLable ):
	ctor= XMLable.CTor( ( 'name', SS+ 'Name' ) )
class Table( XMLable ): pass
class Row( XMLable ):
	ctor= XMLable.CTor( ( 'index', SS+ 'Index' ) )
class Cell( XMLable ):
	ctor= XMLable.CTor( ( 'index', SS+ 'Index' ), ( 'style', SS+
'StyleID' ) )
	ftor= XMLable.FTor( { 'style': lambda x: x.styleid } )
class Data( XMLable ):
	ctor= XMLable.CTor( ( 'type', SS+ 'Type' ), ( 'data',
XMLable.Text ) )
class Styles( XMLable ): pass
class Font( XMLable ):
	#jtor= JTor( 'family', X+ 'Family', req='Swiss' ), Jtor( 'bold', SS+
'Bold', lambda x: str( int( x ) ) )
	ctor= XMLable.CTor( ( 'family', X+ 'Family' ), ( 'bold', SS+
'Bold' ) )
	ftor= XMLable.FTor( { 'bold': lambda x: str( int( x ) ) } )
class Style( XMLable ):
	styles= {}
	ctor= XMLable.CTor( ( 'styleid', SS+ 'ID' ) )
	def __init__( self, par= None, *ar, **kwar ):
		self.styleid= 's%i'% ( 21+ len( Style.styles ) )
		Style.styles[ self.styleid ]= self
		XMLable.__init__( self, par.styles, self.styleid )
		Font( self, *ar, **kwar )

book= Workbook()
sheet= Worksheet( book, 'WSheet1' )
table= Table( sheet )
row= Row( table, index= '2' )
style= Style( book, 'Swiss', True )
celli= Cell( row, style= style )
datai= Data( celli, 'Number', '123' )
cellj= Cell( row, index= 3 )
dataj= Data( cellj, 'String', 'abc' )

out= etree.tostring( book.node, pretty_print= True,
xml_declaration=True )
print( out )
open( 'xl.xml', 'w' ).write( out )
new= etree.XML( out )
etree.XML( etree.tostring( book.node ) )
out= etree.tostring( new, pretty_print= True, xml_declaration=True )
print( out )







More information about the Python-list mailing list