bee*_*ego 6 python yaml pyyaml
我已经使用PyYAML解析器几个月了,将文件类型转换为数据管道的一部分.我发现解析器有时非常特殊,似乎今天我偶然发现了另一个奇怪的行为.我正在转换的文件包含以下部分:
off:
    yes: "Flavor text for yes"
    no: "Flavor text for no"
Run Code Online (Sandbox Code Playgroud)
我保留了字典中当前嵌套的列表,以便我可以构建一个平面文档,但保存嵌套以便稍后转换回YAML.我得到一个TypeError说法,我试图连接str和bool键入在一起.我调查并发现PyYaml实际上是将上面的部分文本转换为以下内容:
with open(filename, "r") as f:
    data = yaml.load(f.read())
print data
>> {False: {True: "Flavor text for yes", False: "Flavor text for no}}
Run Code Online (Sandbox Code Playgroud)
我做了一个快速检查,发现PyYAML是做这行yes,no,true,false,on,off.如果键不加引号,它只会执行此转换.引用的值和键将被正确传递.寻找解决方案,我发现这里记录了这种行为.
虽然对其他人来说知道引用键会停止PyYAML这样做可能会有所帮助,但我没有这个选项,因为我不是这些文件的作者,并且编写了我的代码来尽可能少地触摸数据.
是否有针对此问题的解决方法或覆盖默认转换行为的方法PyYAML?
PyYAML 符合YAML 1.1,用于解析和发射,而对于YAML 1.1,这至少部分是记录在案的行为,因此根本没有特质,而是有意识的设计.
在YAML 1.2(在2009年取代了2005年的1.1规范)中Off/On/Yes/No,除了其他变化之外,这种使用被删除了.
在ruamel.yaml(免责声明:我是该软件包的作者),这round_trip_loader是一个safe_loader,默认为YAML 1.2行为:
import ruamel.yaml as yaml
yaml_str = """\
off:
    yes: "Flavor text for yes"  # quotes around value dropped
    no: "Flavor text for no"
"""
data = yaml.round_trip_load(yaml_str)
assert 'off' in data
print(yaml.round_trip_dump(data, indent=4))
Run Code Online (Sandbox Code Playgroud)
这使:
off:
    yes: Flavor text for yes    # quotes around value dropped
    no: Flavor text for no
Run Code Online (Sandbox Code Playgroud)
如果您的输出需要与1.1版兼容,那么您可以使用显式转储version=(1, 1).
由于嵌套映射的标量值周围的引号是不必要的,因此在写出时不会发出它们.
如果需要使用PyYAML执行此操作,请重写用于布尔识别的(全局)规则:
import  yaml
from yaml.resolver import Resolver
import re
yaml_str = """\
off:
    yes: "Flavor text for yes"  # quotes around value dropped
    no: "Flavor text for no"
"""
# remove resolver entries for On/Off/Yes/No
for ch in "OoYyNn":
    if len(Resolver.yaml_implicit_resolvers[ch]) == 1:
        del Resolver.yaml_implicit_resolvers[ch]
    else:
        Resolver.yaml_implicit_resolvers[ch] = [x for x in
                Resolver.yaml_implicit_resolvers[ch] if x[0] != 'tag:yaml.org,2002:bool']
data = yaml.load(yaml_str)
print(data)
assert 'off' in data
print(yaml.dump(data))
Run Code Online (Sandbox Code Playgroud)
这使:
{'off': {'yes': 'Flavor text for yes', 'no': 'Flavor text for no'}}
off: {no: Flavor text for no, yes: Flavor text for yes}
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为PyYAML保留了一个全局dict(Resolver.yaml_implicit_resolvers),它将第一个字母映射到(tag,re.match_pattern)值列表.为o,O,y且Y只有一个这样的模式(它可以被删除),但n/ N你也可以匹配null/ Null,所以你必须删除正确的模式.
剥离后yes,no,on,Off不再认定为布尔,但True和False仍然是.
yaml.load采用第二个参数,即加载器类(默认情况下为yaml.loader.Loader)。预定义的加载器是许多其他加载器的混搭:
class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver):
    def __init__(self, stream):
        Reader.__init__(self, stream)
        Scanner.__init__(self)
        Parser.__init__(self)
        Composer.__init__(self)
        Constructor.__init__(self)
        Resolver.__init__(self)
Run Code Online (Sandbox Code Playgroud)
该类Constructor是将数据类型映射到 Python 的类。覆盖布尔转换的一种(笨拙但快速)方法可能是:
from yaml.constructor import Constructor
def add_bool(self, node):
    return self.construct_scalar(node)
Constructor.add_constructor(u'tag:yaml.org,2002:bool', add_bool)
Run Code Online (Sandbox Code Playgroud)
它重写构造函数用于将布尔标记数据转换为 Python 布尔值的函数。我们在这里所做的只是逐字返回字符串。
不过,这会影响所有YAML 加载,因为您正在覆盖默认构造函数的行为。更合适的方法可能是创建一个派生自 的新类Constructor,并Loader使用自定义构造函数创建新对象。
|   归档时间:  |  
           
  |  
        
|   查看次数:  |  
           3951 次  |  
        
|   最近记录:  |