Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Doc/library/xml.dom.minidom.rst
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ module documentation. This section lists the differences between the API and
... # Work with dom.


.. method:: Node.writexml(writer, indent="", addindent="", newl="")
.. method:: Node.writexml(writer, indent="", addindent="", newl="", sort=False)

Write XML to the writer object. The writer should have a :meth:`write` method
which matches that of the file object interface. The *indent* parameter is the
Expand All @@ -147,6 +147,11 @@ module documentation. This section lists the differences between the API and
The :meth:`writexml` method now preserves the attribute order specified
by the user.

.. versionadded:: 3.8
The *sort* parameter sorts the XML attributes of the tags. For the
:class:`Document` node, an additional keyword argument *sort* can be used
to specify the sort of the attributes.

.. method:: Node.toxml(encoding=None)

Return a string or byte string containing the XML represented by
Expand Down
17 changes: 17 additions & 0 deletions Doc/library/xml.dom.rst
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,23 @@ inherits properties from :class:`Node`.
particular namespace URI and localname. The localname is the part of the
namespace after the prefix.

.. method:: Document.writexml(self, writer, indent="", addindent="", newl="", encoding=None, sort=False)

Write XML to the writer object. The writer should have a :meth:`write` method
which matches that of the file object interface. The *indent* parameter is the
indentation of the current node. The *addindent* parameter is the incremental
indentation to use for subnodes of the current one. The *newl* parameter
specifies the string to use to terminate newlines.

The keyword argument *encoding* can be used to specify the encoding field of
the XML header.

.. versionchanged:: 3.8
The :meth:`writexml` method now preserves the attribute order specified
by the user.

.. versionadded:: 3.8
The *sort* parameter sorts the XML attributes of the tags.

.. _dom-element-objects:

Expand Down
8 changes: 8 additions & 0 deletions Lib/test/test_minidom.py
Original file line number Diff line number Diff line change
Expand Up @@ -1612,6 +1612,14 @@ def testProcessingInstructionNameError(self):
pi = doc.createProcessingInstruction("y", "z")
pi.nodeValue = "crash"

def test_minidom_attribute_sorted(self):
xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>'
sorted_xml_str = '<?xml version="1.0" ?><curriculum company="example" status="public"/>'
doc = parseString(xml_str)
output = io.StringIO()
doc.writexml(output, sort=True)
self.assertEqual(output.getvalue(), sorted_xml_str)

def test_minidom_attribute_order(self):
xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>'
doc = parseString(xml_str)
Expand Down
30 changes: 17 additions & 13 deletions Lib/xml/dom/minidom.py
Original file line number Diff line number Diff line change
Expand Up @@ -847,27 +847,31 @@ def getElementsByTagNameNS(self, namespaceURI, localName):
def __repr__(self):
return "<DOM Element: %s at %#x>" % (self.tagName, id(self))

def writexml(self, writer, indent="", addindent="", newl=""):
def writexml(self, writer, indent="", addindent="", newl="", sort=False):
# indent = current indentation
# addindent = indentation to add to higher levels
# newl = newline string
writer.write(indent+"<" + self.tagName)

attrs = self._get_attributes()

for a_name in attrs.keys():
attribute_names = attrs.keys()
if sort:
attribute_names = sorted(attribute_names)

for a_name in attribute_names:
writer.write(" %s=\"" % a_name)
_write_data(writer, attrs[a_name].value)
writer.write("\"")
if self.childNodes:
writer.write(">")
if (len(self.childNodes) == 1 and
self.childNodes[0].nodeType == Node.TEXT_NODE):
self.childNodes[0].writexml(writer, '', '', '')
self.childNodes[0].writexml(writer, '', '', '', sort=sort)
else:
writer.write(newl)
for node in self.childNodes:
node.writexml(writer, indent+addindent, addindent, newl)
node.writexml(writer, indent+addindent, addindent, newl, sort=sort)
writer.write(indent)
writer.write("</%s>%s" % (self.tagName, newl))
else:
Expand Down Expand Up @@ -984,8 +988,8 @@ def _set_nodeName(self, value):
self.target = value
nodeName = property(_get_nodeName, _set_nodeName)

def writexml(self, writer, indent="", addindent="", newl=""):
writer.write("%s<?%s %s?>%s" % (indent,self.target, self.data, newl))
def writexml(self, writer, indent="", addindent="", newl="", sort=False):
writer.write("%s<?%s %s?>%s" % (indent,self.target, self.data, newl), sort=sort)


class CharacterData(Childless, Node):
Expand Down Expand Up @@ -1084,7 +1088,7 @@ def splitText(self, offset):
self.data = self.data[:offset]
return newText

def writexml(self, writer, indent="", addindent="", newl=""):
def writexml(self, writer, indent="", addindent="", newl="", sort=False):
_write_data(writer, "%s%s%s" % (indent, self.data, newl))

# DOM Level 3 (WD 9 April 2002)
Expand Down Expand Up @@ -1179,10 +1183,10 @@ def __init__(self, data):
CharacterData.__init__(self)
self._data = data

def writexml(self, writer, indent="", addindent="", newl=""):
def writexml(self, writer, indent="", addindent="", newl="", sort=False):
if "--" in self.data:
raise ValueError("'--' is not allowed in a comment node")
writer.write("%s<!--%s-->%s" % (indent, self.data, newl))
writer.write("%s<!--%s-->%s" % (indent, self.data, newl), sort=sort)


class CDATASection(Text):
Expand All @@ -1191,7 +1195,7 @@ class CDATASection(Text):
nodeType = Node.CDATA_SECTION_NODE
nodeName = "#cdata-section"

def writexml(self, writer, indent="", addindent="", newl=""):
def writexml(self, writer, indent="", addindent="", newl="", sort=False):
if self.data.find("]]>") >= 0:
raise ValueError("']]>' not allowed in a CDATA section")
writer.write("<![CDATA[%s]]>" % self.data)
Expand Down Expand Up @@ -1324,7 +1328,7 @@ def cloneNode(self, deep):
else:
return None

def writexml(self, writer, indent="", addindent="", newl=""):
def writexml(self, writer, indent="", addindent="", newl="", sort=False):
writer.write("<!DOCTYPE ")
writer.write(self.name)
if self.publicId:
Expand Down Expand Up @@ -1786,14 +1790,14 @@ def importNode(self, node, deep):
raise xml.dom.NotSupportedErr("cannot import document type nodes")
return _clone_node(node, deep, self)

def writexml(self, writer, indent="", addindent="", newl="", encoding=None):
def writexml(self, writer, indent="", addindent="", newl="", encoding=None, sort=False):
if encoding is None:
writer.write('<?xml version="1.0" ?>'+newl)
else:
writer.write('<?xml version="1.0" encoding="%s"?>%s' % (
encoding, newl))
for node in self.childNodes:
node.writexml(writer, indent, addindent, newl)
node.writexml(writer, indent, addindent, newl, sort=sort)

# DOM Level 3 (WD 9 April 2002)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Add the *sort* attribute to the :meth:`xml.dom.minidom.Node.writexml` method
allowing to sort the attributes by name (``True``) or by the attribute order
specified by the user (by default ``False``). Document
:meth:`xml.dom.minidom.Document.writexml` method. Contributed by Stéphane
Wirtel.