Can PyYAML dump dict items in non-alphabetical order?
If you upgrade PyYAML to 5.1 version, now, it supports dump without sorting the keys like this:
yaml.dump(data, sort_keys=False)
As shown in help(yaml.Dumper)
, sort_keys
defaults to True
:
Dumper(stream, default_style=None, default_flow_style=False, canonical=None, indent=None, width=None, allow_unicode=None, line_break=None, encoding=None, explicit_start=None, explicit_end=None, version=None, tags=None, sort_keys=True)
(These are passed as kwargs to yaml.dump
)
There's probably a better workaround, but I couldn't find anything in the documentation or the source.
Python 2 (see comments)
I subclassed OrderedDict
and made it return a list of unsortable items:
from collections import OrderedDictclass UnsortableList(list): def sort(self, *args, **kwargs): passclass UnsortableOrderedDict(OrderedDict): def items(self, *args, **kwargs): return UnsortableList(OrderedDict.items(self, *args, **kwargs))yaml.add_representer(UnsortableOrderedDict, yaml.representer.SafeRepresenter.represent_dict)
And it seems to work:
>>> d = UnsortableOrderedDict([... ('z', 0),... ('y', 0),... ('x', 0)... ])>>> yaml.dump(d, default_flow_style=False)'z: 0\ny: 0\nx: 0\n'
Python 3 or 2 (see comments)
You can also write a custom representer, but I don't know if you'll run into problems later on, as I stripped out some style checking code from it:
import yamlfrom collections import OrderedDictdef represent_ordereddict(dumper, data): value = [] for item_key, item_value in data.items(): node_key = dumper.represent_data(item_key) node_value = dumper.represent_data(item_value) value.append((node_key, node_value)) return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', value)yaml.add_representer(OrderedDict, represent_ordereddict)
But with that, you can use the native OrderedDict
class.
One-liner to rule them all:
yaml.add_representer(dict, lambda self, data: yaml.representer.SafeRepresenter.represent_dict(self, data.items()))
That's it. Finally. After all those years and hours, the mighty represent_dict
has been defeated by giving it the dict.items()
instead of just dict
Here is how it works:
This is the relevant PyYaml source code:
if hasattr(mapping, 'items'): mapping = list(mapping.items()) try: mapping = sorted(mapping) except TypeError: pass for item_key, item_value in mapping:
To prevent the sorting we just need some Iterable[Pair]
object that does not have .items()
.
dict_items
is a perfect candidate for this.
Here is how to do this without affecting the global state of the yaml module:
#Using a custom Dumper class to prevent changing the global stateclass CustomDumper(yaml.Dumper): #Super neat hack to preserve the mapping key order. See https://stackoverflow.com/a/52621703/1497385 def represent_dict_preserve_order(self, data): return self.represent_dict(data.items()) CustomDumper.add_representer(dict, CustomDumper.represent_dict_preserve_order)return yaml.dump(component_dict, Dumper=CustomDumper)