This can be done, relatively easily, and without changing the input file.
Since the
dict
PyYAML uses is hard-coded and cannot be patched, you not only have to provide
a dict-like class that behaves as you want, you also have to go through the hoops to make
PyYAML use that class. I.e. change the SafeConstructor
that would normally construct a dict
to use that new class, incorporate that in a new Loader and use PyYAML's load
to use that Loader:
import sys
import yaml
from yaml.loader import Reader, Scanner, Parser, Composer, SafeConstructor, Resolver
class MyDict(dict):
def __getattr__(self, name):
return self[name]
class MySafeConstructor(SafeConstructor):
def construct_yaml_map(self, node):
data = MyDict()
yield data
value = self.construct_mapping(node)
data.update(value)
MySafeConstructor.add_constructor(
u'tag:yaml.org,2002:map', MySafeConstructor.construct_yaml_map)
class MySafeLoader(Reader, Scanner, Parser, Composer, MySafeConstructor, Resolver):
def __init__(self, stream):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
MySafeConstructor.__init__(self)
Resolver.__init__(self)
yaml_str = """\
a: 1
b:
- q: "foo"
r: 99
s: 98
- x: "bar"
y: 97
z: 96
c:
d: 7
e: 8
f: [9,10,11]
"""
mydict = yaml.load(yaml_str, Loader=MySafeLoader)
print(mydict.b[0].r)
which gives:
99
If you need to be able to handle YAML1.2 you should use ruamel.yaml
(disclaimer: I am the author of that package) which makes the above slightly simpler
import ruamel.yaml
# same definitions for yaml_str, MyDict
class MySafeConstructor(ruamel.yaml.constructor.SafeConstructor):
def construct_yaml_map(self, node):
data = MyDict()
yield data
value = self.construct_mapping(node)
data.update(value)
MySafeConstructor.add_constructor(
u'tag:yaml.org,2002:map', MySafeConstructor.construct_yaml_map)
yaml = ruamel.yaml.YAML(typ='safe')
yaml.Constructor = MySafeConstructor
mydict = yaml.load(yaml_str)
print(mydict.b[0].r)
which also gives:
99
(and if your real input is large, should load your data noticably faster)