Can PyYAML dump dict items in non-alphabetical order? Can PyYAML dump dict items in non-alphabetical order? python python

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)