I have a YAML file containing the following data:
%YAML 1.1
%TAG !u! tag:something.com,2019:
--- !u!1337 &1
MainKey:
B: 0
Z: 2
C:
- a: 0
e: 0
c: 0
- a: 0
e: 0
c: 0
To work with this YAML, I created a constructor, and a representer. After loading, then dumping the file, I obtain the following result:
!<tag:something.com,2019:1337>
MainKey:
B: 0
C:
- a: 0
c: 0
e: 0
- a: 1
c: 1
e: 1
Z: 2
I have two questions:
- First: How can I keep the header, and constructor tag identical to the original version ?
- Second: How can I make sure the keys are dumped in the same order, instead of being sorted ?
Here is the code I am working for this example, so it can be reproduced:
import copy
import yaml
from io import StringIO
from typing import Dict, Type, TextIO
class Foo:
tag = "tag:something.com,2019:1337"
def __init__(self, obj: Dict) -> None:
self.obj = copy.deepcopy(obj)
def __eq__(self, other: 'Foo'):
return self.obj == other.obj
@staticmethod
def loader(yaml_loader: yaml.SafeLoader, node: yaml.nodes.MappingNode) -> 'Foo':
decoded_dictionary = yaml_loader.construct_mapping(node, deep=True)
return Foo(decoded_dictionary)
@staticmethod
def representer(dumper: yaml.SafeDumper, foo: 'Foo') -> yaml.nodes.MappingNode:
return dumper.represent_mapping(Foo.tag, foo.obj)
YAML_FILE = """%YAML 1.1
%TAG !u! tag:something.com,2019:
--- !u!1337 &1
MainKey:
B: 0
Z: 2
C:
- a: 0
e: 0
c: 0
- a: 0
e: 0
c: 0
"""
EXPECTED_FOO = Foo({'MainKey': {'B': 0, 'Z': 2, 'C': [{'a': 0, 'e': 0, 'c': 0}, {'a': 0, 'e': 0, 'c': 0}]}})
def get_loader() -> Type[yaml.SafeLoader]:
yaml_loader = yaml.SafeLoader
yaml_loader.add_constructor(Foo.tag, Foo.loader)
return yaml_loader
def get_dumper() -> Type[yaml.SafeDumper]:
dumper = yaml.SafeDumper
dumper.sort_keys = False
dumper.add_representer(Foo, Foo.representer)
return dumper
def load(file_pointer: TextIO) -> Foo:
return yaml.load(file_pointer, Loader=get_loader())
def dump(foo: Foo, file_pointer: TextIO) -> None:
yaml.dump(foo, file_pointer, Dumper=get_dumper())
def test_can_load():
with StringIO(YAML_FILE) as file_pointer:
result = load(file_pointer)
assert result == EXPECTED_FOO
def test_can_dump():
with StringIO() as file_pointer:
dump(EXPECTED_FOO, file_pointer)
file_pointer.seek(0)
assert file_pointer.read() == YAML_FILE
if __name__ == "__main__":
test_can_load()
test_can_dump()