@@ -757,9 +757,10 @@ def write(self, file_or_filename,
757757 if method == "text" :
758758 _serialize_text (write , self ._root )
759759 else :
760- qnames , namespaces = _namespaces (self ._root , default_namespace )
760+ qnames , attr_qnames , namespaces = _namespaces (
761+ self ._root , default_namespace )
761762 serialize = _serialize [method ]
762- serialize (write , self ._root , qnames , namespaces ,
763+ serialize (write , self ._root , qnames , attr_qnames , namespaces ,
763764 short_empty_elements = short_empty_elements )
764765
765766# --------------------------------------------------------------------
@@ -820,28 +821,59 @@ def _namespaces(elem, default_namespace=None):
820821
821822 # maps qnames to *encoded* prefix:local names
822823 qnames = {None : None }
824+ # The default namespace declaration does not apply to attribute names,
825+ # so they are encoded separately: an unqualified name is left as is,
826+ # and a qualified name always gets a prefix.
827+ attr_qnames = {None : None } if default_namespace else qnames
823828
824- # maps uri:s to prefixes
829+ # maps prefixes to uri:s
825830 namespaces = {}
831+ # maps uri:s to prefixes, "" is the prefix of the default namespace
832+ prefixes = {}
833+ # maps uri:s to prefixes usable in attribute names
834+ attr_prefixes = {} if default_namespace else prefixes
826835 if default_namespace :
827- namespaces [default_namespace ] = ""
828-
829- def add_qname (qname ):
836+ namespaces ["" ] = default_namespace
837+ prefixes [default_namespace ] = ""
838+
839+ def get_prefix (uri , isattr ):
840+ # find or create the prefix for the namespace uri
841+ if isattr :
842+ prefix = attr_prefixes .get (uri )
843+ if prefix is None :
844+ # the empty prefix is of no use for an attribute name
845+ prefix = prefixes .get (uri ) or None
846+ else :
847+ prefix = prefixes .get (uri )
848+ if prefix is not None :
849+ return prefix
850+ prefix = _namespace_map .get (uri )
851+ if prefix is None or not prefix and (isattr or default_namespace ):
852+ # the empty prefix is of no use for an attribute name,
853+ # and the default namespace is used for other uri
854+ prefix = "ns%d" % len (namespaces )
855+ if prefix != "xml" :
856+ namespaces [prefix ] = uri
857+ if isattr :
858+ attr_prefixes [uri ] = prefix
859+ prefixes .setdefault (uri , prefix )
860+ return prefix
861+
862+ def add_qname (qname , isattr = False ):
830863 # calculate serialized qname representation
831864 try :
832865 if qname [:1 ] == "{" :
833866 uri , tag = qname [1 :].rsplit ("}" , 1 )
834- prefix = namespaces .get (uri )
835- if prefix is None :
836- prefix = _namespace_map .get (uri )
837- if prefix is None :
838- prefix = "ns%d" % len (namespaces )
839- if prefix != "xml" :
840- namespaces [uri ] = prefix
867+ prefix = get_prefix (uri , isattr )
841868 if prefix :
842- qnames [qname ] = "%s:%s" % (prefix , tag )
869+ if isattr :
870+ attr_qnames [qname ] = "%s:%s" % (prefix , tag )
871+ else :
872+ qnames [qname ] = "%s:%s" % (prefix , tag )
843873 else :
844874 qnames [qname ] = tag # default element
875+ elif isattr :
876+ attr_qnames [qname ] = qname
845877 else :
846878 if default_namespace :
847879 # FIXME: can this be handled in XML 1.0?
@@ -867,16 +899,16 @@ def add_qname(qname):
867899 for key , value in elem .items ():
868900 if isinstance (key , QName ):
869901 key = key .text
870- if key not in qnames :
871- add_qname (key )
902+ if key not in attr_qnames :
903+ add_qname (key , isattr = True )
872904 if isinstance (value , QName ) and value .text not in qnames :
873905 add_qname (value .text )
874906 text = elem .text
875907 if isinstance (text , QName ) and text .text not in qnames :
876908 add_qname (text .text )
877- return qnames , namespaces
909+ return qnames , attr_qnames , namespaces
878910
879- def _serialize_xml (write , elem , qnames , namespaces ,
911+ def _serialize_xml (write , elem , qnames , attr_qnames , namespaces ,
880912 short_empty_elements , ** kwargs ):
881913 tag = elem .tag
882914 text = elem .text
@@ -890,15 +922,14 @@ def _serialize_xml(write, elem, qnames, namespaces,
890922 if text :
891923 write (_escape_cdata (text ))
892924 for e in elem :
893- _serialize_xml (write , e , qnames , None ,
925+ _serialize_xml (write , e , qnames , attr_qnames , None ,
894926 short_empty_elements = short_empty_elements )
895927 else :
896928 write ("<" + tag )
897929 items = list (elem .items ())
898930 if items or namespaces :
899931 if namespaces :
900- for v , k in sorted (namespaces .items (),
901- key = lambda x : x [1 ]): # sort on prefix
932+ for k , v in sorted (namespaces .items ()): # sort on prefix
902933 if k :
903934 k = ":" + k
904935 write (" xmlns%s=\" %s\" " % (
@@ -912,13 +943,13 @@ def _serialize_xml(write, elem, qnames, namespaces,
912943 v = qnames [v .text ]
913944 else :
914945 v = _escape_attrib (v )
915- write (" %s=\" %s\" " % (qnames [k ], v ))
946+ write (" %s=\" %s\" " % (attr_qnames [k ], v ))
916947 if text or len (elem ) or not short_empty_elements :
917948 write (">" )
918949 if text :
919950 write (_escape_cdata (text ))
920951 for e in elem :
921- _serialize_xml (write , e , qnames , None ,
952+ _serialize_xml (write , e , qnames , attr_qnames , None ,
922953 short_empty_elements = short_empty_elements )
923954 write ("</" + tag + ">" )
924955 else :
@@ -933,7 +964,7 @@ def _serialize_xml(write, elem, qnames, namespaces,
933964 "img" , "input" , "isindex" , "link" , "meta" , "param" , "source" ,
934965 "track" , "wbr" , "plaintext" }
935966
936- def _serialize_html (write , elem , qnames , namespaces , ** kwargs ):
967+ def _serialize_html (write , elem , qnames , attr_qnames , namespaces , ** kwargs ):
937968 tag = elem .tag
938969 text = elem .text
939970 if tag is Comment :
@@ -946,14 +977,13 @@ def _serialize_html(write, elem, qnames, namespaces, **kwargs):
946977 if text :
947978 write (_escape_cdata (text ))
948979 for e in elem :
949- _serialize_html (write , e , qnames , None )
980+ _serialize_html (write , e , qnames , attr_qnames , None )
950981 else :
951982 write ("<" + tag )
952983 items = list (elem .items ())
953984 if items or namespaces :
954985 if namespaces :
955- for v , k in sorted (namespaces .items (),
956- key = lambda x : x [1 ]): # sort on prefix
986+ for k , v in sorted (namespaces .items ()): # sort on prefix
957987 if k :
958988 k = ":" + k
959989 write (" xmlns%s=\" %s\" " % (
@@ -963,7 +993,7 @@ def _serialize_html(write, elem, qnames, namespaces, **kwargs):
963993 for k , v in items :
964994 if isinstance (k , QName ):
965995 k = k .text
966- k = qnames [k ]
996+ k = attr_qnames [k ]
967997 if v is None :
968998 write (" %s" % k ) # empty attr
969999 else :
@@ -980,7 +1010,7 @@ def _serialize_html(write, elem, qnames, namespaces, **kwargs):
9801010 else :
9811011 write (_escape_cdata (text ))
9821012 for e in elem :
983- _serialize_html (write , e , qnames , None )
1013+ _serialize_html (write , e , qnames , attr_qnames , None )
9841014 if ltag not in HTML_EMPTY :
9851015 write ("</" + tag + ">" )
9861016 if elem .tail :
0 commit comments